Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: dart-lang/dart_style
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.2.1
Choose a base ref
...
head repository: dart-lang/dart_style
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.2.2
Choose a head ref
  • 9 commits
  • 16 files changed
  • 2 contributors

Commits on Jan 12, 2022

  1. Use 2.17 language version.

    scheglov committed Jan 12, 2022
    Copy the full SHA
    7f5efd8 View commit details

Commits on Jan 18, 2022

  1. Copy the full SHA
    6f894c0 View commit details

Commits on Feb 11, 2022

  1. Copy the full SHA
    4c15895 View commit details

Commits on Feb 23, 2022

  1. Formatting support for named arguments anywhere. (#1094)

    * Formatting support for named arguments anywhere.
    
    I opted to add support for this while being minimally invasive to the
    existing style and formatting. All existing code which does not have any
    positional arguments after named ones retains its previous formatting.
    
    For new argument lists that use positional arguments after named ones,
    I try to mostly follow the existing style rules even though they can be
    fairly complex. In particular, it will be pretty aggressive about
    applying block-style formatting to function literals inside argument
    lists even with named args anywhere. I think that's important to support
    the main use case I know of for the feature which is trailing positional
    closures like:
    
    function(named: 1, another: 2, () {
      block...
    });
    
    In argument lists using named args anywhere that don't have block
    functions, it treats all arguments like named ones. That provides nice
    simple formatting like:
    
    function(
        argument1,
        named: argument2,
        argument3,
        another: argument4);
    
    I think that does a good job of highlighting which arguments are named,
    which is what we want.
    
    Fix #1072.
    
    * Rewrite doc comments.
    munificent authored Feb 23, 2022
    Copy the full SHA
    68eab57 View commit details
  2. Merge branch 'aa-use-internal-api' of https://github.com/scheglov/dar…

    …t_style into scheglov-aa-use-internal-api
    munificent committed Feb 23, 2022
    Copy the full SHA
    c461883 View commit details
  3. Copy the full SHA
    4de83d3 View commit details

Commits on Mar 3, 2022

  1. Format enhanced enums. (#1096)

    * Format enhanced enums.
    
    Fix #1075.
    
    * Include constructor names in enum values.
    
    * Migrate off deprecated analyzer APIs.
    munificent authored Mar 3, 2022
    Copy the full SHA
    c8ed552 View commit details
  2. Copy the full SHA
    7adb2db View commit details
  3. Copy the full SHA
    d7b7353 View commit details
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 2.2.2

* Format named arguments anywhere (#1072).
* Format enhanced enums (#1075).
* Format "super." parameters (#1091).

# 2.2.1

* Require `package:analyzer` version `2.6.0`.
216 changes: 124 additions & 92 deletions lib/src/argument_list_visitor.dart
Original file line number Diff line number Diff line change
@@ -63,97 +63,19 @@ class ArgumentListVisitor {
Token leftParenthesis,
Token rightParenthesis,
List<Expression> arguments) {
// Look for a single contiguous range of block function arguments.
int? functionsStart;
int? functionsEnd;

for (var i = 0; i < arguments.length; i++) {
var argument = arguments[i];
if (_isBlockFunction(argument)) {
functionsStart ??= i;
var functionRange = _contiguousFunctions(arguments);

// The functions must be one contiguous section.
if (functionsEnd != null && functionsEnd != i) {
functionsStart = null;
functionsEnd = null;
break;
}

functionsEnd = i + 1;
}
}

// Edge case: If all of the arguments are named, but they aren't all
// functions, then don't handle the functions specially. A function with a
// bunch of named arguments tends to look best when they are all lined up,
// even the function ones (unless they are all functions).
//
// Prefers:
//
// function(
// named: () {
// something();
// },
// another: argument);
//
// Over:
//
// function(named: () {
// something();
// },
// another: argument);
if (functionsStart != null &&
arguments[0] is NamedExpression &&
(functionsStart > 0 || functionsEnd! < arguments.length)) {
functionsStart = null;
}

// Edge case: If all of the function arguments are named and there are
// other named arguments that are "=>" functions, then don't treat the
// block-bodied functions specially. In a mixture of the two function
// styles, it looks cleaner to treat them all like normal expressions so
// that the named arguments line up.
if (functionsStart != null &&
arguments[functionsStart] is NamedExpression) {
bool isArrow(NamedExpression named) {
var expression = named.expression;

if (expression is FunctionExpression) {
return expression.body is ExpressionFunctionBody;
}

return false;
}

for (var i = 0; i < functionsStart!; i++) {
var argument = arguments[i];
if (argument is! NamedExpression) continue;

if (isArrow(argument)) {
functionsStart = null;
break;
}
}

for (var i = functionsEnd!; i < arguments.length; i++) {
if (isArrow(arguments[i] as NamedExpression)) {
functionsStart = null;
break;
}
}
}

if (functionsStart == null) {
if (functionRange == null) {
// No functions, so there is just a single argument list.
return ArgumentListVisitor._(visitor, leftParenthesis, rightParenthesis,
arguments, ArgumentSublist(arguments, arguments), null, null);
}

// Split the arguments into two independent argument lists with the
// functions in the middle.
var argumentsBefore = arguments.take(functionsStart).toList();
var functions = arguments.sublist(functionsStart, functionsEnd);
var argumentsAfter = arguments.skip(functionsEnd!).toList();
var argumentsBefore = arguments.take(functionRange[0]).toList();
var functions = arguments.sublist(functionRange[0], functionRange[1]);
var argumentsAfter = arguments.skip(functionRange[1]).toList();

return ArgumentListVisitor._(
visitor,
@@ -224,6 +146,83 @@ class ArgumentListVisitor {
if (_isSingle) _visitor.builder.endSpan();
}

/// Look for a single contiguous range of block function [arguments] that
/// should receive special formatting.
///
/// Returns a list of (start, end] indexes if found, otherwise returns `null`.
static List<int>? _contiguousFunctions(List<Expression> arguments) {
int? functionsStart;
var functionsEnd = -1;

// Find the range of block function arguments, if any.
for (var i = 0; i < arguments.length; i++) {
var argument = arguments[i];
if (_isBlockFunction(argument)) {
functionsStart ??= i;

// The functions must be one contiguous section.
if (functionsEnd != -1 && functionsEnd != i) return null;

functionsEnd = i + 1;
}
}

if (functionsStart == null) return null;

// Edge case: If all of the arguments are named, but they aren't all
// functions, then don't handle the functions specially. A function with a
// bunch of named arguments tends to look best when they are all lined up,
// even the function ones (unless they are all functions).
//
// Prefers:
//
// function(
// named: () {
// something();
// },
// another: argument);
//
// Over:
//
// function(named: () {
// something();
// },
// another: argument);
if (_isAllNamed(arguments) &&
(functionsStart > 0 || functionsEnd < arguments.length)) {
return null;
}

// Edge case: If all of the function arguments are named and there are
// other named arguments that are "=>" functions, then don't treat the
// block-bodied functions specially. In a mixture of the two function
// styles, it looks cleaner to treat them all like normal expressions so
// that the named arguments line up.
if (_isAllNamed(arguments.sublist(functionsStart, functionsEnd))) {
bool isNamedArrow(Expression expression) {
if (expression is! NamedExpression) return false;
expression = expression.expression;

return expression is FunctionExpression &&
expression.body is ExpressionFunctionBody;
}

for (var i = 0; i < functionsStart; i++) {
if (isNamedArrow(arguments[i])) return null;
}

for (var i = functionsEnd; i < arguments.length; i++) {
if (isNamedArrow(arguments[i])) return null;
}
}

return [functionsStart, functionsEnd];
}

/// Returns `true` if every expression in [arguments] is named.
static bool _isAllNamed(List<Expression> arguments) =>
arguments.every((argument) => argument is NamedExpression);

/// Returns `true` if [expression] is a [FunctionExpression] with a non-empty
/// block body.
static bool _isBlockFunction(Expression expression) {
@@ -295,10 +294,15 @@ class ArgumentSublist {
/// The full argument list from the AST.
final List<Expression> _allArguments;

/// The positional arguments, in order.
/// If all positional arguments occur before all named arguments, then this
/// contains the positional arguments, in order. Otherwise (there are no
/// positional arguments or they are interleaved with named ones), this is
/// empty.
final List<Expression> _positional;

/// The named arguments, in order.
/// The named arguments, in order. If there are any named arguments that occur
/// before positional arguments, then all arguments are treated as named and
/// end up in this list.
final List<Expression> _named;

/// Maps each block argument, excluding functions, to the first token for that
@@ -325,10 +329,9 @@ class ArgumentSublist {

factory ArgumentSublist(
List<Expression> allArguments, List<Expression> arguments) {
// Assumes named arguments follow all positional ones.
var positional =
arguments.takeWhile((arg) => arg is! NamedExpression).toList();
var named = arguments.skip(positional.length).toList();
var argumentLists = _splitArgumentLists(arguments);
var positional = argumentLists[0];
var named = argumentLists[1];

var blocks = <Expression, Token>{};
for (var argument in arguments) {
@@ -358,9 +361,7 @@ class ArgumentSublist {
if (trailingBlocks != blocks.length) trailingBlocks = 0;

// Ignore any blocks in the middle of the argument list.
if (leadingBlocks == 0 && trailingBlocks == 0) {
blocks.clear();
}
if (leadingBlocks == 0 && trailingBlocks == 0) blocks.clear();

return ArgumentSublist._(
allArguments, positional, named, blocks, leadingBlocks, trailingBlocks);
@@ -484,6 +485,37 @@ class ArgumentSublist {
}
}

/// Splits [arguments] into two lists: the list of leading positional
/// arguments and the list of trailing named arguments.
///
/// If positional arguments are interleaved with the named arguments then
/// all arguments are treat as named since that provides simpler, consistent
/// output.
///
/// Returns a list of two lists: the positional arguments then the named ones.
static List<List<Expression>> _splitArgumentLists(
List<Expression> arguments) {
var positional = <Expression>[];
var named = <Expression>[];
var inNamed = false;
for (var argument in arguments) {
if (argument is NamedExpression) {
inNamed = true;
} else if (inNamed) {
// Got a positional argument after a named one.
return [[], arguments];
}

if (inNamed) {
named.add(argument);
} else {
positional.add(argument);
}
}

return [positional, named];
}

/// If [expression] can be formatted as a block, returns the token that opens
/// the block, such as a collection's bracket.
///
2 changes: 1 addition & 1 deletion lib/src/cli/formatter_options.dart
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import 'show.dart';
import 'summary.dart';

// Note: The following line of code is modified by tool/grind.dart.
const dartStyleVersion = '2.2.1';
const dartStyleVersion = '2.2.2';

/// Global options that affect how the formatter produces and uses its outputs.
class FormatterOptions {
14 changes: 7 additions & 7 deletions lib/src/dart_formatter.dart
Original file line number Diff line number Diff line change
@@ -88,13 +88,13 @@ class DartFormatter {
// TODO(paulberry): consider plumbing in experiment enable flags from the
// command line.
var featureSet = FeatureSet.fromEnableFlags2(
sdkLanguageVersion: Version(2, 13, 0),
flags: [
'constructor-tearoffs',
'generic-metadata',
'nonfunction-type-aliases',
'triple-shift'
]);
sdkLanguageVersion: Version(2, 17, 0),
flags: [
'enhanced-enums',
'named-arguments-anywhere',
'super-parameters',
],
);

var inputOffset = 0;
var text = source.text;
Loading