Coverage report for lib/src/source_visitor.dart

Line coverage: 1383 / 1394 (99.2%)

All files > lib/src/source_visitor.dart

1
// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4
5
library dart_style.src.source_visitor;
6
7
import 'package:analyzer/dart/ast/ast.dart';
8
import 'package:analyzer/dart/ast/token.dart';
9
import 'package:analyzer/dart/ast/visitor.dart';
10
import 'package:analyzer/src/generated/source.dart';
11
12
import 'argument_list_visitor.dart';
13
import 'call_chain_visitor.dart';
14
import 'chunk.dart';
15
import 'chunk_builder.dart';
16
import 'dart_formatter.dart';
17
import 'rule/argument.dart';
18
import 'rule/combinator.dart';
19
import 'rule/metadata.dart';
20
import 'rule/rule.dart';
21
import 'rule/type_argument.dart';
22
import 'source_code.dart';
23
import 'style_fix.dart';
24
import 'whitespace.dart';
25
26
final _capitalPattern = new RegExp(r"^_?[A-Z].*[a-z]");
27
28
/// Visits every token of the AST and passes all of the relevant bits to a
29
/// [ChunkBuilder].
30
class SourceVisitor extends ThrowingAstVisitor {
31
  /// Returns `true` if [node] is a method invocation that looks like it might
32
  /// be a static method or constructor call without a `new` keyword.
33
  ///
34
  /// With optional `new`, we can no longer reliably identify constructor calls
35
  /// statically, but we still don't want to mix named constructor calls into
36
  /// a call chain like:
37
  ///
38
  ///     Iterable
39
  ///         .generate(...)
40
  ///         .toList();
41
  ///
42
  /// And instead prefer:
43
  ///
44
  ///     Iterable.generate(...)
45
  ///         .toList();
46
  ///
47
  /// So we try to identify these calls syntactically. The heuristic we use is
48
  /// that a target that's a capitalized name (possibly prefixed by "_") is
49
  /// assumed to be a class.
50
  ///
51
  /// This has the effect of also keeping static method calls with the class,
52
  /// but that tends to look pretty good too, and is certainly better than
53
  /// splitting up named constructors.
542
  static bool looksLikeStaticCall(Expression node) {
552
    if (node is! MethodInvocation) return false;
56
    var invocation = node as MethodInvocation;
571
    if (invocation.target == null) return false;
58
59
    // A prefixed unnamed constructor call:
60
    //
61
    //     prefix.Foo();
622
    if (invocation.target is SimpleIdentifier &&
633
        _looksLikeClassName(invocation.methodName.name)) {
64
      return true;
65
    }
66
67
    // A prefixed or unprefixed named constructor call:
68
    //
69
    //     Foo.named();
70
    //     prefix.Foo.named();
711
    var target = invocation.target;
721
    if (target is PrefixedIdentifier) {
731
      target = (target as PrefixedIdentifier).identifier;
74
    }
75
763
    return target is SimpleIdentifier && _looksLikeClassName(target.name);
77
  }
78
791
  static bool _looksLikeClassName(String name) {
80
    // Handle the weird lowercase corelib names.
811
    if (name == "bool") return true;
821
    if (name == "double") return true;
831
    if (name == "int") return true;
841
    if (name == "num") return true;
85
862
    return _capitalPattern.hasMatch(name);
87
  }
88
89
  /// The builder for the block that is currently being visited.
90
  ChunkBuilder builder;
91
92
  final DartFormatter _formatter;
93
94
  /// Cached line info for calculating blank lines.
95
  LineInfo _lineInfo;
96
97
  /// The source being formatted.
98
  final SourceCode _source;
99
100
  /// `true` if the visitor has written past the beginning of the selection in
101
  /// the original source text.
102
  bool _passedSelectionStart = false;
103
104
  /// `true` if the visitor has written past the end of the selection in the
105
  /// original source text.
106
  bool _passedSelectionEnd = false;
107
108
  /// The character offset of the end of the selection, if there is a selection.
109
  ///
110
  /// This is calculated and cached by [_findSelectionEnd].
111
  int _selectionEnd;
112
113
  /// How many levels deep inside a constant context the visitor currently is.
114
  int _constNesting = 0;
115
116
  /// Whether we are currently fixing a typedef declaration.
117
  ///
118
  /// Set to `true` while traversing the parameters of a typedef being converted
119
  /// to the new syntax. The new syntax does not allow `int foo()` as a
120
  /// parameter declaration, so it needs to be converted to `int Function() foo`
121
  /// as part of the fix.
122
  bool _insideNewTypedefFix = false;
123
124
  /// A stack that tracks forcing nested collections to split.
125
  ///
126
  /// Each entry corresponds to a collection currently being visited and the
127
  /// value is whether or not it should be forced to split. Every time a
128
  /// collection is entered, it sets all of the existing elements to `true`
129
  /// then it pushes `false` for itself.
130
  ///
131
  /// When done visiting the elements, it removes its value. If it was set to
132
  /// `true`, we know we visited a nested collection so we force this one to
133
  /// split.
134
  final List<bool> _collectionSplits = [];
135
136
  /// The stack of current rules for handling parameter metadata.
137
  ///
138
  /// Each time a parameter (or type parameter) list is begun, a single rule
139
  /// for all of the metadata annotations on parameters in that list is pushed
140
  /// onto this stack. We reuse this rule for all annotations so that they split
141
  /// in unison.
142
  final List<MetadataRule> _metadataRules = [];
143
144
  /// The mapping for blocks that are managed by the argument list that contains
145
  /// them.
146
  ///
147
  /// When a block expression, such as a collection literal or a multiline
148
  /// string, appears inside an [ArgumentSublist], the argument list provides a
149
  /// rule for the body to split to ensure that all blocks split in unison. It
150
  /// also tracks the chunk before the argument that determines whether or not
151
  /// the block body is indented like an expression or a statement.
152
  ///
153
  /// Before a block argument is visited, [ArgumentSublist] binds itself to the
154
  /// beginning token of each block it controls. When we later visit that
155
  /// literal, we use the token to find that association.
156
  final Map<Token, ArgumentSublist> _blockArgumentLists = {};
157
158
  /// Initialize a newly created visitor to write source code representing
159
  /// the visited nodes to the given [writer].
1603
  SourceVisitor(this._formatter, this._lineInfo, this._source) {
16112
    builder = new ChunkBuilder(_formatter, _source);
162
  }
163
164
  /// Runs the visitor on [node], formatting its contents.
165
  ///
166
  /// Returns a [SourceCode] containing the resulting formatted source and
167
  /// updated selection, if any.
168
  ///
169
  /// This is the only method that should be called externally. Everything else
170
  /// is effectively private.
1713
  SourceCode run(AstNode node) {
1723
    visit(node);
173
174
    // Output trailing comments.
1759
    writePrecedingCommentsAndNewlines(node.endToken.next);
176
1776
    assert(_constNesting == 0, "Should have exited all const contexts.");
178
179
    // Finish writing and return the complete result.
1806
    return builder.end();
181
  }
182
1831
  visitAdjacentStrings(AdjacentStrings node) {
1842
    builder.startSpan();
1852
    builder.startRule();
1863
    visitNodes(node.strings, between: splitOrNewline);
1872
    builder.endRule();
1882
    builder.endSpan();
189
  }
190
1912
  visitAnnotation(Annotation node) {
1924
    token(node.atSign);
1934
    visit(node.name);
1944
    token(node.period);
1954
    visit(node.constructorName);
196
197
    // Metadata annotations are always const contexts.
1984
    _constNesting++;
1994
    visit(node.arguments);
2004
    _constNesting--;
201
  }
202
203
  /// Visits an argument list.
204
  ///
205
  /// This is a bit complex to handle the rules for formatting positional and
206
  /// named arguments. The goals, in rough order of descending priority are:
207
  ///
208
  /// 1. Keep everything on the first line.
209
  /// 2. Keep the named arguments together on the next line.
210
  /// 3. Keep everything together on the second line.
211
  /// 4. Split between one or more positional arguments, trying to keep as many
212
  ///    on earlier lines as possible.
213
  /// 5. Split the named arguments each onto their own line.
2143
  visitArgumentList(ArgumentList node, {bool nestExpression: true}) {
215
    // Corner case: handle empty argument lists.
2166
    if (node.arguments.isEmpty) {
2174
      token(node.leftParenthesis);
218
219
      // If there is a comment inside the parens, do allow splitting before it.
2205
      if (node.rightParenthesis.precedingComments != null) soloZeroSplit();
221
2224
      token(node.rightParenthesis);
223
      return;
224
    }
225
226
    // If the argument list has a trailing comma, format it like a collection
227
    // literal where each argument goes on its own line, they are indented +2,
228
    // and the ")" ends up on its own line.
22918
    if (node.arguments.last.endToken.next.type == TokenType.COMMA) {
2301
      _visitCollectionLiteral(
2313
          null, node.leftParenthesis, node.arguments, node.rightParenthesis);
232
      return;
233
    }
234
2354
    if (nestExpression) builder.nestExpression();
2366
    new ArgumentListVisitor(this, node).visit();
2374
    if (nestExpression) builder.unnest();
238
  }
239
2401
  visitAsExpression(AsExpression node) {
2412
    builder.startSpan();
2422
    builder.nestExpression();
2432
    visit(node.expression);
2441
    soloSplit();
2452
    token(node.asOperator);
2461
    space();
2472
    visit(node.type);
2482
    builder.unnest();
2492
    builder.endSpan();
250
  }
251
2521
  visitAssertInitializer(AssertInitializer node) {
2532
    token(node.assertKeyword);
254
2552
    var arguments = <Expression>[node.condition];
2561
    if (node.message != null) arguments.add(node.message);
257
2582
    builder.nestExpression();
2591
    var visitor = new ArgumentListVisitor.forArguments(
2602
        this, node.leftParenthesis, node.rightParenthesis, arguments);
2611
    visitor.visit();
2622
    builder.unnest();
263
  }
264
2651
  visitAssertStatement(AssertStatement node) {
2662
    _simpleStatement(node, () {
2672
      token(node.assertKeyword);
268
2692
      var arguments = [node.condition];
2703
      if (node.message != null) arguments.add(node.message);
271
2721
      var visitor = new ArgumentListVisitor.forArguments(
2732
          this, node.leftParenthesis, node.rightParenthesis, arguments);
2741
      visitor.visit();
275
    });
276
  }
277
2781
  visitAssignmentExpression(AssignmentExpression node) {
2792
    builder.nestExpression();
280
2812
    visit(node.leftHandSide);
2823
    _visitAssignment(node.operator, node.rightHandSide);
283
2842
    builder.unnest();
285
  }
286
2871
  visitAwaitExpression(AwaitExpression node) {
2882
    token(node.awaitKeyword);
2891
    space();
2902
    visit(node.expression);
291
  }
292
2931
  visitBinaryExpression(BinaryExpression node) {
2942
    builder.startSpan();
295
296
    // If a binary operator sequence appears immediately after a `=>`, don't
297
    // add an extra level of nesting. Instead, let the subsequent operands line
298
    // up with the first, as in:
299
    //
300
    //     method() =>
301
    //         argument &&
302
    //         argument &&
303
    //         argument;
3042
    var isArrowBody = node.parent is ExpressionFunctionBody;
3052
    if (!isArrowBody) builder.nestExpression();
306
307
    // Start lazily so we don't force the operator to split if a line comment
308
    // appears before the first operand.
3092
    builder.startLazyRule();
310
311
    // Flatten out a tree/chain of the same precedence. If we split on this
312
    // precedence level, we will break all of them.
3133
    var precedence = node.operator.type.precedence;
314
3151
    traverse(Expression e) {
3165
      if (e is BinaryExpression && e.operator.type.precedence == precedence) {
3172
        traverse(e.leftOperand);
318
3191
        space();
3202
        token(e.operator);
321
3221
        split();
3232
        traverse(e.rightOperand);
324
      } else {
3251
        visit(e);
326
      }
327
    }
328
329
    // Blocks as operands to infix operators should always nest like regular
330
    // operands. (Granted, this case is exceedingly rare in real code.)
3312
    builder.startBlockArgumentNesting();
332
3331
    traverse(node);
334
3352
    builder.endBlockArgumentNesting();
336
3372
    if (!isArrowBody) builder.unnest();
3382
    builder.endSpan();
3392
    builder.endRule();
340
  }
341
3422
  visitBlock(Block node) {
343
    // Treat empty blocks specially. In most cases, they are not allowed to
344
    // split. However, an empty block as the then statement of an if with an
345
    // else is always split.
3464
    if (node.statements.isEmpty &&
3474
        node.rightBracket.precedingComments == null) {
3484
      token(node.leftBracket);
349
350
      // Force a split when used as the then body of an if with an else:
351
      //
352
      //     if (condition) {
353
      //     } else ...
3544
      if (node.parent is IfStatement) {
3551
        var ifStatement = node.parent as IfStatement;
3561
        if (ifStatement.elseStatement != null &&
3572
            ifStatement.thenStatement == node) {
3581
          newline();
359
        }
360
      }
361
3624
      token(node.rightBracket);
363
      return;
364
    }
365
366
    // If the block is a function body, it may get expression-level indentation,
367
    // so handle it specially. Otherwise, just bump the indentation and keep it
368
    // in the current block.
3694
    if (node.parent is BlockFunctionBody) {
3704
      _startLiteralBody(node.leftBracket);
371
    } else {
3724
      _beginBody(node.leftBracket);
373
    }
374
375
    var needsDouble = true;
3764
    for (var statement in node.statements) {
377
      if (needsDouble) {
3782
        twoNewlines();
379
      } else {
3802
        oneOrTwoNewlines();
381
      }
382
3832
      visit(statement);
384
385
      needsDouble = false;
3862
      if (statement is FunctionDeclarationStatement) {
387
        // Add a blank line after non-empty block functions.
3883
        var body = statement.functionDeclaration.functionExpression.body;
3891
        if (body is BlockFunctionBody) {
3903
          needsDouble = body.block.statements.isNotEmpty;
391
        }
392
      }
393
    }
394
3956
    if (node.statements.isNotEmpty) newline();
396
3974
    if (node.parent is BlockFunctionBody) {
3984
      _endLiteralBody(node.rightBracket,
3994
          forceSplit: node.statements.isNotEmpty);
400
    } else {
4014
      _endBody(node.rightBracket);
402
    }
403
  }
404
4052
  visitBlockFunctionBody(BlockFunctionBody node) {
406
    // Space after the parameter list.
4072
    space();
408
409
    // The "async" or "sync" keyword.
4104
    token(node.keyword);
411
412
    // The "*" in "async*" or "sync*".
4134
    token(node.star);
4143
    if (node.keyword != null) space();
415
4164
    visit(node.block);
417
  }
418
4191
  visitBooleanLiteral(BooleanLiteral node) {
4202
    token(node.literal);
421
  }
422
4231
  visitBreakStatement(BreakStatement node) {
4242
    _simpleStatement(node, () {
4252
      token(node.breakKeyword);
4263
      visit(node.label, before: space);
427
    });
428
  }
429
4301
  visitCascadeExpression(CascadeExpression node) {
431
    var splitIfOperandsSplit =
4325
        node.cascadeSections.length > 1 || _isCollectionLike(node.target);
433
434
    // If the cascade sections have consistent names they can be broken
435
    // normally otherwise they always get their own line.
436
    if (splitIfOperandsSplit) {
4372
      builder.startLazyRule(
4383
          _allowInlineCascade(node) ? new Rule() : new Rule.hard());
439
    }
440
441
    // If the target of the cascade is a method call (or chain of them), we
442
    // treat the nesting specially. Normally, you would end up with:
443
    //
444
    //     receiver
445
    //           .method()
446
    //           .method()
447
    //       ..cascade()
448
    //       ..cascade();
449
    //
450
    // This is logical, since the method chain is an operand of the cascade
451
    // expression, so it's more deeply nested. But it looks wrong, so we leave
452
    // the method chain's nesting active until after the cascade sections to
453
    // force the *cascades* to be deeper because it looks better:
454
    //
455
    //     receiver
456
    //         .method()
457
    //         .method()
458
    //           ..cascade()
459
    //           ..cascade();
4602
    if (node.target is MethodInvocation) {
4613
      new CallChainVisitor(this, node.target).visit(unnest: false);
462
    } else {
4632
      visit(node.target);
464
    }
465
4662
    builder.nestExpression(indent: Indent.cascade, now: true);
4672
    builder.startBlockArgumentNesting();
468
469
    // If the cascade section shouldn't cause the cascade to split, end the
470
    // rule early so it isn't affected by it.
471
    if (!splitIfOperandsSplit) {
4721
      builder
4733
          .startRule(_allowInlineCascade(node) ? new Rule() : new Rule.hard());
474
    }
475
4761
    zeroSplit();
477
478
    if (!splitIfOperandsSplit) {
4792
      builder.endRule();
480
    }
481
4823
    visitNodes(node.cascadeSections, between: zeroSplit);
483
484
    if (splitIfOperandsSplit) {
4852
      builder.endRule();
486
    }
487
4882
    builder.endBlockArgumentNesting();
4892
    builder.unnest();
490
4914
    if (node.target is MethodInvocation) builder.unnest();
492
  }
493
494
  /// Whether [expression] is a collection literal, or a call with a trailing
495
  /// comma in an argument list.
496
  ///
497
  /// In that case, when the expression is a target of a cascade, we don't
498
  /// force a split before the ".." as eagerly to avoid ugly results like:
499
  ///
500
  ///     [
501
  ///       1,
502
  ///       2,
503
  ///     ]..addAll(numbers);
5041
  bool _isCollectionLike(Expression expression) {
5051
    if (expression is ListLiteral) return false;
5061
    if (expression is MapLiteral) return false;
507
508
    // If the target is a call with a trailing comma in the argument list,
509
    // treat it like a collection literal.
510
    ArgumentList arguments;
5111
    if (expression is InvocationExpression) {
5121
      arguments = expression.argumentList;
5131
    } else if (expression is InstanceCreationExpression) {
5141
      arguments = expression.argumentList;
515
    }
516
517
    // TODO(rnystrom): Do we want to allow an invocation where the last
518
    // argument is a collection literal? Like:
519
    //
520
    //     foo(argument, [
521
    //       element
522
    //     ])..cascade();
523
524
    return arguments == null ||
5252
        arguments.arguments.isEmpty ||
5266
        arguments.arguments.last.endToken.next.type != TokenType.COMMA;
527
  }
528
529
  /// Whether a cascade should be allowed to be inline as opposed to one
530
  /// expression per line.
5311
  bool _allowInlineCascade(CascadeExpression node) {
532
    // If the receiver is an expression that makes the cascade's very low
533
    // precedence confusing, force it to split. For example:
534
    //
535
    //     a ? b : c..d();
536
    //
537
    // Here, the cascade is applied to the result of the conditional, not "c".
5382
    if (node.target is ConditionalExpression) return false;
5392
    if (node.target is BinaryExpression) return false;
5402
    if (node.target is PrefixExpression) return false;
5412
    if (node.target is AwaitExpression) return false;
542
5433
    if (node.cascadeSections.length < 2) return true;
544
545
    var name;
546
    // We could be more forgiving about what constitutes sections with
547
    // consistent names but for now we require all sections to have the same
548
    // method name.
5492
    for (var expression in node.cascadeSections) {
5501
      if (expression is MethodInvocation) {
551
        if (name == null) {
5522
          name = expression.methodName.name;
5533
        } else if (name != expression.methodName.name) {
554
          return false;
555
        }
556
      } else {
557
        return false;
558
      }
559
    }
560
    return true;
561
  }
562
5631
  visitCatchClause(CatchClause node) {
5643
    token(node.onKeyword, after: space);
5652
    visit(node.exceptionType);
566
5671
    if (node.catchKeyword != null) {
5681
      if (node.exceptionType != null) {
5691
        space();
570
      }
5712
      token(node.catchKeyword);
5721
      space();
5732
      token(node.leftParenthesis);
5742
      visit(node.exceptionParameter);
5753
      token(node.comma, after: space);
5762
      visit(node.stackTraceParameter);
5772
      token(node.rightParenthesis);
5781
      space();
579
    } else {
5800
      space();
581
    }
5822
    visit(node.body);
583
  }
584
5851
  visitClassDeclaration(ClassDeclaration node) {
5862
    visitMetadata(node.metadata);
587
5882
    builder.nestExpression();
5892
    modifier(node.abstractKeyword);
5902
    token(node.classKeyword);
5911
    space();
5922
    visit(node.name);
5932
    visit(node.typeParameters);
5942
    visit(node.extendsClause);
595
5963
    builder.startRule(new CombinatorRule());
5972
    visit(node.withClause);
5982
    visit(node.implementsClause);
5992
    builder.endRule();
600
6013
    visit(node.nativeClause, before: space);
6021
    space();
603
6042
    builder.unnest();
6052
    _beginBody(node.leftBracket);
6062
    _visitMembers(node.members);
6072
    _endBody(node.rightBracket);
608
  }
609
6101
  visitClassTypeAlias(ClassTypeAlias node) {
6112
    visitMetadata(node.metadata);
612
6132
    _simpleStatement(node, () {
6142
      modifier(node.abstractKeyword);
6152
      token(node.typedefKeyword);
6161
      space();
6172
      visit(node.name);
6182
      visit(node.typeParameters);
6191
      space();
6202
      token(node.equals);
6211
      space();
622
6232
      visit(node.superclass);
624
6253
      builder.startRule(new CombinatorRule());
6262
      visit(node.withClause);
6272
      visit(node.implementsClause);
6282
      builder.endRule();
629
    });
630
  }
631
6320
  visitComment(Comment node) => null;
633
6340
  visitCommentReference(CommentReference node) => null;
635
6363
  visitCompilationUnit(CompilationUnit node) {
6376
    visit(node.scriptTag);
638
639
    // Put a blank line between the library tag and the other directives.
6403
    Iterable<Directive> directives = node.directives;
6415
    if (directives.isNotEmpty && directives.first is LibraryDirective) {
6422
      visit(directives.first);
6431
      twoNewlines();
644
6451
      directives = directives.skip(1);
646
    }
647
6486
    visitNodes(directives, between: oneOrTwoNewlines);
649
650
    var needsDouble = true;
6516
    for (var declaration in node.declarations) {
652
      // Add a blank line before classes.
6533
      if (declaration is ClassDeclaration) needsDouble = true;
654
655
      if (needsDouble) {
6563
        twoNewlines();
657
      } else {
658
        // Variables and arrow-bodied members can be more tightly packed if
659
        // the user wants to group things together.
6601
        oneOrTwoNewlines();
661
      }
662
6633
      visit(declaration);
664
665
      needsDouble = false;
6663
      if (declaration is ClassDeclaration) {
667
        // Add a blank line after classes.
668
        needsDouble = true;
6693
      } else if (declaration is FunctionDeclaration) {
670
        // Add a blank line after non-empty block functions.
6716
        var body = declaration.functionExpression.body;
6723
        if (body is BlockFunctionBody) {
6736
          needsDouble = body.block.statements.isNotEmpty;
674
        }
675
      }
676
    }
677
  }
678
6791
  visitConditionalExpression(ConditionalExpression node) {
6802
    builder.nestExpression();
681
682
    // Start lazily so we don't force the operator to split if a line comment
683
    // appears before the first operand. If we split after one clause in a
684
    // conditional, always split after both.
6852
    builder.startLazyRule();
6862
    visit(node.condition);
687
688
    // Push any block arguments all the way past the leading "?" and ":".
6892
    builder.nestExpression(indent: Indent.block, now: true);
6902
    builder.startBlockArgumentNesting();
6912
    builder.unnest();
692
6932
    builder.startSpan();
694
6951
    split();
6962
    token(node.question);
6971
    space();
6982
    builder.nestExpression();
6992
    visit(node.thenExpression);
7002
    builder.unnest();
701
7021
    split();
7032
    token(node.colon);
7041
    space();
7052
    visit(node.elseExpression);
706
7072
    builder.endRule();
7082
    builder.endSpan();
7092
    builder.endBlockArgumentNesting();
7102
    builder.unnest();
711
  }
712
7131
  visitConfiguration(Configuration node) {
7142
    token(node.ifKeyword);
7151
    space();
7162
    token(node.leftParenthesis);
7172
    visit(node.name);
718
7191
    if (node.equalToken != null) {
7202
      builder.nestExpression();
7211
      space();
7222
      token(node.equalToken);
7231
      soloSplit();
7242
      visit(node.value);
7252
      builder.unnest();
726
    }
727
7282
    token(node.rightParenthesis);
7291
    space();
7302
    visit(node.uri);
731
  }
732
7331
  visitConstructorDeclaration(ConstructorDeclaration node) {
7342
    visitMetadata(node.metadata);
735
7362
    modifier(node.externalKeyword);
7372
    modifier(node.constKeyword);
7382
    modifier(node.factoryKeyword);
7392
    visit(node.returnType);
7402
    token(node.period);
7412
    visit(node.name);
742
743
    // Make the rule for the ":" span both the preceding parameter list and
744
    // the entire initialization list. This ensures that we split before the
745
    // ":" if the parameters and initialization list don't all fit on one line.
7462
    builder.startRule();
747
748
    // If the redirecting constructor happens to wrap, we want to make sure
749
    // the parameter list gets more deeply indented.
7503
    if (node.redirectedConstructor != null) builder.nestExpression();
751
7524
    _visitBody(null, node.parameters, node.body, () {
753
      // Check for redirects or initializer lists.
7541
      if (node.redirectedConstructor != null) {
7551
        _visitConstructorRedirects(node);
7562
        builder.unnest();
7572
      } else if (node.initializers.isNotEmpty) {
7581
        _visitConstructorInitializers(node);
759
      }
760
    });
761
  }
762
7631
  void _visitConstructorRedirects(ConstructorDeclaration node) {
7643
    token(node.separator /* = */, before: space);
7651
    soloSplit();
7662
    visitCommaSeparatedNodes(node.initializers);
7672
    visit(node.redirectedConstructor);
768
  }
769
7701
  void _visitConstructorInitializers(ConstructorDeclaration node) {
7713
    var hasTrailingComma = node.parameters.parameters.isNotEmpty &&
7727
        node.parameters.parameters.last.endToken.next.type == TokenType.COMMA;
773
774
    if (hasTrailingComma) {
775
      // Since the ")", "])", or "})" on the preceding line doesn't take up
776
      // much space, it looks weird to move the ":" onto it's own line. Instead,
777
      // keep it and the first initializer on the current line but add enough
778
      // space before it to line it up with any subsequent initializers.
779
      //
780
      //     Foo(
781
      //       parameter,
782
      //     )   : field = value,
783
      //           super();
7841
      space();
7853
      if (node.initializers.length > 1) {
7865
        _writeText(node.parameters.parameters.last.isOptional ? " " : "  ",
7872
            node.separator.offset);
788
      }
789
790
      // ":".
7912
      token(node.separator);
7921
      space();
793
7942
      builder.indent(6);
795
    } else {
796
      // Shift the itself ":" forward.
7972
      builder.indent(Indent.constructorInitializer);
798
799
      // If the parameters or initializers split, put the ":" on its own line.
8001
      split();
801
802
      // ":".
8032
      token(node.separator);
8041
      space();
805
806
      // Try to line up the initializers with the first one that follows the ":":
807
      //
808
      //     Foo(notTrailing)
809
      //         : initializer = value,
810
      //           super(); // +2 from previous line.
811
      //
812
      //     Foo(
813
      //       trailing,
814
      //     ) : initializer = value,
815
      //         super(); // +4 from previous line.
816
      //
817
      // This doesn't work if there is a trailing comma in an optional parameter,
818
      // but we don't want to do a weird +5 alignment:
819
      //
820
      //     Foo({
821
      //       trailing,
822
      //     }) : initializer = value,
823
      //         super(); // Doesn't quite line up. :(
8242
      builder.indent(2);
825
    }
826
8274
    for (var i = 0; i < node.initializers.length; i++) {
8281
      if (i > 0) {
829
        // Preceding comma.
8305
        token(node.initializers[i].beginToken.previous);
8311
        newline();
832
      }
833
8343
      node.initializers[i].accept(this);
835
    }
836
8372
    builder.unindent();
8382
    if (!hasTrailingComma) builder.unindent();
839
840
    // End the rule for ":" after all of the initializers.
8412
    builder.endRule();
842
  }
843
8441
  visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
8452
    builder.nestExpression();
846
8472
    token(node.thisKeyword);
8482
    token(node.period);
8492
    visit(node.fieldName);
850
8513
    _visitAssignment(node.equals, node.expression);
852
8532
    builder.unnest();
854
  }
