| Index: packages/dart_style/lib/src/source_visitor.dart
|
| diff --git a/packages/dart_style/lib/src/source_visitor.dart b/packages/dart_style/lib/src/source_visitor.dart
|
| index 524db2ecf8fe427478953bda1181ddf9019ffd19..a34b36789e70b91f0226e75fd12f41c67e349361 100644
|
| --- a/packages/dart_style/lib/src/source_visitor.dart
|
| +++ b/packages/dart_style/lib/src/source_visitor.dart
|
| @@ -15,6 +15,7 @@ import 'chunk_builder.dart';
|
| import 'dart_formatter.dart';
|
| import 'rule/argument.dart';
|
| import 'rule/combinator.dart';
|
| +import 'rule/metadata.dart';
|
| import 'rule/rule.dart';
|
| import 'rule/type_argument.dart';
|
| import 'source_code.dart';
|
| @@ -47,15 +48,6 @@ class SourceVisitor implements AstVisitor {
|
| /// This is calculated and cached by [_findSelectionEnd].
|
| int _selectionEnd;
|
|
|
| - /// The rule that should be used for the contents of a literal body that are
|
| - /// about to be written.
|
| - ///
|
| - /// This is set by [visitArgumentList] to ensure that all block arguments
|
| - /// share a rule.
|
| - ///
|
| - /// If `null`, a literal body creates its own rule.
|
| - Rule _nextLiteralBodyRule;
|
| -
|
| /// A stack that tracks forcing nested collections to split.
|
| ///
|
| /// Each entry corresponds to a collection currently being visited and the
|
| @@ -68,6 +60,29 @@ class SourceVisitor implements AstVisitor {
|
| /// split.
|
| final List<bool> _collectionSplits = [];
|
|
|
| + /// The stack of current rules for handling parameter metadata.
|
| + ///
|
| + /// Each time a parameter (or type parameter) list is begun, a single rule
|
| + /// for all of the metadata annotations on parameters in that list is pushed
|
| + /// onto this stack. We reuse this rule for all annotations so that they split
|
| + /// in unison.
|
| + final List<MetadataRule> _metadataRules = [];
|
| +
|
| + /// The mapping for collection literals that are managed by the argument
|
| + /// list that contains them.
|
| + ///
|
| + /// When a collection literal appears inside an [ArgumentSublist], the
|
| + /// argument list provides a rule for the body to split to ensure that all
|
| + /// collections split in unison. It also tracks the chunk before the
|
| + /// argument that determines whether or not the collection body is indented
|
| + /// like an expression or a statement.
|
| + ///
|
| + /// Before a collection literal argument is visited, [ArgumentSublist] binds
|
| + /// itself to the left bracket token of each collection literal it controls.
|
| + /// When we later visit that literal, we use the token to find that
|
| + /// association.
|
| + final Map<Token, ArgumentSublist> _collectionArgumentLists = {};
|
| +
|
| /// Initialize a newly created visitor to write source code representing
|
| /// the visited nodes to the given [writer].
|
| SourceVisitor(this._formatter, this._lineInfo, this._source) {
|
| @@ -170,7 +185,17 @@ class SourceVisitor implements AstVisitor {
|
|
|
| visitBinaryExpression(BinaryExpression node) {
|
| builder.startSpan();
|
| - builder.nestExpression();
|
| +
|
| + // If a binary operator sequence appears immediately after a `=>`, don't
|
| + // add an extra level of nesting. Instead, let the subsequent operands line
|
| + // up with the first, as in:
|
| + //
|
| + // method() =>
|
| + // argument &&
|
| + // argument &&
|
| + // argument;
|
| + var isArrowBody = node.parent is ExpressionFunctionBody;
|
| + if (!isArrowBody) builder.nestExpression();
|
|
|
| // Start lazily so we don't force the operator to split if a line comment
|
| // appears before the first operand.
|
| @@ -202,12 +227,20 @@ class SourceVisitor implements AstVisitor {
|
|
|
| builder.endBlockArgumentNesting();
|
|
|
| - builder.unnest();
|
| + if (!isArrowBody) builder.unnest();
|
| builder.endSpan();
|
| builder.endRule();
|
| }
|
|
|
| visitBlock(Block node) {
|
| + // Don't allow splitting in an empty block.
|
| + if (node.statements.isEmpty &&
|
| + node.rightBracket.precedingComments == null) {
|
| + token(node.leftBracket);
|
| + token(node.rightBracket);
|
| + return;
|
| + }
|
| +
|
| // For a block that is not a function body, just bump the indentation and
|
| // keep it in the current block.
|
| if (node.parent is! BlockFunctionBody) {
|
| @@ -284,7 +317,7 @@ class SourceVisitor implements AstVisitor {
|
| visitNodes(node.cascadeSections, between: zeroSplit);
|
| builder.endRule();
|
| } else {
|
| - builder.startRule(new HardSplitRule());
|
| + builder.startRule(new Rule.hard());
|
| zeroSplit();
|
| visitNodes(node.cascadeSections, between: zeroSplit);
|
| builder.endRule();
|
| @@ -515,10 +548,15 @@ class SourceVisitor implements AstVisitor {
|
| // ":" if the parameters and initialization list don't all fit on one line.
|
| builder.startRule();
|
|
|
| + // If the redirecting constructor happens to wrap, we want to make sure
|
| + // the parameter list gets more deeply indented.
|
| + if (node.redirectedConstructor != null) builder.nestExpression();
|
| +
|
| _visitBody(node.parameters, node.body, () {
|
| // Check for redirects or initializer lists.
|
| if (node.redirectedConstructor != null) {
|
| _visitConstructorRedirects(node);
|
| + builder.unnest();
|
| } else if (node.initializers.isNotEmpty) {
|
| _visitConstructorInitializers(node);
|
| }
|
| @@ -526,7 +564,8 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| void _visitConstructorRedirects(ConstructorDeclaration node) {
|
| - token(node.separator /* = */, before: space, after: space);
|
| + token(node.separator /* = */, before: space);
|
| + soloSplit();
|
| visitCommaSeparatedNodes(node.initializers);
|
| visit(node.redirectedConstructor);
|
| }
|
| @@ -600,7 +639,7 @@ class SourceVisitor implements AstVisitor {
|
| if (node.separator.type == TokenType.EQ) space();
|
| token(node.separator);
|
|
|
| - soloSplit(Cost.assignment);
|
| + soloSplit(_assignmentCost(node.defaultValue));
|
| visit(node.defaultValue);
|
|
|
| builder.unnest();
|
| @@ -609,18 +648,22 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitDoStatement(DoStatement node) {
|
| - _simpleStatement(node, () {
|
| - token(node.doKeyword);
|
| - space();
|
| - visit(node.body);
|
| - space();
|
| - token(node.whileKeyword);
|
| - space();
|
| - token(node.leftParenthesis);
|
| - soloZeroSplit();
|
| - visit(node.condition);
|
| - token(node.rightParenthesis);
|
| - });
|
| + builder.nestExpression();
|
| + token(node.doKeyword);
|
| + space();
|
| + builder.unnest(now: false);
|
| + visit(node.body);
|
| +
|
| + builder.nestExpression();
|
| + space();
|
| + token(node.whileKeyword);
|
| + space();
|
| + token(node.leftParenthesis);
|
| + soloZeroSplit();
|
| + visit(node.condition);
|
| + token(node.rightParenthesis);
|
| + token(node.semicolon);
|
| + builder.unnest();
|
| }
|
|
|
| visitDoubleLiteral(DoubleLiteral node) {
|
| @@ -682,7 +725,10 @@ class SourceVisitor implements AstVisitor {
|
| // Split after the "=>", using the rule created before the parameters
|
| // by _visitBody().
|
| split();
|
| - builder.endRule();
|
| +
|
| + // If the body is a binary operator expression, then we want to force the
|
| + // split at `=>` if the operators split. See visitBinaryExpression().
|
| + if (node.expression is! BinaryExpression) builder.endRule();
|
|
|
| if (_isInLambda(node)) builder.endSpan();
|
|
|
| @@ -692,6 +738,8 @@ class SourceVisitor implements AstVisitor {
|
| builder.endSpan();
|
| builder.endBlockArgumentNesting();
|
|
|
| + if (node.expression is BinaryExpression) builder.endRule();
|
| +
|
| token(node.semicolon);
|
| }
|
|
|
| @@ -744,9 +792,9 @@ class SourceVisitor implements AstVisitor {
|
| space();
|
| visit(node.iterable);
|
| token(node.rightParenthesis);
|
| - space();
|
| - visit(node.body);
|
| - builder.unnest();
|
| + builder.unnest(now: false);
|
| +
|
| + _visitLoopBody(node.body);
|
| }
|
|
|
| visitFormalParameterList(FormalParameterList node) {
|
| @@ -771,6 +819,8 @@ class SourceVisitor implements AstVisitor {
|
| builder.nestExpression();
|
| token(node.leftParenthesis);
|
|
|
| + _metadataRules.add(new MetadataRule());
|
| +
|
| var rule;
|
| if (requiredParams.isNotEmpty) {
|
| if (requiredParams.length > 1) {
|
| @@ -779,6 +829,8 @@ class SourceVisitor implements AstVisitor {
|
| rule = new SinglePositionalRule(null);
|
| }
|
|
|
| + _metadataRules.last.bindPositionalRule(rule);
|
| +
|
| builder.startRule(rule);
|
| if (_isInLambda(node)) {
|
| // Don't allow splitting before the first argument (i.e. right after
|
| @@ -806,13 +858,17 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| if (optionalParams.isNotEmpty) {
|
| - var namedRule = new NamedRule(null);
|
| + var namedRule = new NamedRule(null, 0, 0);
|
| if (rule != null) rule.setNamedArgsRule(namedRule);
|
|
|
| + _metadataRules.last.bindNamedRule(namedRule);
|
| +
|
| builder.startRule(namedRule);
|
|
|
| - namedRule
|
| - .beforeArguments(builder.split(space: requiredParams.isNotEmpty));
|
| + // Make sure multi-line default values are indented.
|
| + builder.startBlockArgumentNesting();
|
| +
|
| + namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty));
|
|
|
| // "[" or "{" for optional parameters.
|
| token(node.leftDelimiter);
|
| @@ -822,15 +878,18 @@ class SourceVisitor implements AstVisitor {
|
|
|
| // Write the trailing comma.
|
| if (param != node.parameters.last) token(param.endToken.next);
|
| - if (param != optionalParams.last) split();
|
| + if (param != optionalParams.last) namedRule.beforeArgument(split());
|
| }
|
|
|
| + builder.endBlockArgumentNesting();
|
| builder.endRule();
|
|
|
| // "]" or "}" for optional parameters.
|
| token(node.rightDelimiter);
|
| }
|
|
|
| + _metadataRules.removeLast();
|
| +
|
| token(node.rightParenthesis);
|
| builder.unnest();
|
| }
|
| @@ -847,9 +906,9 @@ class SourceVisitor implements AstVisitor {
|
| if (node.initialization != null) {
|
| visit(node.initialization);
|
| } else if (node.variables != null) {
|
| - // Indent split variables more so they aren't at the same level
|
| + // Nest split variables more so they aren't at the same level
|
| // as the rest of the loop clauses.
|
| - builder.indent(Indent.loopVariable);
|
| + builder.nestExpression();
|
|
|
| // Allow the variables to stay unsplit even if the clauses split.
|
| builder.startRule();
|
| @@ -864,7 +923,7 @@ class SourceVisitor implements AstVisitor {
|
| });
|
|
|
| builder.endRule();
|
| - builder.unindent();
|
| + builder.unnest();
|
| }
|
|
|
| token(node.leftSeparator);
|
| @@ -890,21 +949,11 @@ class SourceVisitor implements AstVisitor {
|
| builder.endRule();
|
| builder.unnest();
|
|
|
| - // The body.
|
| - if (node.body is! EmptyStatement) space();
|
| - visit(node.body);
|
| + _visitLoopBody(node.body);
|
| }
|
|
|
| visitFunctionDeclaration(FunctionDeclaration node) {
|
| - visitMemberMetadata(node.metadata);
|
| -
|
| - builder.nestExpression();
|
| - modifier(node.externalKeyword);
|
| - visit(node.returnType, after: space);
|
| - modifier(node.propertyKeyword);
|
| - visit(node.name);
|
| - visit(node.functionExpression);
|
| - builder.unnest();
|
| + _visitMemberDeclaration(node, node.functionExpression);
|
| }
|
|
|
| visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
|
| @@ -956,10 +1005,35 @@ class SourceVisitor implements AstVisitor {
|
| token(node.leftParenthesis);
|
| visit(node.condition);
|
| token(node.rightParenthesis);
|
| + builder.unnest(now: false);
|
|
|
| - space();
|
| - visit(node.thenStatement);
|
| - builder.unnest();
|
| + visitClause(Statement clause) {
|
| + if (clause is Block || clause is IfStatement) {
|
| + space();
|
| + visit(clause);
|
| + } else {
|
| + // Allow splitting in an expression-bodied if even though it's against
|
| + // the style guide. Since we can't fix the code itself to follow the
|
| + // style guide, we should at least format it as well as we can.
|
| + builder.nestExpression(indent: 2, now: true);
|
| + builder.startRule();
|
| +
|
| + // If there is an else clause, always split before both the then and
|
| + // else statements.
|
| + if (node.elseStatement != null) {
|
| + builder.writeWhitespace(Whitespace.nestedNewline);
|
| + } else {
|
| + split();
|
| + }
|
| +
|
| + visit(clause);
|
| +
|
| + builder.endRule();
|
| + builder.unnest();
|
| + }
|
| + }
|
| +
|
| + visitClause(node.thenStatement);
|
|
|
| if (node.elseStatement != null) {
|
| if (node.thenStatement is Block) {
|
| @@ -972,8 +1046,7 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| token(node.elseKeyword);
|
| - space();
|
| - visit(node.elseStatement);
|
| + visitClause(node.elseStatement);
|
| }
|
| }
|
|
|
| @@ -1012,8 +1085,18 @@ class SourceVisitor implements AstVisitor {
|
| visit(node.target);
|
| }
|
|
|
| + finishIndexExpression(node);
|
| +
|
| + builder.unnest();
|
| + }
|
| +
|
| + /// Visit the index part of [node], excluding the target.
|
| + ///
|
| + /// Called by [CallChainVisitor] to handle index expressions in the middle of
|
| + /// call chains.
|
| + void finishIndexExpression(IndexExpression node) {
|
| if (node.target is IndexExpression) {
|
| - // Corner case: On a chain of [] accesses, allow splitting between them.
|
| + // Edge case: On a chain of [] accesses, allow splitting between them.
|
| // Produces nicer output in cases like:
|
| //
|
| // someJson['property']['property']['property']['property']...
|
| @@ -1026,14 +1109,15 @@ class SourceVisitor implements AstVisitor {
|
| visit(node.index);
|
| token(node.rightBracket);
|
| builder.endSpan();
|
| - builder.unnest();
|
| }
|
|
|
| visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| builder.startSpan();
|
| token(node.keyword);
|
| space();
|
| + builder.startSpan(Cost.constructorName);
|
| visit(node.constructorName);
|
| + builder.endSpan();
|
| visit(node.argumentList);
|
| builder.endSpan();
|
| }
|
| @@ -1112,16 +1196,7 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitMethodDeclaration(MethodDeclaration node) {
|
| - visitMemberMetadata(node.metadata);
|
| -
|
| - modifier(node.externalKeyword);
|
| - modifier(node.modifierKeyword);
|
| - visit(node.returnType, after: space);
|
| - modifier(node.propertyKeyword);
|
| - modifier(node.operatorKeyword);
|
| - visit(node.name);
|
| -
|
| - _visitBody(node.parameters, node.body);
|
| + _visitMemberDeclaration(node, node);
|
| }
|
|
|
| visitMethodInvocation(MethodInvocation node) {
|
| @@ -1149,7 +1224,16 @@ class SourceVisitor implements AstVisitor {
|
| builder.nestExpression();
|
| builder.startSpan();
|
| visit(node.name);
|
| - visit(node.expression, before: soloSplit);
|
| +
|
| + // Don't allow a split between a name and a collection. Instead, we want
|
| + // the collection itself to split, or to split before the argument.
|
| + if (node.expression is ListLiteral || node.expression is MapLiteral) {
|
| + space();
|
| + } else {
|
| + soloSplit();
|
| + }
|
| +
|
| + visit(node.expression);
|
| builder.endSpan();
|
| builder.unnest();
|
| }
|
| @@ -1184,6 +1268,8 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitPartDirective(PartDirective node) {
|
| + visitDeclarationMetadata(node.metadata);
|
| +
|
| _simpleStatement(node, () {
|
| token(node.keyword);
|
| space();
|
| @@ -1192,6 +1278,8 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitPartOfDirective(PartOfDirective node) {
|
| + visitDeclarationMetadata(node.metadata);
|
| +
|
| _simpleStatement(node, () {
|
| token(node.keyword);
|
| space();
|
| @@ -1207,9 +1295,7 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| - visit(node.prefix);
|
| - token(node.period);
|
| - visit(node.identifier);
|
| + new CallChainVisitor(this, node).visit();
|
| }
|
|
|
| visitPrefixExpression(PrefixExpression node) {
|
| @@ -1357,6 +1443,7 @@ class SourceVisitor implements AstVisitor {
|
| token(node.rightParenthesis);
|
| space();
|
| token(node.leftBracket);
|
| + builder.unnest();
|
| builder.indent();
|
| newline();
|
|
|
| @@ -1365,7 +1452,6 @@ class SourceVisitor implements AstVisitor {
|
| builder.unindent();
|
| newline();
|
| });
|
| - builder.unnest();
|
| }
|
|
|
| visitSymbolLiteral(SymbolLiteral node) {
|
| @@ -1425,7 +1511,11 @@ class SourceVisitor implements AstVisitor {
|
| }
|
|
|
| visitTypeParameterList(TypeParameterList node) {
|
| + _metadataRules.add(new MetadataRule());
|
| +
|
| _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
|
| +
|
| + _metadataRules.removeLast();
|
| }
|
|
|
| visitVariableDeclaration(VariableDeclaration node) {
|
| @@ -1437,8 +1527,14 @@ class SourceVisitor implements AstVisitor {
|
|
|
| visitVariableDeclarationList(VariableDeclarationList node) {
|
| visitDeclarationMetadata(node.metadata);
|
| +
|
| + // Allow but try to avoid splitting between the type and name.
|
| + builder.startSpan();
|
| +
|
| modifier(node.keyword);
|
| - visit(node.type, after: space);
|
| + visit(node.type, after: soloSplit);
|
| +
|
| + builder.endSpan();
|
|
|
| // Use a single rule for all of the variables. If there are multiple
|
| // declarations, we will try to keep them all on one line. If that isn't
|
| @@ -1463,9 +1559,9 @@ class SourceVisitor implements AstVisitor {
|
| soloZeroSplit();
|
| visit(node.condition);
|
| token(node.rightParenthesis);
|
| - if (node.body is! EmptyStatement) space();
|
| - visit(node.body);
|
| - builder.unnest();
|
| + builder.unnest(now: false);
|
| +
|
| + _visitLoopBody(node.body);
|
| }
|
|
|
| visitWithClause(WithClause node) {
|
| @@ -1524,11 +1620,29 @@ class SourceVisitor implements AstVisitor {
|
| /// These are always on the same line as the parameter.
|
| void visitParameterMetadata(
|
| NodeList<Annotation> metadata, void visitParameter()) {
|
| + if (metadata == null || metadata.isEmpty) {
|
| + visitParameter();
|
| + return;
|
| + }
|
| +
|
| // Split before all of the annotations or none.
|
| - builder.startRule();
|
| - visitNodes(metadata, between: split, after: split);
|
| + builder.startLazyRule(_metadataRules.last);
|
| +
|
| + visitNodes(metadata, between: split, after: () {
|
| + // Don't nest until right before the last metadata. Ensures we only
|
| + // indent the parameter and not any of the metadata:
|
| + //
|
| + // function(
|
| + // @LongAnnotation
|
| + // @LongAnnotation
|
| + // indentedParameter) {}
|
| + builder.nestExpression(now: true);
|
| + split();
|
| + });
|
| visitParameter();
|
|
|
| + builder.unnest();
|
| +
|
| // Wrap the rule around the parameter too. If it splits, we want to force
|
| // the annotations to split as well.
|
| builder.endRule();
|
| @@ -1543,7 +1657,7 @@ class SourceVisitor implements AstVisitor {
|
| void _visitAssignment(Token equalsOperator, Expression rightHandSide) {
|
| space();
|
| token(equalsOperator);
|
| - soloSplit(Cost.assignment);
|
| + soloSplit(_assignmentCost(rightHandSide));
|
| builder.startSpan();
|
| visit(rightHandSide);
|
| builder.endSpan();
|
| @@ -1577,6 +1691,37 @@ class SourceVisitor implements AstVisitor {
|
| builder.endRule();
|
| }
|
|
|
| + /// Visits a top-level function or method declaration.
|
| + ///
|
| + /// The two AST node types are very similar but, alas, share no common
|
| + /// interface type in analyzer, hence the dynamic typing.
|
| + void _visitMemberDeclaration(
|
| + /* FunctionDeclaration|MethodDeclaration */ node,
|
| + /* FunctionExpression|MethodDeclaration */ function) {
|
| + visitMemberMetadata(node.metadata);
|
| +
|
| + // Nest the signature in case we have to split between the return type and
|
| + // name.
|
| + builder.nestExpression();
|
| + builder.startSpan();
|
| + modifier(node.externalKeyword);
|
| + if (node is MethodDeclaration) modifier(node.modifierKeyword);
|
| + visit(node.returnType, after: soloSplit);
|
| + modifier(node.propertyKeyword);
|
| + if (node is MethodDeclaration) modifier(node.operatorKeyword);
|
| + visit(node.name);
|
| + builder.endSpan();
|
| +
|
| + // If the body is a block, we need to exit any nesting first. If it's an
|
| + // expression, we want to wrap the nesting around that so that the body
|
| + // gets nested farther.
|
| + if (function.body is! ExpressionFunctionBody) builder.unnest();
|
| +
|
| + _visitBody(function.parameters, function.body);
|
| +
|
| + if (function.body is ExpressionFunctionBody) builder.unnest();
|
| + }
|
| +
|
| /// Visit the given function [parameters] followed by its [body], printing a
|
| /// space before it if it's not empty.
|
| ///
|
| @@ -1611,16 +1756,15 @@ class SourceVisitor implements AstVisitor {
|
| builder.nestExpression();
|
|
|
| // This rule is ended by visitExpressionFunctionBody().
|
| - builder.startLazyRule(new SimpleRule(cost: Cost.arrow));
|
| + builder.startLazyRule(new Rule(Cost.arrow));
|
| }
|
|
|
| if (parameters != null) {
|
| builder.nestExpression();
|
| -
|
| visit(parameters);
|
| - if (afterParameters != null) afterParameters();
|
| -
|
| builder.unnest();
|
| +
|
| + if (afterParameters != null) afterParameters();
|
| }
|
|
|
| visit(body);
|
| @@ -1628,6 +1772,29 @@ class SourceVisitor implements AstVisitor {
|
| if (body is ExpressionFunctionBody) builder.unnest();
|
| }
|
|
|
| + /// Visits the body statement of a `for` or `for in` loop.
|
| + void _visitLoopBody(Statement body) {
|
| + if (body is EmptyStatement) {
|
| + // No space before the ";".
|
| + visit(body);
|
| + } else if (body is Block) {
|
| + space();
|
| + visit(body);
|
| + } else {
|
| + // Allow splitting in an expression-bodied for even though it's against
|
| + // the style guide. Since we can't fix the code itself to follow the
|
| + // style guide, we should at least format it as well as we can.
|
| + builder.nestExpression(indent: 2, now: true);
|
| + builder.startRule();
|
| +
|
| + split();
|
| + visit(body);
|
| +
|
| + builder.endRule();
|
| + builder.unnest();
|
| + }
|
| + }
|
| +
|
| /// Visit a list of [nodes] if not null, optionally separated and/or preceded
|
| /// and followed by the given functions.
|
| void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
|
| @@ -1674,10 +1841,6 @@ class SourceVisitor implements AstVisitor {
|
| if (elements.isEmpty && rightBracket.precedingComments == null) {
|
| token(leftBracket);
|
| token(rightBracket);
|
| -
|
| - // Clear this out in case this empty collection is in an argument list.
|
| - // We don't want this rule to bleed over to some other collection.
|
| - _nextLiteralBodyRule = null;
|
| return;
|
| }
|
|
|
| @@ -1694,7 +1857,7 @@ class SourceVisitor implements AstVisitor {
|
| // Always use a hard rule to split the elements. The parent chunk of
|
| // the collection will handle the unsplit case, so this only comes
|
| // into play when the collection is split.
|
| - var rule = new HardSplitRule();
|
| + var rule = new Rule.hard();
|
| builder.startRule(rule);
|
|
|
| // If a collection contains a line comment, we assume it's a big complex
|
| @@ -1712,7 +1875,7 @@ class SourceVisitor implements AstVisitor {
|
| soloSplit();
|
| }
|
| } else {
|
| - builder.blockSplit(space: true);
|
| + builder.split(nest: false, space: true);
|
| }
|
| }
|
|
|
| @@ -1733,6 +1896,37 @@ class SourceVisitor implements AstVisitor {
|
| _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force);
|
| }
|
|
|
| + /// Gets the cost to split at an assignment (or `:` in the case of a named
|
| + /// default value) with the given [rightHandSide].
|
| + ///
|
| + /// "Block-like" expressions (collections and cascades) bind a bit tighter
|
| + /// because it looks better to have code like:
|
| + ///
|
| + /// var list = [
|
| + /// element,
|
| + /// element,
|
| + /// element
|
| + /// ];
|
| + ///
|
| + /// var builder = new SomeBuilderClass()
|
| + /// ..method()
|
| + /// ..method();
|
| + ///
|
| + /// over:
|
| + ///
|
| + /// var list =
|
| + /// [element, element, element];
|
| + ///
|
| + /// var builder =
|
| + /// new SomeBuilderClass()..method()..method();
|
| + int _assignmentCost(Expression rightHandSide) {
|
| + if (rightHandSide is ListLiteral) return Cost.assignBlock;
|
| + if (rightHandSide is MapLiteral) return Cost.assignBlock;
|
| + if (rightHandSide is CascadeExpression) return Cost.assignBlock;
|
| +
|
| + return Cost.assign;
|
| + }
|
| +
|
| /// Returns `true` if the collection withs [elements] delimited by
|
| /// [rightBracket] contains any line comments.
|
| ///
|
| @@ -1765,16 +1959,21 @@ class SourceVisitor implements AstVisitor {
|
| void _startLiteralBody(Token leftBracket) {
|
| token(leftBracket);
|
|
|
| - // Split the literal. Use the explicitly given rule if we have one.
|
| - // Otherwise, create a new rule.
|
| - var rule = _nextLiteralBodyRule;
|
| - _nextLiteralBodyRule = null;
|
| + // See if this literal is associated with an argument list that wants to
|
| + // handle splitting and indenting it. If not, we'll use a default rule.
|
| + var rule;
|
| + var argumentChunk;
|
| + if (_collectionArgumentLists.containsKey(leftBracket)) {
|
| + var argumentList = _collectionArgumentLists[leftBracket];
|
| + rule = argumentList.collectionRule;
|
| + argumentChunk = argumentList.previousSplit;
|
| + }
|
|
|
| // Create a rule for whether or not to split the block contents.
|
| builder.startRule(rule);
|
|
|
| // Process the collection contents as a separate set of chunks.
|
| - builder = builder.startBlock();
|
| + builder = builder.startBlock(argumentChunk);
|
| }
|
|
|
| /// Ends the literal body started by a call to [_startLiteralBody()].
|
| @@ -1834,10 +2033,13 @@ class SourceVisitor implements AstVisitor {
|
| builder.unnest();
|
| }
|
|
|
| - /// Makes [rule] the rule that will be used for the contents of a collection
|
| - /// or function literal body that are about to be visited.
|
| - void setNextLiteralBodyRule(Rule rule) {
|
| - _nextLiteralBodyRule = rule;
|
| + /// Marks the collection literal that starts with [leftBracket] as being
|
| + /// controlled by [argumentList].
|
| + ///
|
| + /// When the collection is visited, [argumentList] will determine the
|
| + /// indentation and splitting rule for the collection.
|
| + void beforeCollection(Token leftBracket, ArgumentSublist argumentList) {
|
| + _collectionArgumentLists[leftBracket] = argumentList;
|
| }
|
|
|
| /// Writes an bracket-delimited body and handles indenting and starting the
|
| @@ -1854,14 +2056,14 @@ class SourceVisitor implements AstVisitor {
|
|
|
| // Split after the bracket.
|
| builder.startRule();
|
| - builder.blockSplit(space: space, isDouble: false);
|
| + builder.split(isDouble: false, nest: false, space: space);
|
|
|
| body();
|
|
|
| token(rightBracket, before: () {
|
| // Split before the closing bracket character.
|
| builder.unindent();
|
| - builder.blockSplit(space: space);
|
| + builder.split(nest: false, space: space);
|
| });
|
|
|
| builder.endRule();
|
| @@ -1869,7 +2071,8 @@ class SourceVisitor implements AstVisitor {
|
|
|
| /// Returns `true` if [node] is immediately contained within an anonymous
|
| /// [FunctionExpression].
|
| - bool _isInLambda(AstNode node) => node.parent is FunctionExpression &&
|
| + bool _isInLambda(AstNode node) =>
|
| + node.parent is FunctionExpression &&
|
| node.parent.parent is! FunctionDeclaration;
|
|
|
| /// Writes the string literal [string] to the output.
|
| @@ -1945,7 +2148,7 @@ class SourceVisitor implements AstVisitor {
|
|
|
| /// Writes a single space split with its own rule.
|
| void soloSplit([int cost]) {
|
| - builder.startRule(new SimpleRule(cost: cost));
|
| + builder.startRule(new Rule(cost));
|
| split();
|
| builder.endRule();
|
| }
|
|
|