855
8562
  visitConstructorName(ConstructorName node) {
8574
    visit(node.type);
8584
    token(node.period);
8594
    visit(node.name);
860
  }
861
8621
  visitContinueStatement(ContinueStatement node) {
8632
    _simpleStatement(node, () {
8642
      token(node.continueKeyword);
8653
      visit(node.label, before: space);
866
    });
867
  }
868
8691
  visitDeclaredIdentifier(DeclaredIdentifier node) {
8702
    modifier(node.keyword);
8713
    visit(node.type, after: space);
8722
    visit(node.identifier);
873
  }
874
8752
  visitDefaultFormalParameter(DefaultFormalParameter node) {
8764
    visit(node.parameter);
8772
    if (node.separator != null) {
8784
      builder.startSpan();
8794
      builder.nestExpression();
880
8816
      if (_formatter.fixes.contains(StyleFix.namedDefaultSeparator)) {
882
        // Change the separator to "=".
8831
        space();
8842
        writePrecedingCommentsAndNewlines(node.separator);
8853
        _writeText("=", node.separator.offset);
886
      } else {
887
        // The '=' separator is preceded by a space, ":" is not.
8884
        if (node.separator.type == TokenType.EQ) space();
8892
        token(node.separator);
890
      }
891
8926
      soloSplit(_assignmentCost(node.defaultValue));
8934
      visit(node.defaultValue);
894
8954
      builder.unnest();
8964
      builder.endSpan();
897
    }
898
  }
899
9001
  visitDoStatement(DoStatement node) {
9012
    builder.nestExpression();
9022
    token(node.doKeyword);
9031
    space();
9042
    builder.unnest(now: false);
9052
    visit(node.body);
906
9072
    builder.nestExpression();
9081
    space();
9092
    token(node.whileKeyword);
9101
    space();
9112
    token(node.leftParenthesis);
9121
    soloZeroSplit();
9132
    visit(node.condition);
9142
    token(node.rightParenthesis);
9152
    token(node.semicolon);
9162
    builder.unnest();
917
  }
918
9191
  visitDottedName(DottedName node) {
9202
    for (var component in node.components) {
921
      // Write the preceding ".".
9223
      if (component != node.components.first) {
9233
        token(component.beginToken.previous);
924
      }
925
9261
      visit(component);
927
    }
928
  }
929
9301
  visitDoubleLiteral(DoubleLiteral node) {
9312
    token(node.literal);
932
  }
933
9341
  visitEmptyFunctionBody(EmptyFunctionBody node) {
9352
    token(node.semicolon);
936
  }
937
9381
  visitEmptyStatement(EmptyStatement node) {
9392
    token(node.semicolon);
940
  }
941
9421
  visitEnumConstantDeclaration(EnumConstantDeclaration node) {
9432
    visitMetadata(node.metadata);
9442
    visit(node.name);
945
  }
946
9471
  visitEnumDeclaration(EnumDeclaration node) {
9482
    visitMetadata(node.metadata);
949
9502
    token(node.enumKeyword);
9511
    space();
9522
    visit(node.name);
9531
    space();
954
9552
    _beginBody(node.leftBracket, space: true);
9563
    visitCommaSeparatedNodes(node.constants, between: splitOrTwoNewlines);
957
958
    // If there is a trailing comma, always force the constants to split.
9596
    if (node.constants.last.endToken.next.type == TokenType.COMMA) {
9602
      builder.forceRules();
961
    }
962
9632
    _endBody(node.rightBracket, space: true);
964
  }
965
9661
  visitExportDirective(ExportDirective node) {
9671
    _visitDirectiveMetadata(node);
9682
    _simpleStatement(node, () {
9692
      token(node.keyword);
9701
      space();
9712
      visit(node.uri);
972
9732
      _visitConfigurations(node.configurations);
974
9753
      builder.startRule(new CombinatorRule());
9762
      visitNodes(node.combinators);
9772
      builder.endRule();
978
    });
979
  }
980
9813
  visitExpressionFunctionBody(ExpressionFunctionBody node) {
982
    // Space after the parameter list.
9833
    space();
984
985
    // The "async" or "sync" keyword.
9869
    token(node.keyword, after: space);
987
988
    // Try to keep the "(...) => " with the start of the body for anonymous
989
    // functions.
9907
    if (_isInLambda(node)) builder.startSpan();
991
9926
    token(node.functionDefinition); // "=>".
993
994
    // Split after the "=>", using the rule created before the parameters
995
    // by _visitBody().
9963
    split();
997
998
    // If the body is a binary operator expression, then we want to force the
999
    // split at `=>` if the operators split. See visitBinaryExpression().
100012
    if (node.expression is! BinaryExpression) builder.endRule();
1001
10027
    if (_isInLambda(node)) builder.endSpan();
1003
10046
    builder.startBlockArgumentNesting();
10056
    builder.startSpan();
10066
    visit(node.expression);
10076
    builder.endSpan();
10086
    builder.endBlockArgumentNesting();
1009
10108
    if (node.expression is BinaryExpression) builder.endRule();
1011
10126
    token(node.semicolon);
1013
  }
1014
10152
  visitExpressionStatement(ExpressionStatement node) {
10164
    _simpleStatement(node, () {
10174
      visit(node.expression);
1018
    });
1019
  }
1020
10211
  visitExtendsClause(ExtendsClause node) {
10221
    soloSplit();
10232
    token(node.extendsKeyword);
10241
    space();
10252
    visit(node.superclass);
1026
  }
1027
10281
  visitFieldDeclaration(FieldDeclaration node) {
10292
    visitMetadata(node.metadata);
1030
10312
    _simpleStatement(node, () {
10322
      modifier(node.staticKeyword);
10332
      modifier(node.covariantKeyword);
10342
      visit(node.fields);
1035
    });
1036
  }
1037
10381
  visitFieldFormalParameter(FieldFormalParameter node) {
10393
    visitParameterMetadata(node.metadata, () {
10401
      _beginFormalParameter(node);
10413
      token(node.keyword, after: space);
10423
      visit(node.type, after: split);
10432
      token(node.thisKeyword);
10442
      token(node.period);
10452
      visit(node.identifier);
10462
      visit(node.parameters);
10471
      _endFormalParameter(node);
1048
    });
1049
  }
1050
10511
  visitForEachStatement(ForEachStatement node) {
10522
    builder.nestExpression();
10533
    token(node.awaitKeyword, after: space);
10542
    token(node.forKeyword);
10551
    space();
10562
    token(node.leftParenthesis);
1057
10581
    if (node.loopVariable != null) {
1059
      // TODO(rnystrom): The formatting logic here is slightly different from
1060
      // how parameter metadata is handled and from how variable metadata is
1061
      // handled. I think what it does works better in the context of a for-in
1062
      // loop, but consider trying to unify this with one of the above.
1063
      //
1064
      // Metadata on class and variable declarations is *always* split:
1065
      //
1066
      //     @foo
1067
      //     class Bar {}
1068
      //
1069
      // Metadata on parameters has some complex logic to handle multiple
1070
      // parameters with metadata. It also indents the parameters farther than
1071
      // the metadata when split:
1072
      //
1073
      //     function(
1074
      //         @foo(long arg list...)
1075
      //             parameter1,
1076
      //         @foo
1077
      //             parameter2) {}
1078
      //
1079
      // For for-in variables, we allow it to not split, like parameters, but
1080
      // don't indent the variable when it does split:
1081
      //
1082
      //     for (
1083
      //         @foo
1084
      //         @bar
1085
      //         var blah in stuff) {}
10862
      builder.startRule();
10875
      visitNodes(node.loopVariable.metadata, between: split, after: split);
10882
      visit(node.loopVariable);
10892
      builder.endRule();
1090
    } else {
10912
      visit(node.identifier);
1092
    }
10931
    soloSplit();
10942
    token(node.inKeyword);
10951
    space();
10962
    visit(node.iterable);
10972
    token(node.rightParenthesis);
10982
    builder.unnest();
1099
11002
    _visitLoopBody(node.body);
1101
  }
1102
11033
  visitFormalParameterList(FormalParameterList node,
1104
      {bool nestExpression: true}) {
1105
    // Corner case: empty parameter lists.
11066
    if (node.parameters.isEmpty) {
11076
      token(node.leftParenthesis);
1108
1109
      // If there is a comment, do allow splitting before it.
11107
      if (node.rightParenthesis.precedingComments != null) soloZeroSplit();
1111
11126
      token(node.rightParenthesis);
1113
      return;
1114
    }
1115
1116
    // If the parameter list has a trailing comma, format it like a collection
1117
    // literal where each parameter goes on its own line, they are indented +2,
1118
    // and the ")" ends up on its own line.
111912
    if (node.parameters.last.endToken.next.type == TokenType.COMMA) {
11201
      _visitTrailingCommaParameterList(node);
1121
      return;
1122
    }
1123
11242
    var requiredParams = node.parameters
11256
        .where((param) => param is! DefaultFormalParameter)
11262
        .toList();
11272
    var optionalParams = node.parameters
11286
        .where((param) => param is DefaultFormalParameter)
11292
        .toList();
1130
11312
    if (nestExpression) builder.nestExpression();
11324
    token(node.leftParenthesis);
1133
11346
    _metadataRules.add(new MetadataRule());
1135
1136
    var rule;
11372
    if (requiredParams.isNotEmpty) {
11382
      rule = new PositionalRule(null, 0, 0);
11396
      _metadataRules.last.bindPositionalRule(rule);
1140
11414
      builder.startRule(rule);
11422
      if (_isInLambda(node)) {
1143
        // Don't allow splitting before the first argument (i.e. right after
1144
        // the bare "(" in a lambda. Instead, just stuff a null chunk in there
1145
        // to avoid confusing the arg rule.
11461
        rule.beforeArgument(null);
1147
      } else {
1148
        // Split before the first argument.
11494
        rule.beforeArgument(zeroSplit());
1150
      }
1151
11524
      builder.startSpan();
1153
11544
      for (var param in requiredParams) {
11552
        visit(param);
1156
1157
        // Write the following comma.
11588
        if (param.endToken.next.type == TokenType.COMMA) {
11596
          token(param.endToken.next);
1160
        }
1161
11628
        if (param != requiredParams.last) rule.beforeArgument(split());
1163
      }
1164
11654
      builder.endSpan();
11664
      builder.endRule();
1167
    }
1168
11692
    if (optionalParams.isNotEmpty) {
11702
      var namedRule = new NamedRule(null, 0, 0);
11711
      if (rule != null) rule.setNamedArgsRule(namedRule);
1172
11736
      _metadataRules.last.bindNamedRule(namedRule);
1174
11754
      builder.startRule(namedRule);
1176
1177
      // Make sure multi-line default values are indented.
11784
      builder.startBlockArgumentNesting();
1179
11808
      namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty));
1181
1182
      // "[" or "{" for optional parameters.
11834
      token(node.leftDelimiter);
1184
11854
      for (var param in optionalParams) {
11862
        visit(param);
1187
1188
        // Write the following comma.
11898
        if (param.endToken.next.type == TokenType.COMMA) {
11906
          token(param.endToken.next);
1191
        }
1192
11938
        if (param != optionalParams.last) namedRule.beforeArgument(split());
1194
      }
1195
11964
      builder.endBlockArgumentNesting();
11974
      builder.endRule();
1198
1199
      // "]" or "}" for optional parameters.
12004
      token(node.rightDelimiter);
1201
    }
1202
12034
    _metadataRules.removeLast();
1204
12054
    token(node.rightParenthesis);
12062
    if (nestExpression) builder.unnest();
1207
  }
1208
12091
  visitForStatement(ForStatement node) {
12102
    builder.nestExpression();
12112
    token(node.forKeyword);
12121
    space();
12132
    token(node.leftParenthesis);
1214
12152
    builder.startRule();
1216
1217
    // The initialization clause.
12181
    if (node.initialization != null) {
12192
      visit(node.initialization);
12201
    } else if (node.variables != null) {
1221
      // Nest split variables more so they aren't at the same level
1222
      // as the rest of the loop clauses.
12232
      builder.nestExpression();
1224
1225
      // Allow the variables to stay unsplit even if the clauses split.
12262
      builder.startRule();
1227
12281
      var declaration = node.variables;
12292
      visitMetadata(declaration.metadata);
12302
      modifier(declaration.keyword);
12313
      visit(declaration.type, after: space);
1232
12333
      visitCommaSeparatedNodes(declaration.variables, between: () {
12341
        split();
1235
      });
1236
12372
      builder.endRule();
12382
      builder.unnest();
1239
    }
1240
12412
    token(node.leftSeparator);
1242
1243
    // The condition clause.
12442
    if (node.condition != null) split();
12452
    visit(node.condition);
12462
    token(node.rightSeparator);
1247
1248
    // The update clause.
12492
    if (node.updaters.isNotEmpty) {
12501
      split();
1251
1252
      // Allow the updates to stay unsplit even if the clauses split.
12532
      builder.startRule();
1254
12553
      visitCommaSeparatedNodes(node.updaters, between: split);
1256
12572
      builder.endRule();
1258
    }
1259
12602
    token(node.rightParenthesis);
12612
    builder.endRule();
12622
    builder.unnest();
1263
12642
    _visitLoopBody(node.body);
1265
  }
1266
12673
  visitFunctionDeclaration(FunctionDeclaration node) {
12686
    _visitMemberDeclaration(node, node.functionExpression);
1269
  }
1270
12712
  visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
12724
    visit(node.functionDeclaration);
1273
  }
1274
12752
  visitFunctionExpression(FunctionExpression node) {
1276
    // Inside a function body is no longer in the surrounding const context.
12772
    var oldConstNesting = _constNesting;
12782
    _constNesting = 0;
1279
1280
    // TODO(rnystrom): This is working but not tested. As of 2016/11/29, the
1281
    // latest version of analyzer on pub does not parse generic lambdas. When
1282
    // a version of it that does is published, upgrade dart_style to use it and
1283
    // then test it:
1284
    //
1285
    //     >>> generic function expression
1286
    //         main() {
1287
    //           var generic = < T,S >(){};
1288
    //     }
1289
    //     <<<
1290
    //     main() {
1291
    //       var generic = <T, S>() {};
1292
    //     }
12938
    _visitBody(node.typeParameters, node.parameters, node.body);
1294
12952
    _constNesting = oldConstNesting;
1296
  }
1297
12981
  visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
1299
    // Try to keep the entire invocation one line.
13002
    builder.startSpan();
13012
    builder.nestExpression();
1302
13032
    visit(node.function);
13042
    visit(node.typeArguments);
13052
    visitArgumentList(node.argumentList, nestExpression: false);
1306
13072
    builder.unnest();
13082
    builder.endSpan();
1309
  }
1310
13112
  visitFunctionTypeAlias(FunctionTypeAlias node) {
13124
    visitMetadata(node.metadata);
1313
13146
    if (_formatter.fixes.contains(StyleFix.functionTypedefs)) {
13152
      _simpleStatement(node, () {
1316
        // Inlined visitGenericTypeAlias
13173
        _visitGenericTypeAliasHeader(node.typedefKeyword, node.name,
13184
            node.typeParameters, null, (node.returnType ?? node.name).offset);
1319
13201
        space();
1321
1322
        // Recursively convert function-arguments to Function syntax.
13231
        _insideNewTypedefFix = true;
13241
        _visitGenericFunctionType(
13254
            node.returnType, null, node.name.offset, null, node.parameters);
13261
        _insideNewTypedefFix = false;
1327
      });
1328
      return;
1329
    }
1330
13312
    _simpleStatement(node, () {
13322
      token(node.typedefKeyword);
13331
      space();
13343
      visit(node.returnType, after: space);
13352
      visit(node.name);
13362
      visit(node.typeParameters);
13372
      visit(node.parameters);
1338
    });
1339
  }
1340
13412
  visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
13426
    visitParameterMetadata(node.metadata, () {
13432
      if (!_insideNewTypedefFix) {
13442
        modifier(node.covariantKeyword);
13453
        visit(node.returnType, after: space);
1346
        // Try to keep the function's parameters with its name.
13472
        builder.startSpan();
13482
        visit(node.identifier);
13493
        _visitParameterSignature(node.typeParameters, node.parameters);
13502
        builder.endSpan();
1351
      } else {
13521
        _beginFormalParameter(node);
13534
        _visitGenericFunctionType(node.returnType, null, node.identifier.offset,
13542
            node.typeParameters, node.parameters);
13551
        split();
13562
        visit(node.identifier);
13571
        _endFormalParameter(node);
1358
      }
1359
    });
1360
  }
1361
13622
  visitGenericFunctionType(GenericFunctionType node) {
13636
    _visitGenericFunctionType(node.returnType, node.functionKeyword, null,
13644
        node.typeParameters, node.parameters);
1365
  }
1366
13672
  visitGenericTypeAlias(GenericTypeAlias node) {
13688
    visitNodes(node.metadata, between: newline, after: newline);
13694
    _simpleStatement(node, () {
13706
      _visitGenericTypeAliasHeader(node.typedefKeyword, node.name,
13714
          node.typeParameters, node.equals, null);
1372
13732
      space();
1374
13754
      visit(node.functionType);
1376
    });
1377
  }
1378
13791
  visitHideCombinator(HideCombinator node) {
13803
    _visitCombinator(node.keyword, node.hiddenNames);
1381
  }
1382
13831
  visitIfStatement(IfStatement node) {
13842
    builder.nestExpression();
13852
    token(node.ifKeyword);
13861
    space();
13872
    token(node.leftParenthesis);
13882
    visit(node.condition);
13892
    token(node.rightParenthesis);
13902
    builder.unnest();
1391
13921
    visitClause(Statement clause) {
13932
      if (clause is Block || clause is IfStatement) {
13941
        space();
13951
        visit(clause);
1396
      } else {
1397
        // Allow splitting in a statement-bodied if even though it's against
1398
        // the style guide. Since we can't fix the code itself to follow the
1399
        // style guide, we should at least format it as well as we can.
14002
        builder.indent();
14012
        builder.startRule();
1402
1403
        // If there is an else clause, always split before both the then and
1404
        // else statements.
14051
        if (node.elseStatement != null) {
14062
          builder.writeWhitespace(Whitespace.newline);
1407
        } else {
14082
          builder.split(nest: false, space: true);
1409
        }
1410
14111
        visit(clause);
1412
14132
        builder.endRule();
14142
        builder.unindent();
1415
      }
1416
    }
1417
14182
    visitClause(node.thenStatement);
1419
14201
    if (node.elseStatement != null) {
14212
      if (node.thenStatement is Block) {
14221
        space();
1423
      } else {
1424
        // Corner case where an else follows a single-statement then clause.
1425
        // This is against the style guide, but we still need to handle it. If
1426
        // it happens, put the else on the next line.
14271
        newline();
1428
      }
1429
14302
      token(node.elseKeyword);
14312
      visitClause(node.elseStatement);
1432
    }
1433
  }
1434
14351
  visitImplementsClause(ImplementsClause node) {
14363
    _visitCombinator(node.implementsKeyword, node.interfaces);
1437
  }
1438
14391
  visitImportDirective(ImportDirective node) {
14401
    _visitDirectiveMetadata(node);
14412
    _simpleStatement(node, () {
14422
      token(node.keyword);
14431
      space();
14442
      visit(node.uri);
1445
14462
      _visitConfigurations(node.configurations);
1447
14481
      if (node.asKeyword != null) {
14491
        soloSplit();
14503
        token(node.deferredKeyword, after: space);
14512
        token(node.asKeyword);
14521
        space();
14532
        visit(node.prefix);
1454
      }
1455
14563
      builder.startRule(new CombinatorRule());
14572
      visitNodes(node.combinators);
14582
      builder.endRule();
1459
    });
1460
  }
1461
14621
  visitIndexExpression(IndexExpression node) {
14632
    builder.nestExpression();
1464
14651
    if (node.isCascaded) {
14660
      token(node.period);
1467
    } else {
14682
      visit(node.target);
1469
    }
1470
14711
    finishIndexExpression(node);
1472
14732
    builder.unnest();
1474
  }
1475
1476
  /// Visit the index part of [node], excluding the target.
1477
  ///
1478
  /// Called by [CallChainVisitor] to handle index expressions in the middle of
1479
  /// call chains.
14801
  void finishIndexExpression(IndexExpression node) {
14812
    if (node.target is IndexExpression) {
1482
      // Edge case: On a chain of [] accesses, allow splitting between them.
1483
      // Produces nicer output in cases like:
1484
      //
1485
      //     someJson['property']['property']['property']['property']...
14861
      soloZeroSplit();
1487
    }
1488
14892
    builder.startSpan(Cost.index);
14902
    token(node.leftBracket);
14911
    soloZeroSplit();
14922
    visit(node.index);
14932
    token(node.rightBracket);
14942
    builder.endSpan();
1495
  }
1496
14972
  visitInstanceCreationExpression(InstanceCreationExpression node) {
14984
    builder.startSpan();
1499
1500
    var includeKeyword = true;
1501
15022
    if (node.keyword != null) {
15036
      if (node.keyword.keyword == Keyword.NEW &&
15046
          _formatter.fixes.contains(StyleFix.optionalNew)) {
1505
        includeKeyword = false;
15066
      } else if (node.keyword.keyword == Keyword.CONST &&
15076
          _formatter.fixes.contains(StyleFix.optionalConst) &&
15084
          _constNesting > 0) {
1509
        includeKeyword = false;
1510
      }
1511
    }
1512
1513
    if (includeKeyword) {
15146
      token(node.keyword, after: space);
1515
    } else {
1516
      // Don't lose comments before the discarded keyword, if any.
15174
      writePrecedingCommentsAndNewlines(node.keyword);
1518
    }
1519
15204
    builder.startSpan(Cost.constructorName);
1521
1522
    // Start the expression nesting for the argument list here, in case this
1523
    // is a generic constructor with type arguments. If it is, we need the type
1524
    // arguments to be nested too so they get indented past the arguments.
15254
    builder.nestExpression();
15264
    visit(node.constructorName);
1527
15284
    _startPossibleConstContext(node.keyword);
1529
15304
    builder.endSpan();
15314
    visitArgumentList(node.argumentList, nestExpression: false);
15324
    builder.endSpan();
1533
15344
    _endPossibleConstContext(node.keyword);
1535
15364
    builder.unnest();
1537
  }
1538
15392
  visitIntegerLiteral(IntegerLiteral node) {
15404
    token(node.literal);
1541
  }
1542
15432
  visitInterpolationExpression(InterpolationExpression node) {
15444
    builder.preventSplit();
15454
    token(node.leftBracket);
15464
    builder.startSpan();
15474
    visit(node.expression);
15484
    builder.endSpan();
15494
    token(node.rightBracket);
15504
    builder.endPreventSplit();
1551
  }
1552
15532
  visitInterpolationString(InterpolationString node) {
15544
    _writeStringLiteral(node.contents);
1555
  }
1556
15571
  visitIsExpression(IsExpression node) {
15582
    builder.startSpan();
15592
    builder.nestExpression();
15602
    visit(node.expression);
15611
    soloSplit();
15622
    token(node.isOperator);
15632
    token(node.notOperator);
15641
    space();
15652
    visit(node.type);
15662
    builder.unnest();
15672
    builder.endSpan();
1568
  }
1569
15701
  visitLabel(Label node) {
15712
    visit(node.label);
15722
    token(node.colon);
1573
  }
1574
15751
  visitLabeledStatement(LabeledStatement node) {
15762
    _visitLabels(node.labels);
15772
    visit(node.statement);
1578
  }
1579
15801
  visitLibraryDirective(LibraryDirective node) {
15811
    _visitDirectiveMetadata(node);
15822
    _simpleStatement(node, () {
15832
      token(node.keyword);
15841
      space();
15852
      visit(node.name);
1586
    });
1587
  }
1588
15891
  visitLibraryIdentifier(LibraryIdentifier node) {
15903
    visit(node.components.first);
15913
    for (var component in node.components.skip(1)) {
15923
      token(component.beginToken.previous); // "."
15931
      visit(component);
1594
    }
1595
  }
1596
15972
  visitListLiteral(ListLiteral node) {
1598
    // Corner case: Splitting inside a list looks bad if there's only one
1599
    // element, so make those more costly.
16006
    var cost = node.elements.length <= 1 ? Cost.singleElementList : Cost.normal;
16012
    _visitCollectionLiteral(
16026
        node, node.leftBracket, node.elements, node.rightBracket, cost);
1603
  }
1604
16052
  visitMapLiteral(MapLiteral node) {
16062
    _visitCollectionLiteral(
16076
        node, node.leftBracket, node.entries, node.rightBracket);
1608
  }
1609
16101
  visitMapLiteralEntry(MapLiteralEntry node) {
16112
    visit(node.key);
16122
    token(node.separator);
16131
    soloSplit();
16142
    visit(node.value);
1615
  }
1616
16171
  visitMethodDeclaration(MethodDeclaration node) {
16181
    _visitMemberDeclaration(node, node);
1619
  }
1620
16213
  visitMethodInvocation(MethodInvocation node) {
1622
    // If there's no target, this is a "bare" function call like "foo(1, 2)",
1623
    // or a section in a cascade.
1624
    //
1625
    // If it looks like a constructor or static call, we want to keep the
1626
    // target and method together instead of including the method in the
1627
    // subsequent method chain. When this happens, it's important that this
1628
    // code here has the same rules as in [visitInstanceCreationExpression].
1629
    //
1630
    // That ensures that the way some code is formatted is not affected by the
1631
    // presence or absence of `new`/`const`. In particular, it means that if
1632
    // they run `dartfmt --fix`, and then run `dartfmt` *again*, the second run
1633
    // will not produce any additional changes.
16344
    if (node.target == null || looksLikeStaticCall(node)) {
1635
      // Try to keep the entire method invocation one line.
16366
      builder.nestExpression();
16376
      builder.startSpan();
1638
16393
      if (node.target != null) {
16402
        builder.startSpan(Cost.constructorName);
16412
        visit(node.target);
16421
        soloZeroSplit();
1643
      }
1644
1645
      // If target is null, this will be `..` for a cascade.
16466
      token(node.operator);
16476
      visit(node.methodName);
1648
16495
      if (node.target != null) builder.endSpan();
1650
1651
      // TODO(rnystrom): Currently, there are no constraints between a generic
1652
      // method's type arguments and arguments. That can lead to some funny
1653
      // splitting like:
1654
      //
1655
      //     method<VeryLongType,
1656
      //             AnotherTypeArgument>(argument,
1657
      //         argument, argument, argument);
1658
      //
1659
      // The indentation is fine, but splitting in the middle of each argument
1660
      // list looks kind of strange. If this ends up happening in real world
1661
      // code, consider putting a constraint between them.
16626
      builder.nestExpression();
16636
      visit(node.typeArguments);
16646
      visitArgumentList(node.argumentList, nestExpression: false);
16656
      builder.unnest();
1666
16676
      builder.endSpan();
16686
      builder.unnest();
1669
      return;
1670
    }
1671
16722
    new CallChainVisitor(this, node).visit();
1673
  }
1674
16751
  visitMixinDeclaration(MixinDeclaration node) {
16762
    visitMetadata(node.metadata);
1677
16782
    builder.nestExpression();
16792
    token(node.mixinKeyword);
16801
    space();
16812
    visit(node.name);
16822
    visit(node.typeParameters);
1683
1684
    // If there is only a single superclass constraint, format it like an
1685
    // "extends" in a class.
16861
    if (node.onClause != null &&
16874
        node.onClause.superclassConstraints.length == 1) {
16881
      soloSplit();
16893
      token(node.onClause.onKeyword);
16901
      space();
16914
      visit(node.onClause.superclassConstraints.single);
1692
    }
1693
16943
    builder.startRule(new CombinatorRule());
1695
1696
    // If there are multiple superclass constraints, format them like the
1697
    // "implements" clause.
16981
    if (node.onClause != null &&
16994
        node.onClause.superclassConstraints.length > 1) {
17002
      visit(node.onClause);
1701
    }
1702
17032
    visit(node.implementsClause);
17042
    builder.endRule();
1705
17061
    space();
1707
17082
    builder.unnest();
17092
    _beginBody(node.leftBracket);
17102
    _visitMembers(node.members);
17112
    _endBody(node.rightBracket);
1712
  }
1713
17141
  visitNamedExpression(NamedExpression node) {
17151
    visitNamedArgument(node);
1716
  }
1717
17181
  visitNativeClause(NativeClause node) {
17192
    token(node.nativeKeyword);
17203
    visit(node.name, before: space);
1721
  }
1722
17231
  visitNativeFunctionBody(NativeFunctionBody node) {
17242
    _simpleStatement(node, () {
17252
      builder.nestExpression(now: true);
17261
      soloSplit();
17272
      token(node.nativeKeyword);
17283
      visit(node.stringLiteral, before: space);
17292
      builder.unnest();
1730
    });
1731
  }
1732
17331
  visitNullLiteral(NullLiteral node) {
17342
    token(node.literal);
1735
  }
1736
17371
  visitOnClause(OnClause node) {
17383
    _visitCombinator(node.onKeyword, node.superclassConstraints);
1739
  }
1740
17411
  visitParenthesizedExpression(ParenthesizedExpression node) {
17422
    builder.nestExpression();
17432
    token(node.leftParenthesis);
17442
    visit(node.expression);
17452
    builder.unnest();
17462
    token(node.rightParenthesis);
1747
  }
1748
17491
  visitPartDirective(PartDirective node) {
17501
    _visitDirectiveMetadata(node);
17512
    _simpleStatement(node, () {
17522
      token(node.keyword);
17531
      space();
17542
      visit(node.uri);
1755
    });
1756
  }
1757
17581
  visitPartOfDirective(PartOfDirective node) {
17591
    _visitDirectiveMetadata(node);
17602
    _simpleStatement(node, () {
17612
      token(node.keyword);
17621
      space();
17632
      token(node.ofKeyword);
17641
      space();
1765
1766
      // Part-of may have either a name or a URI. Only one of these will be
1767
      // non-null. We visit both since visit() ignores null.
17682
      visit(node.libraryName);
17692
      visit(node.uri);
1770
    });
1771
  }
1772
17731
  visitPostfixExpression(PostfixExpression node) {
17742
    visit(node.operand);
17752
    token(node.operator);
1776
  }
1777
17782
  visitPrefixedIdentifier(PrefixedIdentifier node) {
17794
    new CallChainVisitor(this, node).visit();
1780
  }
1781
17821
  visitPrefixExpression(PrefixExpression node) {
17832
    token(node.operator);
1784
1785
    // Edge case: put a space after "-" if the operand is "-" or "--" so we
1786
    // don't merge the operators.
17871
    var operand = node.operand;
17881
    if (operand is PrefixExpression &&
17896
        (operand.operator.lexeme == "-" || operand.operator.lexeme == "--")) {
17901
      space();
1791
    }
1792
17932
    visit(node.operand);
1794
  }
1795
17961
  visitPropertyAccess(PropertyAccess node) {
17971
    if (node.isCascaded) {
17982
      token(node.operator);
17992
      visit(node.propertyName);
1800
      return;
1801
    }
1802
18032
    new CallChainVisitor(this, node).visit();
1804
  }
1805
18061
  visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
18072
    builder.startSpan();
1808
18092
    token(node.thisKeyword);
18102
    token(node.period);
18112
    visit(node.constructorName);
18122
    visit(node.argumentList);
1813
18142
    builder.endSpan();
1815
  }
1816
18170
  visitRethrowExpression(RethrowExpression node) {
18180
    token(node.rethrowKeyword);
1819
  }
1820
18211
  visitReturnStatement(ReturnStatement node) {
18222
    _simpleStatement(node, () {
18232
      token(node.returnKeyword);
18243
      visit(node.expression, before: space);
1825
    });
1826
  }
1827
18281
  visitScriptTag(ScriptTag node) {
1829
    // The lexeme includes the trailing newline. Strip it off since the
1830
    // formatter ensures it gets a newline after it. Since the script tag must
1831
    // come at the top of the file, we don't have to worry about preceding
1832
    // comments or whitespace.
18335
    _writeText(node.scriptTag.lexeme.trim(), node.offset);
18341
    newline();
1835
  }
1836
18371
  visitSetLiteral(SetLiteral node) {
18381
    _visitCollectionLiteral(
18393
        node, node.leftBracket, node.elements, node.rightBracket);
1840
  }
1841
18421
  visitShowCombinator(ShowCombinator node) {
18433
    _visitCombinator(node.keyword, node.shownNames);
1844
  }
1845
18462
  visitSimpleFormalParameter(SimpleFormalParameter node) {
18476
    visitParameterMetadata(node.metadata, () {
18482
      _beginFormalParameter(node);
1849
18504
      modifier(node.keyword);
18512
      var hasType = node.type != null;
18522
      if (_insideNewTypedefFix && !hasType) {
1853
        // In function declarations and the old typedef syntax, you can have a
1854
        // parameter name without a type. In the new syntax, you can have a type
1855
        // without a name. Add "dynamic" in that case.
1856
1857
        // Ensure comments on the identifier comes before the inserted type.
18584
        token(node.identifier.token, before: () {
18593
          _writeText("dynamic", node.identifier.offset);
18601
          split();
1861
        });
1862
      } else {
18634
        visit(node.type);
1864
18654
        if (hasType && node.identifier != null) split();
1866
18674
        visit(node.identifier);
1868
      }
1869
18702
      _endFormalParameter(node);
1871
    });
1872
  }
1873
18743
  visitSimpleIdentifier(SimpleIdentifier node) {
18756
    token(node.token);
1876
  }
1877
18782
  visitSimpleStringLiteral(SimpleStringLiteral node) {
18794
    _writeStringLiteral(node.literal);
1880
  }
1881
18822
  visitStringInterpolation(StringInterpolation node) {
18834
    for (var element in node.elements) {
18842
      visit(element);
1885
    }
1886
  }
1887
18881
  visitSuperConstructorInvocation(SuperConstructorInvocation node) {
18892
    builder.startSpan();
1890
18912
    token(node.superKeyword);
18922
    token(node.period);
18932
    visit(node.constructorName);
18942
    visit(node.argumentList);
1895
18962
    builder.endSpan();
1897
  }
1898
18990
  visitSuperExpression(SuperExpression node) {
19000
    token(node.superKeyword);
1901
  }
1902
19031
  visitSwitchCase(SwitchCase node) {
19042
    _visitLabels(node.labels);
19052
    token(node.keyword);
19061
    space();
19072
    visit(node.expression);
19082
    token(node.colon);
1909
19102
    builder.indent();
1911
    // TODO(rnystrom): Allow inline cases?
19121
    newline();
1913
19143
    visitNodes(node.statements, between: oneOrTwoNewlines);
19152
    builder.unindent();
1916
  }
1917
19181
  visitSwitchDefault(SwitchDefault node) {
19192
    _visitLabels(node.labels);
19202
    token(node.keyword);
19212
    token(node.colon);
1922
19232
    builder.indent();
1924
    // TODO(rnystrom): Allow inline cases?
19251
    newline();
1926
19273
    visitNodes(node.statements, between: oneOrTwoNewlines);
19282
    builder.unindent();
1929
  }
1930
19311
  visitSwitchStatement(SwitchStatement node) {
19322
    builder.nestExpression();
19332
    token(node.switchKeyword);
19341
    space();
19352
    token(node.leftParenthesis);
19361
    soloZeroSplit();
19372
    visit(node.expression);
19382
    token(node.rightParenthesis);
19391
    space();
19402
    token(node.leftBracket);
19412
    builder.unnest();
19422
    builder.indent();
19431
    newline();
1944
19454
    visitNodes(node.members, between: oneOrTwoNewlines, after: newline);
19463
    token(node.rightBracket, before: () {
19472
      builder.unindent();
19481
      newline();
1949
    });
1950
  }
1951
19521
  visitSymbolLiteral(SymbolLiteral node) {
19532
    token(node.poundSign);
19541
    var components = node.components;
19552
    for (var component in components) {
1956
      // The '.' separator
19573
      if (component.previous.lexeme == '.') {
19582
        token(component.previous);
1959
      }
19601
      token(component);
1961
    }
1962
  }
1963
19641
  visitThisExpression(ThisExpression node) {
19652
    token(node.thisKeyword);
1966
  }
1967
19681
  visitThrowExpression(ThrowExpression node) {
19692
    token(node.throwKeyword);
19701
    space();
19712
    visit(node.expression);
1972
  }
1973
19742
  visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
19754
    visitMetadata(node.metadata);
1976
19774
    _simpleStatement(node, () {
19784
      visit(node.variables);
1979
    });
1980
  }
1981
19821
  visitTryStatement(TryStatement node) {
19832
    token(node.tryKeyword);
19841
    space();
19852
    visit(node.body);
19864
    visitNodes(node.catchClauses, before: space, between: space);
19874
    token(node.finallyKeyword, before: space, after: space);
19882
    visit(node.finallyBlock);
1989
  }
1990
19912
  visitTypeArgumentList(TypeArgumentList node) {
19928
    _visitGenericList(node.leftBracket, node.rightBracket, node.arguments);
1993
  }
1994
19953
  visitTypeName(TypeName node) {
19966
    visit(node.name);
19976
    visit(node.typeArguments);
1998
  }
1999
20002
  visitTypeParameter(TypeParameter node) {
20016
    visitParameterMetadata(node.metadata, () {
20024
      visit(node.name);
20038
      token(node.extendsKeyword, before: space, after: space);
20044
      visit(node.bound);
2005
    });
2006
  }
2007
20082
  visitTypeParameterList(TypeParameterList node) {
20096
    _metadataRules.add(new MetadataRule());
2010
20118
    _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
2012
20134
    _metadataRules.removeLast();
2014
  }
2015
20162
  visitVariableDeclaration(VariableDeclaration node) {
20174
    visit(node.name);
20182
    if (node.initializer == null) return;
2019
2020
    // If there are multiple variables being declared, we want to nest the
2021
    // initializers farther so they don't line up with the variables. Bad:
2022
    //
2023
    //     var a =
2024
    //         aValue,
2025
    //         b =
2026
    //         bValue;
2027
    //
2028
    // Good:
2029
    //
2030
    //     var a =
2031
    //             aValue,
2032
    //         b =
2033
    //             bValue;
2034
    var hasMultipleVariables =
20358
        (node.parent as VariableDeclarationList).variables.length > 1;
2036
20376
    _visitAssignment(node.equals, node.initializer, nest: hasMultipleVariables);
2038
  }
2039
20402
  visitVariableDeclarationList(VariableDeclarationList node) {
20414
    visitMetadata(node.metadata);
2042
2043
    // Allow but try to avoid splitting between the type and name.
20444
    builder.startSpan();
2045
20464
    modifier(node.keyword);
20476
    visit(node.type, after: soloSplit);
2048
20494
    builder.endSpan();
2050
20514
    _startPossibleConstContext(node.keyword);
2052
2053
    // Use a single rule for all of the variables. If there are multiple
2054
    // declarations, we will try to keep them all on one line. If that isn't
2055
    // possible, we split after *every* declaration so that each is on its own
2056
    // line.
20574
    builder.startRule();
20586
    visitCommaSeparatedNodes(node.variables, between: split);
20594
    builder.endRule();
2060
20614
    _endPossibleConstContext(node.keyword);
2062
  }
2063
20642
  visitVariableDeclarationStatement(VariableDeclarationStatement node) {
20654
    _simpleStatement(node, () {
20664
      visit(node.variables);
2067
    });
2068
  }
2069
20701
  visitWhileStatement(WhileStatement node) {
20712
    builder.nestExpression();
20722
    token(node.whileKeyword);
20731
    space();
20742
    token(node.leftParenthesis);
20751
    soloZeroSplit();
20762
    visit(node.condition);
20772
    token(node.rightParenthesis);
20782
    builder.unnest();
2079
20802
    _visitLoopBody(node.body);
2081
  }
2082
20831
  visitWithClause(WithClause node) {
20843
    _visitCombinator(node.withKeyword, node.mixinTypes);
2085
  }
2086
20871
  visitYieldStatement(YieldStatement node) {
20882
    _simpleStatement(node, () {
20892
      token(node.yieldKeyword);
20902
      token(node.star);
20911
      space();
20922
      visit(node.expression);
2093
    });
2094
  }
2095
2096
  /// Visit a [node], and if not null, optionally preceded or followed by the
2097
  /// specified functions.
20983
  void visit(AstNode node, {void before(), void after()}) {
2099
    if (node == null) return;
2100
21011
    if (before != null) before();
2102
21033
    node.accept(this);
2104
21053
    if (after != null) after();
2106
  }
2107
2108
  /// Visit metadata annotations on declarations, and members.
2109
  ///
2110
  /// These always force the annotations to be on the previous line.
21113
  void visitMetadata(NodeList<Annotation> metadata) {
21129
    visitNodes(metadata, between: newline, after: newline);
2113
  }
2114
2115
  /// Visit metadata annotations for a directive.
2116
  ///
2117
  /// Always force the annotations to be on a previous line.
21181
  void _visitDirectiveMetadata(Directive directive) {
2119
    // Preserve a blank line before the first directive since users (in
2120
    // particular the test package) sometimes use that for metadata that
2121
    // applies to the entire library and not the following directive itself.
2122
    var isFirst =
21234
        directive == (directive.parent as CompilationUnit).directives.first;
2124
21252
    visitNodes(directive.metadata,
21263
        between: newline, after: isFirst ? oneOrTwoNewlines : newline);
2127
  }
2128
2129
  /// Visits metadata annotations on parameters and type parameters.
2130
  ///
2131
  /// Unlike other annotations, these are allowed to stay on the same line as
2132
  /// the parameter.
21332
  void visitParameterMetadata(
2134
      NodeList<Annotation> metadata, void visitParameter()) {
21352
    if (metadata == null || metadata.isEmpty) {
21362
      visitParameter();
2137
      return;
2138
    }
2139
2140
    // Split before all of the annotations or none.
21418
    builder.startLazyRule(_metadataRules.last);
2142
21436
    visitNodes(metadata, between: split, after: () {
2144
      // Don't nest until right before the last metadata. Ensures we only
2145
      // indent the parameter and not any of the metadata:
2146
      //
2147
      //     function(
2148
      //         @LongAnnotation
2149
      //         @LongAnnotation
2150
      //             indentedParameter) {}
21514
      builder.nestExpression(now: true);
21522
      split();
2153
    });
21542
    visitParameter();
2155
21564
    builder.unnest();
2157
2158
    // Wrap the rule around the parameter too. If it splits, we want to force
2159
    // the annotations to split as well.
21604
    builder.endRule();
2161
  }
2162
2163
  /// Visits [node], which may be in an argument list controlled by [rule].
2164
  ///
2165
  /// This is called directly by [ArgumentListVisitor] so that it can pass in
2166
  /// the surrounding named argument rule. That way, this can ensure that a
2167
  /// split between the name and argument forces the argument list to split
2168
  /// too.
21691
  void visitNamedArgument(NamedExpression node, [NamedRule rule]) {
21702
    builder.nestExpression();
21712
    builder.startSpan();
21722
    visit(node.name);
2173
2174
    // Don't allow a split between a name and a collection. Instead, we want
2175
    // the collection itself to split, or to split before the argument.
21764
    if (node.expression is ListLiteral || node.expression is MapLiteral) {
21771
      space();
2178
    } else {
21791
      var split = soloSplit();
21801
      if (rule != null) split.imply(rule);
2181
    }
2182
21832
    visit(node.expression);
21842
    builder.endSpan();
21852
    builder.unnest();
2186
  }
2187
2188
  /// Visits the `=` and the following expression in any place where an `=`
2189
  /// appears:
2190
  ///
2191
  /// * Assignment
2192
  /// * Variable declaration
2193
  /// * Constructor initialization
2194
  ///
2195
  /// If [nest] is true, an extra level of expression nesting is added after
2196
  /// the "=".
21972
  void _visitAssignment(Token equalsOperator, Expression rightHandSide,
2198
      {bool nest: false}) {
21992
    space();
22002
    token(equalsOperator);
2201
22022
    if (nest) builder.nestExpression(now: true);
2203
22044
    soloSplit(_assignmentCost(rightHandSide));
22054
    builder.startSpan();
22062
    visit(rightHandSide);
22074
    builder.endSpan();
2208
22092
    if (nest) builder.unnest();
2210
  }
2211
2212
  /// Visits a type parameter or type argument list.
22132
  void _visitGenericList(
2214
      Token leftBracket, Token rightBracket, List<AstNode> nodes) {
22152
    var rule = new TypeArgumentRule();
22164
    builder.startLazyRule(rule);
22174
    builder.startSpan();
22184
    builder.nestExpression();
2219
22202
    token(leftBracket);
22214
    rule.beforeArgument(zeroSplit());
2222
22234
    for (var node in nodes) {
22242
      visit(node);
2225
2226
      // Write the trailing comma.
22274
      if (node != nodes.last) {
22286
        token(node.endToken.next);
22294
        rule.beforeArgument(split());
2230
      }
2231
    }
2232
22332
    token(rightBracket);
2234
22354
    builder.unnest();
22364
    builder.endSpan();
22374
    builder.endRule();
2238
  }
2239
2240
  /// Visits a sequence of labels before a statement or switch case.
22411
  void _visitLabels(NodeList<Label> labels) {
22423
    visitNodes(labels, between: newline, after: newline);
2243
  }
2244
2245
  /// Visits the list of members in a class or mixin declaration.
22461
  void _visitMembers(NodeList<ClassMember> members) {
22472
    for (var member in members) {
22481
      visit(member);
2249
22502
      if (member == members.last) {
22511
        newline();
2252
        break;
2253
      }
2254
2255
      // Add a blank line after non-empty block methods.
2256
      var needsDouble = false;
22573
      if (member is MethodDeclaration && member.body is BlockFunctionBody) {
22581
        var body = member.body as BlockFunctionBody;
22593
        needsDouble = body.block.statements.isNotEmpty;
2260
      }
2261
2262
      if (needsDouble) {
22631
        twoNewlines();
2264
      } else {
2265
        // Variables and arrow-bodied members can be more tightly packed if
2266
        // the user wants to group things together.
22671
        oneOrTwoNewlines();
2268
      }
2269
    }
2270
  }
2271
2272
  /// Visits a top-level function or method declaration.
2273
  ///
2274
  /// The two AST node types are very similar but, alas, share no common
2275
  /// interface type in analyzer, hence the dynamic typing.
22763
  void _visitMemberDeclaration(
2277
      /* FunctionDeclaration|MethodDeclaration */ node,
2278
      /* FunctionExpression|MethodDeclaration */ function) {
22796
    visitMetadata(node.metadata as NodeList<Annotation>);
2280
2281
    // Nest the signature in case we have to split between the return type and
2282
    // name.
22836
    builder.nestExpression();
22846
    builder.startSpan();
22856
    modifier(node.externalKeyword);
22865
    if (node is MethodDeclaration) modifier(node.modifierKeyword);
22879
    visit(node.returnType, after: soloSplit);
22886
    modifier(node.propertyKeyword);
22895
    if (node is MethodDeclaration) modifier(node.operatorKeyword);
22906
    visit(node.name);
22916
    builder.endSpan();
2292
2293
    TypeParameterList typeParameters;
22943
    if (node is FunctionDeclaration) {
22956
      typeParameters = node.functionExpression.typeParameters;
2296
    } else {
22971
      typeParameters = (node as MethodDeclaration).typeParameters;
2298
    }
2299
230012
    _visitBody(typeParameters, function.parameters, function.body, () {
2301
      // If the body is a block, we need to exit nesting before we hit the body
2302
      // indentation, but we do want to wrap it around the parameters.
230310
      if (function.body is! ExpressionFunctionBody) builder.unnest();
2304
    });
2305
2306
    // If it's an expression, we want to wrap the nesting around that so that
2307
    // the body gets nested farther.
230810
    if (function.body is ExpressionFunctionBody) builder.unnest();
2309
  }
2310
2311
  /// Visit the given function [parameters] followed by its [body], printing a
2312
  /// space before it if it's not empty.
2313
  ///
2314
  /// If [beforeBody] is provided, it is invoked before the body is visited.
23153
  void _visitBody(TypeParameterList typeParameters,
2316
      FormalParameterList parameters, FunctionBody body,
2317
      [beforeBody()]) {
2318
    // If the body is "=>", add an extra level of indentation around the
2319
    // parameters and a rule that spans the parameters and the "=>". This
2320
    // ensures that if the parameters wrap, they wrap more deeply than the "=>"
2321
    // does, as in:
2322
    //
2323
    //     someFunction(parameter,
2324
    //             parameter, parameter) =>
2325
    //         "the body";
2326
    //
2327
    // Also, it ensures that if the parameters wrap, we split at the "=>" too
2328
    // to avoid:
2329
    //
2330
    //     someFunction(parameter,
2331
    //         parameter) => function(
2332
    //         argument);
2333
    //
2334
    // This is confusing because it looks like those two lines are at the same
2335
    // level when they are actually unrelated. Splitting at "=>" forces:
2336
    //
2337
    //     someFunction(parameter,
2338
    //             parameter) =>
2339
    //         function(
2340
    //             argument);
23413
    if (body is ExpressionFunctionBody) {
23426
      builder.nestExpression();
2343
2344
      // This rule is ended by visitExpressionFunctionBody().
23459
      builder.startLazyRule(new Rule(Cost.arrow));
2346
    }
2347
23483
    _visitParameterSignature(typeParameters, parameters);
2349
23503
    if (beforeBody != null) beforeBody();
23513
    visit(body);
2352
23539
    if (body is ExpressionFunctionBody) builder.unnest();
2354
  }
2355
2356
  /// Visits the type parameters (if any) and formal parameters of a method
2357
  /// declaration, function declaration, or generic function type.
23583
  void _visitParameterSignature(
2359
      TypeParameterList typeParameters, FormalParameterList parameters) {
2360
    // Start the nesting for the parameters here, so they indent past the
2361
    // type parameters too, if any.
23626
    builder.nestExpression();
2363
23643
    visit(typeParameters);
2365
    if (parameters != null) {
23663
      visitFormalParameterList(parameters, nestExpression: false);
2367
    }
2368
23696
    builder.unnest();
2370
  }
2371
2372
  /// Visits the body statement of a `for`, `for in`, or `while` loop.
23731
  void _visitLoopBody(Statement body) {
23741
    if (body is EmptyStatement) {
2375
      // No space before the ";".
23761
      visit(body);
23771
    } else if (body is Block) {
23781
      space();
23791
      visit(body);
2380
    } else {
2381
      // Allow splitting in a statement-bodied loop even though it's against
2382
      // the style guide. Since we can't fix the code itself to follow the
2383
      // style guide, we should at least format it as well as we can.
23842
      builder.indent();
23852
      builder.startRule();
2386
23872
      builder.split(nest: false, space: true);
23881
      visit(body);
2389
23902
      builder.endRule();
23912
      builder.unindent();
2392
    }
2393
  }
2394
2395
  /// Visit a list of [nodes] if not null, optionally separated and/or preceded
2396
  /// and followed by the given functions.
23973
  void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
23983
    if (nodes == null || nodes.isEmpty) return;
2399
24001
    if (before != null) before();
2401
24024
    visit(nodes.first);
24033
    for (var node in nodes.skip(1)) {
24041
      if (between != null) between();
24051
      visit(node);
2406
    }
2407
24082
    if (after != null) after();
2409
  }
2410
2411
  /// Visit a comma-separated list of [nodes] if not null.
24122
  void visitCommaSeparatedNodes(Iterable<AstNode> nodes, {between()}) {
24132
    if (nodes == null || nodes.isEmpty) return;
2414
24150
    if (between == null) between = space;
2416
2417
    var first = true;
24184
    for (var node in nodes) {
24191
      if (!first) between();
2420
      first = false;
2421
24222
      visit(node);
2423
2424
      // The comma after the node.
242511
      if (node.endToken.next.lexeme == ",") token(node.endToken.next);
2426
    }
2427
  }
2428
2429
  /// Visits the collection literal [node] whose body starts with [leftBracket],
2430
  /// ends with [rightBracket] and contains [elements].
2431
  ///
2432
  /// This is also used for argument lists with a trailing comma which are
2433
  /// considered "collection-like". In that case, [node] is `null`.
24342
  void _visitCollectionLiteral(TypedLiteral node, Token leftBracket,
2435
      Iterable<AstNode> elements, Token rightBracket,
2436
      [int cost]) {
2437
    if (node != null) {
2438
      // See if `const` should be removed.
24392
      if (node.constKeyword != null &&
24404
          _constNesting > 0 &&
24416
          _formatter.fixes.contains(StyleFix.optionalConst)) {
2442
        // Don't lose comments before the discarded keyword, if any.
24434
        writePrecedingCommentsAndNewlines(node.constKeyword);
2444
      } else {
24454
        modifier(node.constKeyword);
2446
      }
2447
24484
      visit(node.typeArguments);
2449
    }
2450
2451
    // Don't allow splitting in an empty collection.
24524
    if (elements.isEmpty && rightBracket.precedingComments == null) {
24532
      token(leftBracket);
24542
      token(rightBracket);
2455
      return;
2456
    }
2457
2458
    // Force all of the surrounding collections to split.
24597
    for (var i = 0; i < _collectionSplits.length; i++) {
24602
      _collectionSplits[i] = true;
2461
    }
2462
2463
    // Add this collection to the stack.
24644
    _collectionSplits.add(false);
2465
24662
    _startLiteralBody(leftBracket);
24674
    if (node != null) _startPossibleConstContext(node.constKeyword);
2468
2469
    // If a collection contains a line comment, we assume it's a big complex
2470
    // blob of data with some documented structure. In that case, the user
2471
    // probably broke the elements into lines deliberately, so preserve those.
24722
    var preserveNewlines = _containsLineComments(elements, rightBracket);
2473
2474
    var rule;
2475
    var lineRule;
2476
    if (preserveNewlines) {
2477
      // Newlines are significant, so we'll explicitly write those. Elements
2478
      // on the same line all share an argument-list-like rule that allows
2479
      // splitting between zero, one, or all of them. This is faster in long
2480
      // lists than using individual splits after each element.
24811
      lineRule = new TypeArgumentRule();
24822
      builder.startLazyRule(lineRule);
2483
    } else {
2484
      // Newlines aren't significant, so use a hard rule to split the elements.
2485
      // The parent chunk of the collection will handle the unsplit case, so
2486
      // this only comes into play when the collection is split.
24872
      rule = new Rule.hard();
24884
      builder.startRule(rule);
2489
    }
2490
24914
    for (var element in elements) {
24924
      if (element != elements.first) {
2493
        if (preserveNewlines) {
2494
          // See if the next element is on the next line.
24954
          if (_endLine(element.beginToken.previous) !=
24962
              _startLine(element.beginToken)) {
24971
            oneOrTwoNewlines();
2498
2499
            // Start a new rule for the new line.
25002
            builder.endRule();
25011
            lineRule = new TypeArgumentRule();
25022
            builder.startLazyRule(lineRule);
2503
          } else {
25042
            lineRule.beforeArgument(split());
2505
          }
2506
        } else {
25074
          builder.split(nest: false, space: true);
2508
        }
2509
      }
2510
25114
      builder.nestExpression();
25122
      visit(element);
2513
2514
      // The comma after the element.
25158
      if (element.endToken.next.type == TokenType.COMMA) {
25166
        token(element.endToken.next);
2517
      }
2518
25194
      builder.unnest();
2520
    }
2521
25224
    builder.endRule();
2523
2524
    // If there is a collection inside this one, it forces this one to split.
25254
    var force = _collectionSplits.removeLast();
2526
2527
    // If the collection has a trailing comma, the user must want it to split.
25282
    if (elements.isNotEmpty &&
252910
        elements.last.endToken.next.type == TokenType.COMMA) {
2530
      force = true;
2531
    }
2532
25334
    if (node != null) _endPossibleConstContext(node.constKeyword);
25342
    _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force);
2535
  }
2536
2537
  /// Writes [parameters], which is assumed to have a trailing comma after the
2538
  /// last parameter.
2539
  ///
2540
  /// Parameter lists with trailing commas are formatted differently from
2541
  /// regular parameter lists. They are treated more like collection literals.
2542
  ///
2543
  /// We don't reuse [_visitCollectionLiteral] here because there are enough
2544
  /// weird differences around optional parameters that it's easiest just to
2545
  /// give them their own method.
25461
  void _visitTrailingCommaParameterList(FormalParameterList parameters) {
2547
    // Can't have a trailing comma if there are no parameters.
25482
    assert(parameters.parameters.isNotEmpty);
2549
25503
    _metadataRules.add(new MetadataRule());
2551
2552
    // Always split the parameters.
25533
    builder.startRule(new Rule.hard());
2554
25552
    token(parameters.leftParenthesis);
2556
2557
    // Find the parameter immediately preceding the optional parameters (if
2558
    // there are any).
2559
    FormalParameter lastRequired;
25604
    for (var i = 0; i < parameters.parameters.length; i++) {
25613
      if (parameters.parameters[i] is DefaultFormalParameter) {
25624
        if (i > 0) lastRequired = parameters.parameters[i - 1];
2563
        break;
2564
      }
2565
    }
2566
2567
    // If all parameters are optional, put the "[" or "{" right after "(".
25683
    if (parameters.parameters.first is DefaultFormalParameter) {
25692
      token(parameters.leftDelimiter);
2570
    }
2571
2572
    // Process the parameters as a separate set of chunks.
25733
    builder = builder.startBlock(null);
2574
25752
    for (var parameter in parameters.parameters) {
25761
      visit(parameter);
2577
2578
      // The comma after the parameter.
25794
      if (parameter.endToken.next.type == TokenType.COMMA) {
25803
        token(parameter.endToken.next);
2581
      }
2582
2583
      // If the optional parameters start after this one, put the delimiter
2584
      // at the end of its line.
25851
      if (parameter == lastRequired) {
25861
        space();
25872
        token(parameters.leftDelimiter);
2588
        lastRequired = null;
2589
      }
2590
25911
      newline();
2592
    }
2593
2594
    // Put comments before the closing ")", "]", or "}" inside the block.
2595
    var firstDelimiter =
25962
        parameters.rightDelimiter ?? parameters.rightParenthesis;
25971
    writePrecedingCommentsAndNewlines(firstDelimiter);
25983
    builder = builder.endBlock(null, forceSplit: true);
25992
    builder.endRule();
2600
26012
    _metadataRules.removeLast();
2602
2603
    // Now write the delimiter itself.
26043
    _writeText(firstDelimiter.lexeme, firstDelimiter.offset);
26052
    if (firstDelimiter != parameters.rightParenthesis) {
26062
      token(parameters.rightParenthesis);
2607
    }
2608
  }
2609
2610
  /// Begins writing a formal parameter of any kind.
26112
  void _beginFormalParameter(FormalParameter node) {
26126
    builder.startLazyRule(new Rule(Cost.parameterType));
26134
    builder.nestExpression();
26144
    modifier(node.covariantKeyword);
2615
  }
2616
2617
  /// Ends writing a formal parameter of any kind.
26182
  void _endFormalParameter(FormalParameter node) {
26194
    builder.unnest();
26204
    builder.endRule();
2621
  }
2622
2623
  /// Writes a `Function` function type.
2624
  ///
2625
  /// Used also by a fix, so there may not be a [functionKeyword].
2626
  /// In that case [functionKeywordPosition] should be the source position
2627
  /// used for the inserted "Function" text.
26282
  void _visitGenericFunctionType(
2629
      AstNode returnType,
2630
      Token functionKeyword,
2631
      int functionKeywordPosition,
2632
      TypeParameterList typeParameters,
2633
      FormalParameterList parameters) {
26344
    builder.startLazyRule();
26354
    builder.nestExpression();
2636
26374
    visit(returnType, after: split);
2638
    if (functionKeyword != null) {
26392
      token(functionKeyword);
2640
    } else {
26411
      _writeText("Function", functionKeywordPosition);
2642
    }
2643
26444
    builder.unnest();
26454
    builder.endRule();
26462
    _visitParameterSignature(typeParameters, parameters);
2647
  }
2648
2649
  /// Writes the header of a new-style typedef.
2650
  ///
2651
  /// Also used by a fix so there may not be an [equals] token.
2652
  /// If [equals] is `null`, then [equalsPosition] must be a
2653
  /// position to use for the inserted text "=".
26542
  void _visitGenericTypeAliasHeader(Token typedefKeyword, AstNode name,
2655
      AstNode typeParameters, Token equals, int equalsPosition) {
26562
    token(typedefKeyword);
26572
    space();
2658
2659
    // If the typedef's type parameters split, split after the "=" too,
2660
    // mainly to ensure the function's type parameters and parameters get
2661
    // end up on successive lines with the same indentation.
26624
    builder.startRule();
2663
26642
    visit(name);
2665
26662
    visit(typeParameters);
26672
    split();
2668
2669
    if (equals != null) {
26702
      token(equals);
2671
    } else {
26721
      _writeText("=", equalsPosition);
2673
    }
2674
26754
    builder.endRule();
2676
  }
2677
2678
  /// Gets the cost to split at an assignment (or `:` in the case of a named
2679
  /// default value) with the given [rightHandSide].
2680
  ///
2681
  /// "Block-like" expressions (collections and cascades) bind a bit tighter
2682
  /// because it looks better to have code like:
2683
  ///
2684
  ///     var list = [
2685
  ///       element,
2686
  ///       element,
2687
  ///       element
2688
  ///     ];
2689
  ///
2690
  ///     var builder = new SomeBuilderClass()
2691
  ///       ..method()
2692
  ///       ..method();
2693
  ///
2694
  /// over:
2695
  ///
2696
  ///     var list =
2697
  ///         [element, element, element];
2698
  ///
2699
  ///     var builder =
2700
  ///         new SomeBuilderClass()..method()..method();
27012
  int _assignmentCost(Expression rightHandSide) {
27022
    if (rightHandSide is ListLiteral) return Cost.assignBlock;
27032
    if (rightHandSide is MapLiteral) return Cost.assignBlock;
27042
    if (rightHandSide is CascadeExpression) return Cost.assignBlock;
2705
2706
    return Cost.assign;
2707
  }
2708
2709
  /// Returns `true` if the collection withs [elements] delimited by
2710
  /// [rightBracket] contains any line comments.
2711
  ///
2712
  /// This only looks for comments at the element boundary. Comments within an
2713
  /// element are ignored.
27142
  bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) {
27152
    hasLineCommentBefore(token) {
27162
      var comment = token.precedingComments;
27171
      for (; comment != null; comment = comment.next) {
27182
        if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true;
2719
      }
2720
2721
      return false;
2722
    }
2723
2724
    // Look before each element.
27254
    for (var element in elements) {
27264
      if (hasLineCommentBefore(element.beginToken)) return true;
2727
    }
2728
2729
    // Look before the closing bracket.
27302
    return hasLineCommentBefore(rightBracket);
2731
  }
2732
2733
  /// Begins writing a literal body: a collection or block-bodied function
2734
  /// expression.
2735
  ///
2736
  /// Writes the delimiter and then creates the [Rule] that handles splitting
2737
  /// the body.
27382
  void _startLiteralBody(Token leftBracket) {
27392
    token(leftBracket);
2740
2741
    // See if this literal is associated with an argument list that wants to
2742
    // handle splitting and indenting it. If not, we'll use a default rule.
2743
    var rule;
2744
    var argumentChunk;
27454
    if (_blockArgumentLists.containsKey(leftBracket)) {
27462
      var argumentList = _blockArgumentLists[leftBracket];
27471
      rule = argumentList.blockRule;
27481
      argumentChunk = argumentList.previousSplit;
2749
    }
2750
2751
    // Create a rule for whether or not to split the block contents.
27524
    builder.startRule(rule);
2753
2754
    // Process the collection contents as a separate set of chunks.
27556
    builder = builder.startBlock(argumentChunk);
2756
  }
2757
2758
  /// Ends the literal body started by a call to [_startLiteralBody()].
2759
  ///
2760
  /// If [forceSplit] is `true`, forces the body to split. If [ignoredRule] is
2761
  /// given, ignores that rule inside the body when determining if it should
2762
  /// split.
27632
  void _endLiteralBody(Token rightBracket,
2764
      {Rule ignoredRule, bool forceSplit}) {
2765
    if (forceSplit == null) forceSplit = false;
2766
2767
    // Put comments before the closing delimiter inside the block.
27682
    var hasLeadingNewline = writePrecedingCommentsAndNewlines(rightBracket);
2769
27706
    builder = builder.endBlock(ignoredRule,
2771
        forceSplit: hasLeadingNewline || forceSplit);
2772
27734
    builder.endRule();
2774
2775
    // Now write the delimiter itself.
27766
    _writeText(rightBracket.lexeme, rightBracket.offset);
2777
  }
2778
2779
  /// Visits a list of configurations in an import or export directive.
27801
  void _visitConfigurations(NodeList<Configuration> configurations) {
27811
    if (configurations.isEmpty) return;
2782
27832
    builder.startRule();
2784
27852
    for (var configuration in configurations) {
27861
      split();
27871
      visit(configuration);
2788
    }
2789
27902
    builder.endRule();
2791
  }
2792
2793
  /// Visits a "combinator".
2794
  ///
2795
  /// This is a [keyword] followed by a list of [nodes], with specific line
2796
  /// splitting rules. As the name implies, this is used for [HideCombinator]
2797
  /// and [ShowCombinator], but it also used for "with" and "implements"
2798
  /// clauses in class declarations, which are formatted the same way.
2799
  ///
2800
  /// This assumes the current rule is a [CombinatorRule].
28011
  void _visitCombinator(Token keyword, Iterable<AstNode> nodes) {
2802
    // Allow splitting before the keyword.
28032
    var rule = builder.rule as CombinatorRule;
28042
    rule.addCombinator(split());
2805
28062
    builder.nestExpression();
28071
    token(keyword);
2808
28092
    rule.addName(split());
28104
    visitCommaSeparatedNodes(nodes, between: () => rule.addName(split()));
2811
28122
    builder.unnest();
2813
  }
2814
2815
  /// If [keyword] is `const`, begins a new constant context.
28162
  void _startPossibleConstContext(Token keyword) {
28174
    if (keyword != null && keyword.keyword == Keyword.CONST) {
28184
      _constNesting++;
2819
    }
2820
  }
2821
2822
  /// If [keyword] is `const`, ends the current outermost constant context.
28232
  void _endPossibleConstContext(Token keyword) {
28244
    if (keyword != null && keyword.keyword == Keyword.CONST) {
28254
      _constNesting--;
2826
    }
2827
  }
2828
2829
  /// Writes the simple statement or semicolon-delimited top-level declaration.
2830
  ///
2831
  /// Handles nesting if a line break occurs in the statement and writes the
2832
  /// terminating semicolon. Invokes [body] which should write statement itself.
28332
  void _simpleStatement(AstNode node, body()) {
28344
    builder.nestExpression();
28352
    body();
2836
2837
    // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base
2838
    // type?
28394
    token((node as dynamic).semicolon);
28404
    builder.unnest();
2841
  }
2842
2843
  /// Marks the block that starts with [token] as being controlled by
2844
  /// [argumentList].
2845
  ///
2846
  /// When the block is visited, [argumentList] will determine the
2847
  /// indentation and splitting rule for the block.
28481
  void beforeBlock(Token token, ArgumentSublist argumentList) {
28492
    _blockArgumentLists[token] = argumentList;
2850
  }
2851
2852
  /// Writes the beginning of a brace-delimited body and handles indenting and
2853
  /// starting the rule used to split the contents.
28542
  void _beginBody(Token leftBracket, {bool space: false}) {
28552
    token(leftBracket);
2856
2857
    // Indent the body.
28584
    builder.indent();
2859
2860
    // Split after the bracket.
28614
    builder.startRule();
28624
    builder.split(isDouble: false, nest: false, space: space);
2863
  }
2864
2865
  /// Finishes the body started by a call to [_beginBody].
28662
  void _endBody(Token rightBracket, {bool space: false}) {
28674
    token(rightBracket, before: () {
2868
      // Split before the closing bracket character.
28694
      builder.unindent();
28704
      builder.split(nest: false, space: space);
2871
    });
2872
28734
    builder.endRule();
2874
  }
2875
2876
  /// Returns `true` if [node] is immediately contained within an anonymous
2877
  /// [FunctionExpression].
28783
  bool _isInLambda(AstNode node) =>
28796
      node.parent is FunctionExpression &&
28809
      node.parent.parent is! FunctionDeclaration;
2881
2882
  /// Writes the string literal [string] to the output.
2883
  ///
2884
  /// Splits multiline strings into separate chunks so that the line splitter
2885
  /// can handle them correctly.
28863
  void _writeStringLiteral(Token string) {
2887
    // Since we output the string literal manually, ensure any preceding
2888
    // comments are written first.
28893
    writePrecedingCommentsAndNewlines(string);
2890
2891
    // Split each line of a multiline string into separate chunks.
289212
    var lines = string.lexeme.split(_formatter.lineEnding);
28933
    var offset = string.offset;
2894
28956
    _writeText(lines.first, offset);
28969
    offset += lines.first.length;
2897
28984
    for (var line in lines.skip(1)) {
28992
      builder.writeWhitespace(Whitespace.newlineFlushLeft);
29001
      offset++;
29011
      _writeText(line, offset);
29022
      offset += line.length;
2903
    }
2904
  }
2905
2906
  /// Emit the given [modifier] if it's non null, followed by non-breaking
2907
  /// whitespace.
29083
  void modifier(Token modifier) {
29096
    token(modifier, after: space);
2910
  }
2911
2912
  /// Emit a non-breaking space.
29133
  void space() {
29146
    builder.writeWhitespace(Whitespace.space);
2915
  }
2916
2917
  /// Emit a single mandatory newline.
29182
  void newline() {
29194
    builder.writeWhitespace(Whitespace.newline);
2920
  }
2921
2922
  /// Emit a two mandatory newlines.
29233
  void twoNewlines() {
29246
    builder.writeWhitespace(Whitespace.twoNewlines);
2925
  }
2926
2927
  /// Allow either a single split or newline to be emitted before the next
2928
  /// non-whitespace token based on whether a newline exists in the source
2929
  /// between the last token and the next one.
29301
  void splitOrNewline() {
29312
    builder.writeWhitespace(Whitespace.splitOrNewline);
2932
  }
2933
2934
  /// Allow either a single split or newline to be emitted before the next
2935
  /// non-whitespace token based on whether a newline exists in the source
2936
  /// between the last token and the next one.
29371
  void splitOrTwoNewlines() {
29382
    builder.writeWhitespace(Whitespace.splitOrTwoNewlines);
2939
  }
2940
2941
  /// Allow either one or two newlines to be emitted before the next
2942
  /// non-whitespace token based on whether more than one newline exists in the
2943
  /// source between the last token and the next one.
29442
  void oneOrTwoNewlines() {
29454
    builder.writeWhitespace(Whitespace.oneOrTwoNewlines);
2946
  }
2947
2948
  /// Writes a single space split owned by the current rule.
2949
  ///
2950
  /// Returns the chunk the split was applied to.
29519
  Chunk split() => builder.split(space: true);
2952
2953
  /// Writes a zero-space split owned by the current rule.
2954
  ///
2955
  /// Returns the chunk the split was applied to.
29566
  Chunk zeroSplit() => builder.split();
2957
2958
  /// Writes a single space split with its own rule.
29593
  Rule soloSplit([int cost]) {
29603
    var rule = new Rule(cost);
29616
    builder.startRule(rule);
29623
    split();
29636
    builder.endRule();
2964
    return rule;
2965
  }
2966
2967
  /// Writes a zero-space split with its own rule.
29682
  void soloZeroSplit() {
29694
    builder.startRule();
29704
    builder.split();
29714
    builder.endRule();
2972
  }
2973
2974
  /// Emit [token], along with any comments and formatted whitespace that comes
2975
  /// before it.
2976
  ///
2977
  /// Does nothing if [token] is `null`. If [before] is given, it will be
2978
  /// executed before the token is outout. Likewise, [after] will be called
2979
  /// after the token is output.
29803
  void token(Token token, {before(), after()}) {
2981
    if (token == null) return;
2982
29833
    writePrecedingCommentsAndNewlines(token);
2984
29852
    if (before != null) before();
2986
29879
    _writeText(token.lexeme, token.offset);
2988
29892
    if (after != null) after();
2990
  }
2991
2992
  /// Writes all formatted whitespace and comments that appear before [token].
29933
  bool writePrecedingCommentsAndNewlines(Token token) {
29943
    var comment = token.precedingComments;
2995
2996
    // For performance, avoid calculating newlines between tokens unless
2997
    // actually needed.
2998
    if (comment == null) {
29996
      if (builder.needsToPreserveNewlines) {
300012
        builder.preserveNewlines(_startLine(token) - _endLine(token.previous));
3001
      }
3002
3003
      return false;
3004
    }
3005
30064
    var previousLine = _endLine(token.previous);
30072
    var tokenLine = _startLine(token);
3008
3009
    // Edge case: The analyzer includes the "\n" in the script tag's lexeme,
3010
    // which confuses some of these calculations. We don't want to allow a
3011
    // blank line between the script tag and a following comment anyway, so
3012
    // just override the script tag's line.
30136
    if (token.previous.type == TokenType.SCRIPT_TAG) previousLine = tokenLine;
3014
30152
    var comments = <SourceComment>[];
3016
    while (comment != null) {
30172
      var commentLine = _startLine(comment);
3018
3019
      // Don't preserve newlines at the top of the file.
30204
      if (comment == token.precedingComments &&
30216
          token.previous.type == TokenType.EOF) {
3022
        previousLine = commentLine;
3023
      }
3024
30254
      var text = comment.lexeme.trim();
30262
      var linesBefore = commentLine - previousLine;
30274
      var flushLeft = _startColumn(comment) == 1;
3028
30293
      if (text.startsWith("///") && !text.startsWith("////")) {
3030
        // Line doc comments are always indented even if they were flush left.
3031
        flushLeft = false;
3032
3033
        // Always add a blank line (if possible) before a doc comment block.
30342
        if (comment == token.precedingComments) linesBefore = 2;
3035
      }
3036
30372
      var sourceComment = new SourceComment(text, linesBefore,
30384
          isLineComment: comment.type == TokenType.SINGLE_LINE_COMMENT,
3039
          flushLeft: flushLeft);
3040
3041
      // If this comment contains either of the selection endpoints, mark them
3042
      // in the comment.
30436
      var start = _getSelectionStartWithin(comment.offset, comment.length);
30440
      if (start != null) sourceComment.startSelection(start);
3045
30466
      var end = _getSelectionEndWithin(comment.offset, comment.length);
30470
      if (end != null) sourceComment.endSelection(end);
3048
30492
      comments.add(sourceComment);
3050
30512
      previousLine = _endLine(comment);
30522
      comment = comment.next;
3053
    }
3054
30558
    builder.writeComments(comments, tokenLine - previousLine, token.lexeme);
3056
3057
    // TODO(rnystrom): This is wrong. Consider:
3058
    //
3059
    // [/* inline comment */
3060
    //     // line comment
3061
    //     element];
30626
    return comments.first.linesBefore > 0;
3063
  }
3064
3065
  /// Write [text] to the current chunk, given that it starts at [offset] in
3066
  /// the original source.
3067
  ///
3068
  /// Also outputs the selection endpoints if needed.
30693
  void _writeText(String text, int offset) {
30706
    builder.write(text);
3071
3072
    // If this text contains either of the selection endpoints, mark them in
3073
    // the chunk.
30746
    var start = _getSelectionStartWithin(offset, text.length);
3075
    if (start != null) {
30764
      builder.startSelectionFromEnd(text.length - start);
3077
    }
3078
30796
    var end = _getSelectionEndWithin(offset, text.length);
3080
    if (end != null) {
30814
      builder.endSelectionFromEnd(text.length - end);
3082
    }
3083
  }
3084
3085
  /// Returns the number of characters past [offset] in the source where the
3086
  /// selection start appears if it appears before `offset + length`.
3087
  ///
3088
  /// Returns `null` if the selection start has already been processed or is
3089
  /// not within that range.
30903
  int _getSelectionStartWithin(int offset, int length) {
3091
    // If there is no selection, do nothing.
30926
    if (_source.selectionStart == null) return null;
3093
3094
    // If we've already passed it, don't consider it again.
30951
    if (_passedSelectionStart) return null;
3096
30973
    var start = _source.selectionStart - offset;
3098
3099
    // If it started in whitespace before this text, push it forward to the
3100
    // beginning of the non-whitespace text.
31011
    if (start < 0) start = 0;
3102
3103
    // If we haven't reached it yet, don't consider it.
31041
    if (start >= length) return null;
3105
3106
    // We found it.
31071
    _passedSelectionStart = true;
3108
3109
    return start;
3110
  }
3111
3112
  /// Returns the number of characters past [offset] in the source where the
3113
  /// selection endpoint appears if it appears before `offset + length`.
3114
  ///
3115
  /// Returns `null` if the selection endpoint has already been processed or is
3116
  /// not within that range.
31173
  int _getSelectionEndWithin(int offset, int length) {
3118
    // If there is no selection, do nothing.
31196
    if (_source.selectionLength == null) return null;
3120
3121
    // If we've already passed it, don't consider it again.
31221
    if (_passedSelectionEnd) return null;
3123
31242
    var end = _findSelectionEnd() - offset;
3125
3126
    // If it started in whitespace before this text, push it forward to the
3127
    // beginning of the non-whitespace text.
31281
    if (end < 0) end = 0;
3129
3130
    // If we haven't reached it yet, don't consider it.
31311
    if (end > length) return null;
3132
31335
    if (end == length && _findSelectionEnd() == _source.selectionStart) {
3134
      return null;
3135
    }
3136
3137
    // We found it.
31381
    _passedSelectionEnd = true;
3139
3140
    return end;
3141
  }
3142
3143
  /// Calculates the character offset in the source text of the end of the
3144
  /// selection.
3145
  ///
3146
  /// Removes any trailing whitespace from the selection.
31471
  int _findSelectionEnd() {
31482
    if (_selectionEnd != null) return _selectionEnd;
3149
31506
    _selectionEnd = _source.selectionStart + _source.selectionLength;
3151
3152
    // If the selection bumps to the end of the source, pin it there.
31535
    if (_selectionEnd == _source.text.length) return _selectionEnd;
3154
3155
    // Trim off any trailing whitespace. We want the selection to "rubberband"
3156
    // around the selected non-whitespace tokens since the whitespace will
3157
    // be munged by the formatter itself.
31584
    while (_selectionEnd > _source.selectionStart) {
3159
      // Stop if we hit anything other than space, tab, newline or carriage
3160
      // return.
31615
      var char = _source.text.codeUnitAt(_selectionEnd - 1);
31624
      if (char != 0x20 && char != 0x09 && char != 0x0a && char != 0x0d) {
3163
        break;
3164
      }
3165
31662
      _selectionEnd--;
3167
    }
3168
31691
    return _selectionEnd;
3170
  }
3171
3172
  /// Gets the 1-based line number that the beginning of [token] lies on.
317310
  int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
3174
3175
  /// Gets the 1-based line number that the end of [token] lies on.
317610
  int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
3177
3178
  /// Gets the 1-based column number that the beginning of [token] lies on.
31792
  int _startColumn(Token token) =>
31808
      _lineInfo.getLocation(token.offset).columnNumber;
3181
}