Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1533)

Side by Side Diff: packages/dart_style/lib/src/source_visitor.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dart_style.src.source_visitor; 5 library dart_style.src.source_visitor;
6 6
7 import 'package:analyzer/analyzer.dart'; 7 import 'package:analyzer/analyzer.dart';
8 import 'package:analyzer/src/generated/scanner.dart'; 8 import 'package:analyzer/src/generated/scanner.dart';
9 import 'package:analyzer/src/generated/source.dart'; 9 import 'package:analyzer/src/generated/source.dart';
10 10
11 import 'argument_list_visitor.dart'; 11 import 'argument_list_visitor.dart';
12 import 'call_chain_visitor.dart'; 12 import 'call_chain_visitor.dart';
13 import 'chunk.dart'; 13 import 'chunk.dart';
14 import 'chunk_builder.dart'; 14 import 'chunk_builder.dart';
15 import 'dart_formatter.dart'; 15 import 'dart_formatter.dart';
16 import 'rule/argument.dart'; 16 import 'rule/argument.dart';
17 import 'rule/combinator.dart'; 17 import 'rule/combinator.dart';
18 import 'rule/metadata.dart';
18 import 'rule/rule.dart'; 19 import 'rule/rule.dart';
19 import 'rule/type_argument.dart'; 20 import 'rule/type_argument.dart';
20 import 'source_code.dart'; 21 import 'source_code.dart';
21 import 'whitespace.dart'; 22 import 'whitespace.dart';
22 23
23 /// Visits every token of the AST and passes all of the relevant bits to a 24 /// Visits every token of the AST and passes all of the relevant bits to a
24 /// [ChunkBuilder]. 25 /// [ChunkBuilder].
25 class SourceVisitor implements AstVisitor { 26 class SourceVisitor implements AstVisitor {
26 /// The builder for the block that is currently being visited. 27 /// The builder for the block that is currently being visited.
27 ChunkBuilder builder; 28 ChunkBuilder builder;
(...skipping 12 matching lines...) Expand all
40 41
41 /// `true` if the visitor has written past the end of the selection in the 42 /// `true` if the visitor has written past the end of the selection in the
42 /// original source text. 43 /// original source text.
43 bool _passedSelectionEnd = false; 44 bool _passedSelectionEnd = false;
44 45
45 /// The character offset of the end of the selection, if there is a selection. 46 /// The character offset of the end of the selection, if there is a selection.
46 /// 47 ///
47 /// This is calculated and cached by [_findSelectionEnd]. 48 /// This is calculated and cached by [_findSelectionEnd].
48 int _selectionEnd; 49 int _selectionEnd;
49 50
50 /// The rule that should be used for the contents of a literal body that are
51 /// about to be written.
52 ///
53 /// This is set by [visitArgumentList] to ensure that all block arguments
54 /// share a rule.
55 ///
56 /// If `null`, a literal body creates its own rule.
57 Rule _nextLiteralBodyRule;
58
59 /// A stack that tracks forcing nested collections to split. 51 /// A stack that tracks forcing nested collections to split.
60 /// 52 ///
61 /// Each entry corresponds to a collection currently being visited and the 53 /// Each entry corresponds to a collection currently being visited and the
62 /// value is whether or not it should be forced to split. Every time a 54 /// value is whether or not it should be forced to split. Every time a
63 /// collection is entered, it sets all of the existing elements to `true` 55 /// collection is entered, it sets all of the existing elements to `true`
64 /// then it pushes `false` for itself. 56 /// then it pushes `false` for itself.
65 /// 57 ///
66 /// When done visiting the elements, it removes its value. If it was set to 58 /// When done visiting the elements, it removes its value. If it was set to
67 /// `true`, we know we visited a nested collection so we force this one to 59 /// `true`, we know we visited a nested collection so we force this one to
68 /// split. 60 /// split.
69 final List<bool> _collectionSplits = []; 61 final List<bool> _collectionSplits = [];
70 62
63 /// The stack of current rules for handling parameter metadata.
64 ///
65 /// Each time a parameter (or type parameter) list is begun, a single rule
66 /// for all of the metadata annotations on parameters in that list is pushed
67 /// onto this stack. We reuse this rule for all annotations so that they split
68 /// in unison.
69 final List<MetadataRule> _metadataRules = [];
70
71 /// The mapping for collection literals that are managed by the argument
72 /// list that contains them.
73 ///
74 /// When a collection literal appears inside an [ArgumentSublist], the
75 /// argument list provides a rule for the body to split to ensure that all
76 /// collections split in unison. It also tracks the chunk before the
77 /// argument that determines whether or not the collection body is indented
78 /// like an expression or a statement.
79 ///
80 /// Before a collection literal argument is visited, [ArgumentSublist] binds
81 /// itself to the left bracket token of each collection literal it controls.
82 /// When we later visit that literal, we use the token to find that
83 /// association.
84 final Map<Token, ArgumentSublist> _collectionArgumentLists = {};
85
71 /// Initialize a newly created visitor to write source code representing 86 /// Initialize a newly created visitor to write source code representing
72 /// the visited nodes to the given [writer]. 87 /// the visited nodes to the given [writer].
73 SourceVisitor(this._formatter, this._lineInfo, this._source) { 88 SourceVisitor(this._formatter, this._lineInfo, this._source) {
74 builder = new ChunkBuilder(_formatter, _source); 89 builder = new ChunkBuilder(_formatter, _source);
75 } 90 }
76 91
77 /// Runs the visitor on [node], formatting its contents. 92 /// Runs the visitor on [node], formatting its contents.
78 /// 93 ///
79 /// Returns a [SourceCode] containing the resulting formatted source and 94 /// Returns a [SourceCode] containing the resulting formatted source and
80 /// updated selection, if any. 95 /// updated selection, if any.
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 } 178 }
164 179
165 visitAwaitExpression(AwaitExpression node) { 180 visitAwaitExpression(AwaitExpression node) {
166 token(node.awaitKeyword); 181 token(node.awaitKeyword);
167 space(); 182 space();
168 visit(node.expression); 183 visit(node.expression);
169 } 184 }
170 185
171 visitBinaryExpression(BinaryExpression node) { 186 visitBinaryExpression(BinaryExpression node) {
172 builder.startSpan(); 187 builder.startSpan();
173 builder.nestExpression(); 188
189 // If a binary operator sequence appears immediately after a `=>`, don't
190 // add an extra level of nesting. Instead, let the subsequent operands line
191 // up with the first, as in:
192 //
193 // method() =>
194 // argument &&
195 // argument &&
196 // argument;
197 var isArrowBody = node.parent is ExpressionFunctionBody;
198 if (!isArrowBody) builder.nestExpression();
174 199
175 // Start lazily so we don't force the operator to split if a line comment 200 // Start lazily so we don't force the operator to split if a line comment
176 // appears before the first operand. 201 // appears before the first operand.
177 builder.startLazyRule(); 202 builder.startLazyRule();
178 203
179 // Flatten out a tree/chain of the same precedence. If we split on this 204 // Flatten out a tree/chain of the same precedence. If we split on this
180 // precedence level, we will break all of them. 205 // precedence level, we will break all of them.
181 var precedence = node.operator.type.precedence; 206 var precedence = node.operator.type.precedence;
182 207
183 traverse(Expression e) { 208 traverse(Expression e) {
(...skipping 11 matching lines...) Expand all
195 } 220 }
196 221
197 // Blocks as operands to infix operators should always nest like regular 222 // Blocks as operands to infix operators should always nest like regular
198 // operands. (Granted, this case is exceedingly rare in real code.) 223 // operands. (Granted, this case is exceedingly rare in real code.)
199 builder.startBlockArgumentNesting(); 224 builder.startBlockArgumentNesting();
200 225
201 traverse(node); 226 traverse(node);
202 227
203 builder.endBlockArgumentNesting(); 228 builder.endBlockArgumentNesting();
204 229
205 builder.unnest(); 230 if (!isArrowBody) builder.unnest();
206 builder.endSpan(); 231 builder.endSpan();
207 builder.endRule(); 232 builder.endRule();
208 } 233 }
209 234
210 visitBlock(Block node) { 235 visitBlock(Block node) {
236 // Don't allow splitting in an empty block.
237 if (node.statements.isEmpty &&
238 node.rightBracket.precedingComments == null) {
239 token(node.leftBracket);
240 token(node.rightBracket);
241 return;
242 }
243
211 // For a block that is not a function body, just bump the indentation and 244 // For a block that is not a function body, just bump the indentation and
212 // keep it in the current block. 245 // keep it in the current block.
213 if (node.parent is! BlockFunctionBody) { 246 if (node.parent is! BlockFunctionBody) {
214 _writeBody(node.leftBracket, node.rightBracket, body: () { 247 _writeBody(node.leftBracket, node.rightBracket, body: () {
215 visitNodes(node.statements, between: oneOrTwoNewlines, after: newline); 248 visitNodes(node.statements, between: oneOrTwoNewlines, after: newline);
216 }); 249 });
217 return; 250 return;
218 } 251 }
219 252
220 _startLiteralBody(node.leftBracket); 253 _startLiteralBody(node.leftBracket);
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 builder.startBlockArgumentNesting(); 310 builder.startBlockArgumentNesting();
278 311
279 // If the cascade sections have consistent names they can be broken 312 // If the cascade sections have consistent names they can be broken
280 // normally otherwise they always get their own line. 313 // normally otherwise they always get their own line.
281 if (_allowInlineCascade(node.cascadeSections)) { 314 if (_allowInlineCascade(node.cascadeSections)) {
282 builder.startRule(); 315 builder.startRule();
283 zeroSplit(); 316 zeroSplit();
284 visitNodes(node.cascadeSections, between: zeroSplit); 317 visitNodes(node.cascadeSections, between: zeroSplit);
285 builder.endRule(); 318 builder.endRule();
286 } else { 319 } else {
287 builder.startRule(new HardSplitRule()); 320 builder.startRule(new Rule.hard());
288 zeroSplit(); 321 zeroSplit();
289 visitNodes(node.cascadeSections, between: zeroSplit); 322 visitNodes(node.cascadeSections, between: zeroSplit);
290 builder.endRule(); 323 builder.endRule();
291 } 324 }
292 325
293 builder.endBlockArgumentNesting(); 326 builder.endBlockArgumentNesting();
294 builder.unnest(); 327 builder.unnest();
295 328
296 if (node.target is MethodInvocation) builder.unnest(); 329 if (node.target is MethodInvocation) builder.unnest();
297 } 330 }
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
508 modifier(node.factoryKeyword); 541 modifier(node.factoryKeyword);
509 visit(node.returnType); 542 visit(node.returnType);
510 token(node.period); 543 token(node.period);
511 visit(node.name); 544 visit(node.name);
512 545
513 // Make the rule for the ":" span both the preceding parameter list and 546 // Make the rule for the ":" span both the preceding parameter list and
514 // the entire initialization list. This ensures that we split before the 547 // the entire initialization list. This ensures that we split before the
515 // ":" if the parameters and initialization list don't all fit on one line. 548 // ":" if the parameters and initialization list don't all fit on one line.
516 builder.startRule(); 549 builder.startRule();
517 550
551 // If the redirecting constructor happens to wrap, we want to make sure
552 // the parameter list gets more deeply indented.
553 if (node.redirectedConstructor != null) builder.nestExpression();
554
518 _visitBody(node.parameters, node.body, () { 555 _visitBody(node.parameters, node.body, () {
519 // Check for redirects or initializer lists. 556 // Check for redirects or initializer lists.
520 if (node.redirectedConstructor != null) { 557 if (node.redirectedConstructor != null) {
521 _visitConstructorRedirects(node); 558 _visitConstructorRedirects(node);
559 builder.unnest();
522 } else if (node.initializers.isNotEmpty) { 560 } else if (node.initializers.isNotEmpty) {
523 _visitConstructorInitializers(node); 561 _visitConstructorInitializers(node);
524 } 562 }
525 }); 563 });
526 } 564 }
527 565
528 void _visitConstructorRedirects(ConstructorDeclaration node) { 566 void _visitConstructorRedirects(ConstructorDeclaration node) {
529 token(node.separator /* = */, before: space, after: space); 567 token(node.separator /* = */, before: space);
568 soloSplit();
530 visitCommaSeparatedNodes(node.initializers); 569 visitCommaSeparatedNodes(node.initializers);
531 visit(node.redirectedConstructor); 570 visit(node.redirectedConstructor);
532 } 571 }
533 572
534 void _visitConstructorInitializers(ConstructorDeclaration node) { 573 void _visitConstructorInitializers(ConstructorDeclaration node) {
535 // Shift the ":" forward. 574 // Shift the ":" forward.
536 builder.indent(Indent.constructorInitializer); 575 builder.indent(Indent.constructorInitializer);
537 576
538 split(); 577 split();
539 token(node.separator); // ":". 578 token(node.separator); // ":".
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
593 visitDefaultFormalParameter(DefaultFormalParameter node) { 632 visitDefaultFormalParameter(DefaultFormalParameter node) {
594 visit(node.parameter); 633 visit(node.parameter);
595 if (node.separator != null) { 634 if (node.separator != null) {
596 builder.startSpan(); 635 builder.startSpan();
597 builder.nestExpression(); 636 builder.nestExpression();
598 637
599 // The '=' separator is preceded by a space, ":" is not. 638 // The '=' separator is preceded by a space, ":" is not.
600 if (node.separator.type == TokenType.EQ) space(); 639 if (node.separator.type == TokenType.EQ) space();
601 token(node.separator); 640 token(node.separator);
602 641
603 soloSplit(Cost.assignment); 642 soloSplit(_assignmentCost(node.defaultValue));
604 visit(node.defaultValue); 643 visit(node.defaultValue);
605 644
606 builder.unnest(); 645 builder.unnest();
607 builder.endSpan(); 646 builder.endSpan();
608 } 647 }
609 } 648 }
610 649
611 visitDoStatement(DoStatement node) { 650 visitDoStatement(DoStatement node) {
612 _simpleStatement(node, () { 651 builder.nestExpression();
613 token(node.doKeyword); 652 token(node.doKeyword);
614 space(); 653 space();
615 visit(node.body); 654 builder.unnest(now: false);
616 space(); 655 visit(node.body);
617 token(node.whileKeyword); 656
618 space(); 657 builder.nestExpression();
619 token(node.leftParenthesis); 658 space();
620 soloZeroSplit(); 659 token(node.whileKeyword);
621 visit(node.condition); 660 space();
622 token(node.rightParenthesis); 661 token(node.leftParenthesis);
623 }); 662 soloZeroSplit();
663 visit(node.condition);
664 token(node.rightParenthesis);
665 token(node.semicolon);
666 builder.unnest();
624 } 667 }
625 668
626 visitDoubleLiteral(DoubleLiteral node) { 669 visitDoubleLiteral(DoubleLiteral node) {
627 token(node.literal); 670 token(node.literal);
628 } 671 }
629 672
630 visitEmptyFunctionBody(EmptyFunctionBody node) { 673 visitEmptyFunctionBody(EmptyFunctionBody node) {
631 token(node.semicolon); 674 token(node.semicolon);
632 } 675 }
633 676
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 718
676 // Try to keep the "(...) => " with the start of the body for anonymous 719 // Try to keep the "(...) => " with the start of the body for anonymous
677 // functions. 720 // functions.
678 if (_isInLambda(node)) builder.startSpan(); 721 if (_isInLambda(node)) builder.startSpan();
679 722
680 token(node.functionDefinition); // "=>". 723 token(node.functionDefinition); // "=>".
681 724
682 // Split after the "=>", using the rule created before the parameters 725 // Split after the "=>", using the rule created before the parameters
683 // by _visitBody(). 726 // by _visitBody().
684 split(); 727 split();
685 builder.endRule(); 728
729 // If the body is a binary operator expression, then we want to force the
730 // split at `=>` if the operators split. See visitBinaryExpression().
731 if (node.expression is! BinaryExpression) builder.endRule();
686 732
687 if (_isInLambda(node)) builder.endSpan(); 733 if (_isInLambda(node)) builder.endSpan();
688 734
689 builder.startBlockArgumentNesting(); 735 builder.startBlockArgumentNesting();
690 builder.startSpan(); 736 builder.startSpan();
691 visit(node.expression); 737 visit(node.expression);
692 builder.endSpan(); 738 builder.endSpan();
693 builder.endBlockArgumentNesting(); 739 builder.endBlockArgumentNesting();
694 740
741 if (node.expression is BinaryExpression) builder.endRule();
742
695 token(node.semicolon); 743 token(node.semicolon);
696 } 744 }
697 745
698 visitExpressionStatement(ExpressionStatement node) { 746 visitExpressionStatement(ExpressionStatement node) {
699 _simpleStatement(node, () { 747 _simpleStatement(node, () {
700 visit(node.expression); 748 visit(node.expression);
701 }); 749 });
702 } 750 }
703 751
704 visitExtendsClause(ExtendsClause node) { 752 visitExtendsClause(ExtendsClause node) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
737 if (node.loopVariable != null) { 785 if (node.loopVariable != null) {
738 visit(node.loopVariable); 786 visit(node.loopVariable);
739 } else { 787 } else {
740 visit(node.identifier); 788 visit(node.identifier);
741 } 789 }
742 soloSplit(); 790 soloSplit();
743 token(node.inKeyword); 791 token(node.inKeyword);
744 space(); 792 space();
745 visit(node.iterable); 793 visit(node.iterable);
746 token(node.rightParenthesis); 794 token(node.rightParenthesis);
747 space(); 795 builder.unnest(now: false);
748 visit(node.body); 796
749 builder.unnest(); 797 _visitLoopBody(node.body);
750 } 798 }
751 799
752 visitFormalParameterList(FormalParameterList node) { 800 visitFormalParameterList(FormalParameterList node) {
753 // Corner case: empty parameter lists. 801 // Corner case: empty parameter lists.
754 if (node.parameters.isEmpty) { 802 if (node.parameters.isEmpty) {
755 token(node.leftParenthesis); 803 token(node.leftParenthesis);
756 804
757 // If there is a comment, do allow splitting before it. 805 // If there is a comment, do allow splitting before it.
758 if (node.rightParenthesis.precedingComments != null) soloZeroSplit(); 806 if (node.rightParenthesis.precedingComments != null) soloZeroSplit();
759 807
760 token(node.rightParenthesis); 808 token(node.rightParenthesis);
761 return; 809 return;
762 } 810 }
763 811
764 var requiredParams = node.parameters 812 var requiredParams = node.parameters
765 .where((param) => param is! DefaultFormalParameter) 813 .where((param) => param is! DefaultFormalParameter)
766 .toList(); 814 .toList();
767 var optionalParams = node.parameters 815 var optionalParams = node.parameters
768 .where((param) => param is DefaultFormalParameter) 816 .where((param) => param is DefaultFormalParameter)
769 .toList(); 817 .toList();
770 818
771 builder.nestExpression(); 819 builder.nestExpression();
772 token(node.leftParenthesis); 820 token(node.leftParenthesis);
773 821
822 _metadataRules.add(new MetadataRule());
823
774 var rule; 824 var rule;
775 if (requiredParams.isNotEmpty) { 825 if (requiredParams.isNotEmpty) {
776 if (requiredParams.length > 1) { 826 if (requiredParams.length > 1) {
777 rule = new MultiplePositionalRule(null, 0, 0); 827 rule = new MultiplePositionalRule(null, 0, 0);
778 } else { 828 } else {
779 rule = new SinglePositionalRule(null); 829 rule = new SinglePositionalRule(null);
780 } 830 }
781 831
832 _metadataRules.last.bindPositionalRule(rule);
833
782 builder.startRule(rule); 834 builder.startRule(rule);
783 if (_isInLambda(node)) { 835 if (_isInLambda(node)) {
784 // Don't allow splitting before the first argument (i.e. right after 836 // Don't allow splitting before the first argument (i.e. right after
785 // the bare "(" in a lambda. Instead, just stuff a null chunk in there 837 // the bare "(" in a lambda. Instead, just stuff a null chunk in there
786 // to avoid confusing the arg rule. 838 // to avoid confusing the arg rule.
787 rule.beforeArgument(null); 839 rule.beforeArgument(null);
788 } else { 840 } else {
789 // Split before the first argument. 841 // Split before the first argument.
790 rule.beforeArgument(zeroSplit()); 842 rule.beforeArgument(zeroSplit());
791 } 843 }
792 844
793 builder.startSpan(); 845 builder.startSpan();
794 846
795 for (var param in requiredParams) { 847 for (var param in requiredParams) {
796 visit(param); 848 visit(param);
797 849
798 // Write the trailing comma. 850 // Write the trailing comma.
799 if (param != node.parameters.last) token(param.endToken.next); 851 if (param != node.parameters.last) token(param.endToken.next);
800 852
801 if (param != requiredParams.last) rule.beforeArgument(split()); 853 if (param != requiredParams.last) rule.beforeArgument(split());
802 } 854 }
803 855
804 builder.endSpan(); 856 builder.endSpan();
805 builder.endRule(); 857 builder.endRule();
806 } 858 }
807 859
808 if (optionalParams.isNotEmpty) { 860 if (optionalParams.isNotEmpty) {
809 var namedRule = new NamedRule(null); 861 var namedRule = new NamedRule(null, 0, 0);
810 if (rule != null) rule.setNamedArgsRule(namedRule); 862 if (rule != null) rule.setNamedArgsRule(namedRule);
811 863
864 _metadataRules.last.bindNamedRule(namedRule);
865
812 builder.startRule(namedRule); 866 builder.startRule(namedRule);
813 867
814 namedRule 868 // Make sure multi-line default values are indented.
815 .beforeArguments(builder.split(space: requiredParams.isNotEmpty)); 869 builder.startBlockArgumentNesting();
870
871 namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty));
816 872
817 // "[" or "{" for optional parameters. 873 // "[" or "{" for optional parameters.
818 token(node.leftDelimiter); 874 token(node.leftDelimiter);
819 875
820 for (var param in optionalParams) { 876 for (var param in optionalParams) {
821 visit(param); 877 visit(param);
822 878
823 // Write the trailing comma. 879 // Write the trailing comma.
824 if (param != node.parameters.last) token(param.endToken.next); 880 if (param != node.parameters.last) token(param.endToken.next);
825 if (param != optionalParams.last) split(); 881 if (param != optionalParams.last) namedRule.beforeArgument(split());
826 } 882 }
827 883
884 builder.endBlockArgumentNesting();
828 builder.endRule(); 885 builder.endRule();
829 886
830 // "]" or "}" for optional parameters. 887 // "]" or "}" for optional parameters.
831 token(node.rightDelimiter); 888 token(node.rightDelimiter);
832 } 889 }
833 890
891 _metadataRules.removeLast();
892
834 token(node.rightParenthesis); 893 token(node.rightParenthesis);
835 builder.unnest(); 894 builder.unnest();
836 } 895 }
837 896
838 visitForStatement(ForStatement node) { 897 visitForStatement(ForStatement node) {
839 builder.nestExpression(); 898 builder.nestExpression();
840 token(node.forKeyword); 899 token(node.forKeyword);
841 space(); 900 space();
842 token(node.leftParenthesis); 901 token(node.leftParenthesis);
843 902
844 builder.startRule(); 903 builder.startRule();
845 904
846 // The initialization clause. 905 // The initialization clause.
847 if (node.initialization != null) { 906 if (node.initialization != null) {
848 visit(node.initialization); 907 visit(node.initialization);
849 } else if (node.variables != null) { 908 } else if (node.variables != null) {
850 // Indent split variables more so they aren't at the same level 909 // Nest split variables more so they aren't at the same level
851 // as the rest of the loop clauses. 910 // as the rest of the loop clauses.
852 builder.indent(Indent.loopVariable); 911 builder.nestExpression();
853 912
854 // Allow the variables to stay unsplit even if the clauses split. 913 // Allow the variables to stay unsplit even if the clauses split.
855 builder.startRule(); 914 builder.startRule();
856 915
857 var declaration = node.variables; 916 var declaration = node.variables;
858 visitDeclarationMetadata(declaration.metadata); 917 visitDeclarationMetadata(declaration.metadata);
859 modifier(declaration.keyword); 918 modifier(declaration.keyword);
860 visit(declaration.type, after: space); 919 visit(declaration.type, after: space);
861 920
862 visitCommaSeparatedNodes(declaration.variables, between: () { 921 visitCommaSeparatedNodes(declaration.variables, between: () {
863 split(); 922 split();
864 }); 923 });
865 924
866 builder.endRule(); 925 builder.endRule();
867 builder.unindent(); 926 builder.unnest();
868 } 927 }
869 928
870 token(node.leftSeparator); 929 token(node.leftSeparator);
871 930
872 // The condition clause. 931 // The condition clause.
873 if (node.condition != null) split(); 932 if (node.condition != null) split();
874 visit(node.condition); 933 visit(node.condition);
875 token(node.rightSeparator); 934 token(node.rightSeparator);
876 935
877 // The update clause. 936 // The update clause.
878 if (node.updaters.isNotEmpty) { 937 if (node.updaters.isNotEmpty) {
879 split(); 938 split();
880 939
881 // Allow the updates to stay unsplit even if the clauses split. 940 // Allow the updates to stay unsplit even if the clauses split.
882 builder.startRule(); 941 builder.startRule();
883 942
884 visitCommaSeparatedNodes(node.updaters, between: split); 943 visitCommaSeparatedNodes(node.updaters, between: split);
885 944
886 builder.endRule(); 945 builder.endRule();
887 } 946 }
888 947
889 token(node.rightParenthesis); 948 token(node.rightParenthesis);
890 builder.endRule(); 949 builder.endRule();
891 builder.unnest(); 950 builder.unnest();
892 951
893 // The body. 952 _visitLoopBody(node.body);
894 if (node.body is! EmptyStatement) space();
895 visit(node.body);
896 } 953 }
897 954
898 visitFunctionDeclaration(FunctionDeclaration node) { 955 visitFunctionDeclaration(FunctionDeclaration node) {
899 visitMemberMetadata(node.metadata); 956 _visitMemberDeclaration(node, node.functionExpression);
900
901 builder.nestExpression();
902 modifier(node.externalKeyword);
903 visit(node.returnType, after: space);
904 modifier(node.propertyKeyword);
905 visit(node.name);
906 visit(node.functionExpression);
907 builder.unnest();
908 } 957 }
909 958
910 visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { 959 visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
911 visit(node.functionDeclaration); 960 visit(node.functionDeclaration);
912 } 961 }
913 962
914 visitFunctionExpression(FunctionExpression node) { 963 visitFunctionExpression(FunctionExpression node) {
915 _visitBody(node.parameters, node.body); 964 _visitBody(node.parameters, node.body);
916 } 965 }
917 966
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
949 _visitCombinator(node.keyword, node.hiddenNames); 998 _visitCombinator(node.keyword, node.hiddenNames);
950 } 999 }
951 1000
952 visitIfStatement(IfStatement node) { 1001 visitIfStatement(IfStatement node) {
953 builder.nestExpression(); 1002 builder.nestExpression();
954 token(node.ifKeyword); 1003 token(node.ifKeyword);
955 space(); 1004 space();
956 token(node.leftParenthesis); 1005 token(node.leftParenthesis);
957 visit(node.condition); 1006 visit(node.condition);
958 token(node.rightParenthesis); 1007 token(node.rightParenthesis);
1008 builder.unnest(now: false);
959 1009
960 space(); 1010 visitClause(Statement clause) {
961 visit(node.thenStatement); 1011 if (clause is Block || clause is IfStatement) {
962 builder.unnest(); 1012 space();
1013 visit(clause);
1014 } else {
1015 // Allow splitting in an expression-bodied if even though it's against
1016 // the style guide. Since we can't fix the code itself to follow the
1017 // style guide, we should at least format it as well as we can.
1018 builder.nestExpression(indent: 2, now: true);
1019 builder.startRule();
1020
1021 // If there is an else clause, always split before both the then and
1022 // else statements.
1023 if (node.elseStatement != null) {
1024 builder.writeWhitespace(Whitespace.nestedNewline);
1025 } else {
1026 split();
1027 }
1028
1029 visit(clause);
1030
1031 builder.endRule();
1032 builder.unnest();
1033 }
1034 }
1035
1036 visitClause(node.thenStatement);
963 1037
964 if (node.elseStatement != null) { 1038 if (node.elseStatement != null) {
965 if (node.thenStatement is Block) { 1039 if (node.thenStatement is Block) {
966 space(); 1040 space();
967 } else { 1041 } else {
968 // Corner case where an else follows a single-statement then clause. 1042 // Corner case where an else follows a single-statement then clause.
969 // This is against the style guide, but we still need to handle it. If 1043 // This is against the style guide, but we still need to handle it. If
970 // it happens, put the else on the next line. 1044 // it happens, put the else on the next line.
971 newline(); 1045 newline();
972 } 1046 }
973 1047
974 token(node.elseKeyword); 1048 token(node.elseKeyword);
975 space(); 1049 visitClause(node.elseStatement);
976 visit(node.elseStatement);
977 } 1050 }
978 } 1051 }
979 1052
980 visitImplementsClause(ImplementsClause node) { 1053 visitImplementsClause(ImplementsClause node) {
981 _visitCombinator(node.implementsKeyword, node.interfaces); 1054 _visitCombinator(node.implementsKeyword, node.interfaces);
982 } 1055 }
983 1056
984 visitImportDirective(ImportDirective node) { 1057 visitImportDirective(ImportDirective node) {
985 visitDeclarationMetadata(node.metadata); 1058 visitDeclarationMetadata(node.metadata);
986 1059
(...skipping 18 matching lines...) Expand all
1005 1078
1006 visitIndexExpression(IndexExpression node) { 1079 visitIndexExpression(IndexExpression node) {
1007 builder.nestExpression(); 1080 builder.nestExpression();
1008 1081
1009 if (node.isCascaded) { 1082 if (node.isCascaded) {
1010 token(node.period); 1083 token(node.period);
1011 } else { 1084 } else {
1012 visit(node.target); 1085 visit(node.target);
1013 } 1086 }
1014 1087
1088 finishIndexExpression(node);
1089
1090 builder.unnest();
1091 }
1092
1093 /// Visit the index part of [node], excluding the target.
1094 ///
1095 /// Called by [CallChainVisitor] to handle index expressions in the middle of
1096 /// call chains.
1097 void finishIndexExpression(IndexExpression node) {
1015 if (node.target is IndexExpression) { 1098 if (node.target is IndexExpression) {
1016 // Corner case: On a chain of [] accesses, allow splitting between them. 1099 // Edge case: On a chain of [] accesses, allow splitting between them.
1017 // Produces nicer output in cases like: 1100 // Produces nicer output in cases like:
1018 // 1101 //
1019 // someJson['property']['property']['property']['property']... 1102 // someJson['property']['property']['property']['property']...
1020 soloZeroSplit(); 1103 soloZeroSplit();
1021 } 1104 }
1022 1105
1023 builder.startSpan(); 1106 builder.startSpan();
1024 token(node.leftBracket); 1107 token(node.leftBracket);
1025 soloZeroSplit(); 1108 soloZeroSplit();
1026 visit(node.index); 1109 visit(node.index);
1027 token(node.rightBracket); 1110 token(node.rightBracket);
1028 builder.endSpan(); 1111 builder.endSpan();
1029 builder.unnest();
1030 } 1112 }
1031 1113
1032 visitInstanceCreationExpression(InstanceCreationExpression node) { 1114 visitInstanceCreationExpression(InstanceCreationExpression node) {
1033 builder.startSpan(); 1115 builder.startSpan();
1034 token(node.keyword); 1116 token(node.keyword);
1035 space(); 1117 space();
1118 builder.startSpan(Cost.constructorName);
1036 visit(node.constructorName); 1119 visit(node.constructorName);
1120 builder.endSpan();
1037 visit(node.argumentList); 1121 visit(node.argumentList);
1038 builder.endSpan(); 1122 builder.endSpan();
1039 } 1123 }
1040 1124
1041 visitIntegerLiteral(IntegerLiteral node) { 1125 visitIntegerLiteral(IntegerLiteral node) {
1042 token(node.literal); 1126 token(node.literal);
1043 } 1127 }
1044 1128
1045 visitInterpolationExpression(InterpolationExpression node) { 1129 visitInterpolationExpression(InterpolationExpression node) {
1046 token(node.leftBracket); 1130 token(node.leftBracket);
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
1105 } 1189 }
1106 1190
1107 visitMapLiteralEntry(MapLiteralEntry node) { 1191 visitMapLiteralEntry(MapLiteralEntry node) {
1108 visit(node.key); 1192 visit(node.key);
1109 token(node.separator); 1193 token(node.separator);
1110 soloSplit(); 1194 soloSplit();
1111 visit(node.value); 1195 visit(node.value);
1112 } 1196 }
1113 1197
1114 visitMethodDeclaration(MethodDeclaration node) { 1198 visitMethodDeclaration(MethodDeclaration node) {
1115 visitMemberMetadata(node.metadata); 1199 _visitMemberDeclaration(node, node);
1116
1117 modifier(node.externalKeyword);
1118 modifier(node.modifierKeyword);
1119 visit(node.returnType, after: space);
1120 modifier(node.propertyKeyword);
1121 modifier(node.operatorKeyword);
1122 visit(node.name);
1123
1124 _visitBody(node.parameters, node.body);
1125 } 1200 }
1126 1201
1127 visitMethodInvocation(MethodInvocation node) { 1202 visitMethodInvocation(MethodInvocation node) {
1128 // If there's no target, this is a "bare" function call like "foo(1, 2)", 1203 // If there's no target, this is a "bare" function call like "foo(1, 2)",
1129 // or a section in a cascade. Handle this case specially. 1204 // or a section in a cascade. Handle this case specially.
1130 if (node.target == null) { 1205 if (node.target == null) {
1131 // Try to keep the entire method invocation one line. 1206 // Try to keep the entire method invocation one line.
1132 builder.startSpan(); 1207 builder.startSpan();
1133 builder.nestExpression(); 1208 builder.nestExpression();
1134 1209
1135 // This will be non-null for cascade sections. 1210 // This will be non-null for cascade sections.
1136 token(node.operator); 1211 token(node.operator);
1137 token(node.methodName.token); 1212 token(node.methodName.token);
1138 visit(node.argumentList); 1213 visit(node.argumentList);
1139 1214
1140 builder.unnest(); 1215 builder.unnest();
1141 builder.endSpan(); 1216 builder.endSpan();
1142 return; 1217 return;
1143 } 1218 }
1144 1219
1145 new CallChainVisitor(this, node).visit(); 1220 new CallChainVisitor(this, node).visit();
1146 } 1221 }
1147 1222
1148 visitNamedExpression(NamedExpression node) { 1223 visitNamedExpression(NamedExpression node) {
1149 builder.nestExpression(); 1224 builder.nestExpression();
1150 builder.startSpan(); 1225 builder.startSpan();
1151 visit(node.name); 1226 visit(node.name);
1152 visit(node.expression, before: soloSplit); 1227
1228 // Don't allow a split between a name and a collection. Instead, we want
1229 // the collection itself to split, or to split before the argument.
1230 if (node.expression is ListLiteral || node.expression is MapLiteral) {
1231 space();
1232 } else {
1233 soloSplit();
1234 }
1235
1236 visit(node.expression);
1153 builder.endSpan(); 1237 builder.endSpan();
1154 builder.unnest(); 1238 builder.unnest();
1155 } 1239 }
1156 1240
1157 visitNativeClause(NativeClause node) { 1241 visitNativeClause(NativeClause node) {
1158 token(node.nativeKeyword); 1242 token(node.nativeKeyword);
1159 space(); 1243 space();
1160 visit(node.name); 1244 visit(node.name);
1161 } 1245 }
1162 1246
(...skipping 14 matching lines...) Expand all
1177 1261
1178 visitParenthesizedExpression(ParenthesizedExpression node) { 1262 visitParenthesizedExpression(ParenthesizedExpression node) {
1179 builder.nestExpression(); 1263 builder.nestExpression();
1180 token(node.leftParenthesis); 1264 token(node.leftParenthesis);
1181 visit(node.expression); 1265 visit(node.expression);
1182 builder.unnest(); 1266 builder.unnest();
1183 token(node.rightParenthesis); 1267 token(node.rightParenthesis);
1184 } 1268 }
1185 1269
1186 visitPartDirective(PartDirective node) { 1270 visitPartDirective(PartDirective node) {
1271 visitDeclarationMetadata(node.metadata);
1272
1187 _simpleStatement(node, () { 1273 _simpleStatement(node, () {
1188 token(node.keyword); 1274 token(node.keyword);
1189 space(); 1275 space();
1190 visit(node.uri); 1276 visit(node.uri);
1191 }); 1277 });
1192 } 1278 }
1193 1279
1194 visitPartOfDirective(PartOfDirective node) { 1280 visitPartOfDirective(PartOfDirective node) {
1281 visitDeclarationMetadata(node.metadata);
1282
1195 _simpleStatement(node, () { 1283 _simpleStatement(node, () {
1196 token(node.keyword); 1284 token(node.keyword);
1197 space(); 1285 space();
1198 token(node.ofKeyword); 1286 token(node.ofKeyword);
1199 space(); 1287 space();
1200 visit(node.libraryName); 1288 visit(node.libraryName);
1201 }); 1289 });
1202 } 1290 }
1203 1291
1204 visitPostfixExpression(PostfixExpression node) { 1292 visitPostfixExpression(PostfixExpression node) {
1205 visit(node.operand); 1293 visit(node.operand);
1206 token(node.operator); 1294 token(node.operator);
1207 } 1295 }
1208 1296
1209 visitPrefixedIdentifier(PrefixedIdentifier node) { 1297 visitPrefixedIdentifier(PrefixedIdentifier node) {
1210 visit(node.prefix); 1298 new CallChainVisitor(this, node).visit();
1211 token(node.period);
1212 visit(node.identifier);
1213 } 1299 }
1214 1300
1215 visitPrefixExpression(PrefixExpression node) { 1301 visitPrefixExpression(PrefixExpression node) {
1216 token(node.operator); 1302 token(node.operator);
1217 1303
1218 // Corner case: put a space between successive "-" operators so we don't 1304 // Corner case: put a space between successive "-" operators so we don't
1219 // inadvertently turn them into a "--" decrement operator. 1305 // inadvertently turn them into a "--" decrement operator.
1220 if (node.operand is PrefixExpression && 1306 if (node.operand is PrefixExpression &&
1221 (node.operand as PrefixExpression).operator.lexeme == "-") { 1307 (node.operand as PrefixExpression).operator.lexeme == "-") {
1222 space(); 1308 space();
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
1350 visitSwitchStatement(SwitchStatement node) { 1436 visitSwitchStatement(SwitchStatement node) {
1351 builder.nestExpression(); 1437 builder.nestExpression();
1352 token(node.switchKeyword); 1438 token(node.switchKeyword);
1353 space(); 1439 space();
1354 token(node.leftParenthesis); 1440 token(node.leftParenthesis);
1355 soloZeroSplit(); 1441 soloZeroSplit();
1356 visit(node.expression); 1442 visit(node.expression);
1357 token(node.rightParenthesis); 1443 token(node.rightParenthesis);
1358 space(); 1444 space();
1359 token(node.leftBracket); 1445 token(node.leftBracket);
1446 builder.unnest();
1360 builder.indent(); 1447 builder.indent();
1361 newline(); 1448 newline();
1362 1449
1363 visitNodes(node.members, between: oneOrTwoNewlines, after: newline); 1450 visitNodes(node.members, between: oneOrTwoNewlines, after: newline);
1364 token(node.rightBracket, before: () { 1451 token(node.rightBracket, before: () {
1365 builder.unindent(); 1452 builder.unindent();
1366 newline(); 1453 newline();
1367 }); 1454 });
1368 builder.unnest();
1369 } 1455 }
1370 1456
1371 visitSymbolLiteral(SymbolLiteral node) { 1457 visitSymbolLiteral(SymbolLiteral node) {
1372 token(node.poundSign); 1458 token(node.poundSign);
1373 var components = node.components; 1459 var components = node.components;
1374 for (var component in components) { 1460 for (var component in components) {
1375 // The '.' separator 1461 // The '.' separator
1376 if (component.previous.lexeme == '.') { 1462 if (component.previous.lexeme == '.') {
1377 token(component.previous); 1463 token(component.previous);
1378 } 1464 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1418 1504
1419 visitTypeParameter(TypeParameter node) { 1505 visitTypeParameter(TypeParameter node) {
1420 visitParameterMetadata(node.metadata, () { 1506 visitParameterMetadata(node.metadata, () {
1421 visit(node.name); 1507 visit(node.name);
1422 token(node.extendsKeyword, before: space, after: space); 1508 token(node.extendsKeyword, before: space, after: space);
1423 visit(node.bound); 1509 visit(node.bound);
1424 }); 1510 });
1425 } 1511 }
1426 1512
1427 visitTypeParameterList(TypeParameterList node) { 1513 visitTypeParameterList(TypeParameterList node) {
1514 _metadataRules.add(new MetadataRule());
1515
1428 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters); 1516 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
1517
1518 _metadataRules.removeLast();
1429 } 1519 }
1430 1520
1431 visitVariableDeclaration(VariableDeclaration node) { 1521 visitVariableDeclaration(VariableDeclaration node) {
1432 visit(node.name); 1522 visit(node.name);
1433 if (node.initializer == null) return; 1523 if (node.initializer == null) return;
1434 1524
1435 _visitAssignment(node.equals, node.initializer); 1525 _visitAssignment(node.equals, node.initializer);
1436 } 1526 }
1437 1527
1438 visitVariableDeclarationList(VariableDeclarationList node) { 1528 visitVariableDeclarationList(VariableDeclarationList node) {
1439 visitDeclarationMetadata(node.metadata); 1529 visitDeclarationMetadata(node.metadata);
1530
1531 // Allow but try to avoid splitting between the type and name.
1532 builder.startSpan();
1533
1440 modifier(node.keyword); 1534 modifier(node.keyword);
1441 visit(node.type, after: space); 1535 visit(node.type, after: soloSplit);
1536
1537 builder.endSpan();
1442 1538
1443 // Use a single rule for all of the variables. If there are multiple 1539 // Use a single rule for all of the variables. If there are multiple
1444 // declarations, we will try to keep them all on one line. If that isn't 1540 // declarations, we will try to keep them all on one line. If that isn't
1445 // possible, we split after *every* declaration so that each is on its own 1541 // possible, we split after *every* declaration so that each is on its own
1446 // line. 1542 // line.
1447 builder.startRule(); 1543 builder.startRule();
1448 visitCommaSeparatedNodes(node.variables, between: split); 1544 visitCommaSeparatedNodes(node.variables, between: split);
1449 builder.endRule(); 1545 builder.endRule();
1450 } 1546 }
1451 1547
1452 visitVariableDeclarationStatement(VariableDeclarationStatement node) { 1548 visitVariableDeclarationStatement(VariableDeclarationStatement node) {
1453 _simpleStatement(node, () { 1549 _simpleStatement(node, () {
1454 visit(node.variables); 1550 visit(node.variables);
1455 }); 1551 });
1456 } 1552 }
1457 1553
1458 visitWhileStatement(WhileStatement node) { 1554 visitWhileStatement(WhileStatement node) {
1459 builder.nestExpression(); 1555 builder.nestExpression();
1460 token(node.whileKeyword); 1556 token(node.whileKeyword);
1461 space(); 1557 space();
1462 token(node.leftParenthesis); 1558 token(node.leftParenthesis);
1463 soloZeroSplit(); 1559 soloZeroSplit();
1464 visit(node.condition); 1560 visit(node.condition);
1465 token(node.rightParenthesis); 1561 token(node.rightParenthesis);
1466 if (node.body is! EmptyStatement) space(); 1562 builder.unnest(now: false);
1467 visit(node.body); 1563
1468 builder.unnest(); 1564 _visitLoopBody(node.body);
1469 } 1565 }
1470 1566
1471 visitWithClause(WithClause node) { 1567 visitWithClause(WithClause node) {
1472 _visitCombinator(node.withKeyword, node.mixinTypes); 1568 _visitCombinator(node.withKeyword, node.mixinTypes);
1473 } 1569 }
1474 1570
1475 visitYieldStatement(YieldStatement node) { 1571 visitYieldStatement(YieldStatement node) {
1476 _simpleStatement(node, () { 1572 _simpleStatement(node, () {
1477 token(node.yieldKeyword); 1573 token(node.yieldKeyword);
1478 token(node.star); 1574 token(node.star);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1517 } else { 1613 } else {
1518 visitNodes(metadata, between: space, after: spaceOrNewline); 1614 visitNodes(metadata, between: space, after: spaceOrNewline);
1519 } 1615 }
1520 } 1616 }
1521 1617
1522 /// Visits metadata annotations on parameters and type parameters. 1618 /// Visits metadata annotations on parameters and type parameters.
1523 /// 1619 ///
1524 /// These are always on the same line as the parameter. 1620 /// These are always on the same line as the parameter.
1525 void visitParameterMetadata( 1621 void visitParameterMetadata(
1526 NodeList<Annotation> metadata, void visitParameter()) { 1622 NodeList<Annotation> metadata, void visitParameter()) {
1623 if (metadata == null || metadata.isEmpty) {
1624 visitParameter();
1625 return;
1626 }
1627
1527 // Split before all of the annotations or none. 1628 // Split before all of the annotations or none.
1528 builder.startRule(); 1629 builder.startLazyRule(_metadataRules.last);
1529 visitNodes(metadata, between: split, after: split); 1630
1631 visitNodes(metadata, between: split, after: () {
1632 // Don't nest until right before the last metadata. Ensures we only
1633 // indent the parameter and not any of the metadata:
1634 //
1635 // function(
1636 // @LongAnnotation
1637 // @LongAnnotation
1638 // indentedParameter) {}
1639 builder.nestExpression(now: true);
1640 split();
1641 });
1530 visitParameter(); 1642 visitParameter();
1531 1643
1644 builder.unnest();
1645
1532 // Wrap the rule around the parameter too. If it splits, we want to force 1646 // Wrap the rule around the parameter too. If it splits, we want to force
1533 // the annotations to split as well. 1647 // the annotations to split as well.
1534 builder.endRule(); 1648 builder.endRule();
1535 } 1649 }
1536 1650
1537 /// Visits the `=` and the following expression in any place where an `=` 1651 /// Visits the `=` and the following expression in any place where an `=`
1538 /// appears: 1652 /// appears:
1539 /// 1653 ///
1540 /// * Assignment 1654 /// * Assignment
1541 /// * Variable declaration 1655 /// * Variable declaration
1542 /// * Constructor initialization 1656 /// * Constructor initialization
1543 void _visitAssignment(Token equalsOperator, Expression rightHandSide) { 1657 void _visitAssignment(Token equalsOperator, Expression rightHandSide) {
1544 space(); 1658 space();
1545 token(equalsOperator); 1659 token(equalsOperator);
1546 soloSplit(Cost.assignment); 1660 soloSplit(_assignmentCost(rightHandSide));
1547 builder.startSpan(); 1661 builder.startSpan();
1548 visit(rightHandSide); 1662 visit(rightHandSide);
1549 builder.endSpan(); 1663 builder.endSpan();
1550 } 1664 }
1551 1665
1552 /// Visits a type parameter or type argument list. 1666 /// Visits a type parameter or type argument list.
1553 void _visitGenericList( 1667 void _visitGenericList(
1554 Token leftBracket, Token rightBracket, List<AstNode> nodes) { 1668 Token leftBracket, Token rightBracket, List<AstNode> nodes) {
1555 var rule = new TypeArgumentRule(); 1669 var rule = new TypeArgumentRule();
1556 builder.startRule(rule); 1670 builder.startRule(rule);
(...skipping 13 matching lines...) Expand all
1570 } 1684 }
1571 } 1685 }
1572 1686
1573 token(rightBracket); 1687 token(rightBracket);
1574 1688
1575 builder.unnest(); 1689 builder.unnest();
1576 builder.endSpan(); 1690 builder.endSpan();
1577 builder.endRule(); 1691 builder.endRule();
1578 } 1692 }
1579 1693
1694 /// Visits a top-level function or method declaration.
1695 ///
1696 /// The two AST node types are very similar but, alas, share no common
1697 /// interface type in analyzer, hence the dynamic typing.
1698 void _visitMemberDeclaration(
1699 /* FunctionDeclaration|MethodDeclaration */ node,
1700 /* FunctionExpression|MethodDeclaration */ function) {
1701 visitMemberMetadata(node.metadata);
1702
1703 // Nest the signature in case we have to split between the return type and
1704 // name.
1705 builder.nestExpression();
1706 builder.startSpan();
1707 modifier(node.externalKeyword);
1708 if (node is MethodDeclaration) modifier(node.modifierKeyword);
1709 visit(node.returnType, after: soloSplit);
1710 modifier(node.propertyKeyword);
1711 if (node is MethodDeclaration) modifier(node.operatorKeyword);
1712 visit(node.name);
1713 builder.endSpan();
1714
1715 // If the body is a block, we need to exit any nesting first. If it's an
1716 // expression, we want to wrap the nesting around that so that the body
1717 // gets nested farther.
1718 if (function.body is! ExpressionFunctionBody) builder.unnest();
1719
1720 _visitBody(function.parameters, function.body);
1721
1722 if (function.body is ExpressionFunctionBody) builder.unnest();
1723 }
1724
1580 /// Visit the given function [parameters] followed by its [body], printing a 1725 /// Visit the given function [parameters] followed by its [body], printing a
1581 /// space before it if it's not empty. 1726 /// space before it if it's not empty.
1582 /// 1727 ///
1583 /// If [afterParameters] is provided, it is invoked between the parameters 1728 /// If [afterParameters] is provided, it is invoked between the parameters
1584 /// and body. (It's used for constructor initialization lists.) 1729 /// and body. (It's used for constructor initialization lists.)
1585 void _visitBody(FormalParameterList parameters, FunctionBody body, 1730 void _visitBody(FormalParameterList parameters, FunctionBody body,
1586 [afterParameters()]) { 1731 [afterParameters()]) {
1587 // If the body is "=>", add an extra level of indentation around the 1732 // If the body is "=>", add an extra level of indentation around the
1588 // parameters and a rule that spans the parameters and the "=>". This 1733 // parameters and a rule that spans the parameters and the "=>". This
1589 // ensures that if the parameters wrap, they wrap more deeply than the "=>" 1734 // ensures that if the parameters wrap, they wrap more deeply than the "=>"
(...skipping 14 matching lines...) Expand all
1604 // level when they are actually unrelated. Splitting at "=>" forces: 1749 // level when they are actually unrelated. Splitting at "=>" forces:
1605 // 1750 //
1606 // someFunction(parameter, 1751 // someFunction(parameter,
1607 // parameter) => 1752 // parameter) =>
1608 // function( 1753 // function(
1609 // argument); 1754 // argument);
1610 if (body is ExpressionFunctionBody) { 1755 if (body is ExpressionFunctionBody) {
1611 builder.nestExpression(); 1756 builder.nestExpression();
1612 1757
1613 // This rule is ended by visitExpressionFunctionBody(). 1758 // This rule is ended by visitExpressionFunctionBody().
1614 builder.startLazyRule(new SimpleRule(cost: Cost.arrow)); 1759 builder.startLazyRule(new Rule(Cost.arrow));
1615 } 1760 }
1616 1761
1617 if (parameters != null) { 1762 if (parameters != null) {
1618 builder.nestExpression(); 1763 builder.nestExpression();
1764 visit(parameters);
1765 builder.unnest();
1619 1766
1620 visit(parameters);
1621 if (afterParameters != null) afterParameters(); 1767 if (afterParameters != null) afterParameters();
1622
1623 builder.unnest();
1624 } 1768 }
1625 1769
1626 visit(body); 1770 visit(body);
1627 1771
1628 if (body is ExpressionFunctionBody) builder.unnest(); 1772 if (body is ExpressionFunctionBody) builder.unnest();
1629 } 1773 }
1630 1774
1775 /// Visits the body statement of a `for` or `for in` loop.
1776 void _visitLoopBody(Statement body) {
1777 if (body is EmptyStatement) {
1778 // No space before the ";".
1779 visit(body);
1780 } else if (body is Block) {
1781 space();
1782 visit(body);
1783 } else {
1784 // Allow splitting in an expression-bodied for even though it's against
1785 // the style guide. Since we can't fix the code itself to follow the
1786 // style guide, we should at least format it as well as we can.
1787 builder.nestExpression(indent: 2, now: true);
1788 builder.startRule();
1789
1790 split();
1791 visit(body);
1792
1793 builder.endRule();
1794 builder.unnest();
1795 }
1796 }
1797
1631 /// Visit a list of [nodes] if not null, optionally separated and/or preceded 1798 /// Visit a list of [nodes] if not null, optionally separated and/or preceded
1632 /// and followed by the given functions. 1799 /// and followed by the given functions.
1633 void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) { 1800 void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
1634 if (nodes == null || nodes.isEmpty) return; 1801 if (nodes == null || nodes.isEmpty) return;
1635 1802
1636 if (before != null) before(); 1803 if (before != null) before();
1637 1804
1638 visit(nodes.first); 1805 visit(nodes.first);
1639 for (var node in nodes.skip(1)) { 1806 for (var node in nodes.skip(1)) {
1640 if (between != null) between(); 1807 if (between != null) between();
(...skipping 26 matching lines...) Expand all
1667 void _visitCollectionLiteral(TypedLiteral node, Token leftBracket, 1834 void _visitCollectionLiteral(TypedLiteral node, Token leftBracket,
1668 Iterable<AstNode> elements, Token rightBracket, 1835 Iterable<AstNode> elements, Token rightBracket,
1669 [int cost]) { 1836 [int cost]) {
1670 modifier(node.constKeyword); 1837 modifier(node.constKeyword);
1671 visit(node.typeArguments); 1838 visit(node.typeArguments);
1672 1839
1673 // Don't allow splitting in an empty collection. 1840 // Don't allow splitting in an empty collection.
1674 if (elements.isEmpty && rightBracket.precedingComments == null) { 1841 if (elements.isEmpty && rightBracket.precedingComments == null) {
1675 token(leftBracket); 1842 token(leftBracket);
1676 token(rightBracket); 1843 token(rightBracket);
1677
1678 // Clear this out in case this empty collection is in an argument list.
1679 // We don't want this rule to bleed over to some other collection.
1680 _nextLiteralBodyRule = null;
1681 return; 1844 return;
1682 } 1845 }
1683 1846
1684 // Force all of the surrounding collections to split. 1847 // Force all of the surrounding collections to split.
1685 for (var i = 0; i < _collectionSplits.length; i++) { 1848 for (var i = 0; i < _collectionSplits.length; i++) {
1686 _collectionSplits[i] = true; 1849 _collectionSplits[i] = true;
1687 } 1850 }
1688 1851
1689 // Add this collection to the stack. 1852 // Add this collection to the stack.
1690 _collectionSplits.add(false); 1853 _collectionSplits.add(false);
1691 1854
1692 _startLiteralBody(leftBracket); 1855 _startLiteralBody(leftBracket);
1693 1856
1694 // Always use a hard rule to split the elements. The parent chunk of 1857 // Always use a hard rule to split the elements. The parent chunk of
1695 // the collection will handle the unsplit case, so this only comes 1858 // the collection will handle the unsplit case, so this only comes
1696 // into play when the collection is split. 1859 // into play when the collection is split.
1697 var rule = new HardSplitRule(); 1860 var rule = new Rule.hard();
1698 builder.startRule(rule); 1861 builder.startRule(rule);
1699 1862
1700 // If a collection contains a line comment, we assume it's a big complex 1863 // If a collection contains a line comment, we assume it's a big complex
1701 // blob of data with some documented structure. In that case, the user 1864 // blob of data with some documented structure. In that case, the user
1702 // probably broke the elements into lines deliberately, so preserve those. 1865 // probably broke the elements into lines deliberately, so preserve those.
1703 var preserveNewlines = _containsLineComments(elements, rightBracket); 1866 var preserveNewlines = _containsLineComments(elements, rightBracket);
1704 1867
1705 for (var element in elements) { 1868 for (var element in elements) {
1706 if (element != elements.first) { 1869 if (element != elements.first) {
1707 if (preserveNewlines) { 1870 if (preserveNewlines) {
1708 if (_endLine(element.beginToken.previous) != 1871 if (_endLine(element.beginToken.previous) !=
1709 _startLine(element.beginToken)) { 1872 _startLine(element.beginToken)) {
1710 oneOrTwoNewlines(); 1873 oneOrTwoNewlines();
1711 } else { 1874 } else {
1712 soloSplit(); 1875 soloSplit();
1713 } 1876 }
1714 } else { 1877 } else {
1715 builder.blockSplit(space: true); 1878 builder.split(nest: false, space: true);
1716 } 1879 }
1717 } 1880 }
1718 1881
1719 builder.nestExpression(); 1882 builder.nestExpression();
1720 visit(element); 1883 visit(element);
1721 1884
1722 // The comma after the element. 1885 // The comma after the element.
1723 if (element.endToken.next.lexeme == ",") token(element.endToken.next); 1886 if (element.endToken.next.lexeme == ",") token(element.endToken.next);
1724 1887
1725 builder.unnest(); 1888 builder.unnest();
1726 } 1889 }
1727 1890
1728 builder.endRule(); 1891 builder.endRule();
1729 1892
1730 // If there is a collection inside this one, it forces this one to split. 1893 // If there is a collection inside this one, it forces this one to split.
1731 var force = _collectionSplits.removeLast(); 1894 var force = _collectionSplits.removeLast();
1732 1895
1733 _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force); 1896 _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force);
1734 } 1897 }
1735 1898
1899 /// Gets the cost to split at an assignment (or `:` in the case of a named
1900 /// default value) with the given [rightHandSide].
1901 ///
1902 /// "Block-like" expressions (collections and cascades) bind a bit tighter
1903 /// because it looks better to have code like:
1904 ///
1905 /// var list = [
1906 /// element,
1907 /// element,
1908 /// element
1909 /// ];
1910 ///
1911 /// var builder = new SomeBuilderClass()
1912 /// ..method()
1913 /// ..method();
1914 ///
1915 /// over:
1916 ///
1917 /// var list =
1918 /// [element, element, element];
1919 ///
1920 /// var builder =
1921 /// new SomeBuilderClass()..method()..method();
1922 int _assignmentCost(Expression rightHandSide) {
1923 if (rightHandSide is ListLiteral) return Cost.assignBlock;
1924 if (rightHandSide is MapLiteral) return Cost.assignBlock;
1925 if (rightHandSide is CascadeExpression) return Cost.assignBlock;
1926
1927 return Cost.assign;
1928 }
1929
1736 /// Returns `true` if the collection withs [elements] delimited by 1930 /// Returns `true` if the collection withs [elements] delimited by
1737 /// [rightBracket] contains any line comments. 1931 /// [rightBracket] contains any line comments.
1738 /// 1932 ///
1739 /// This only looks for comments at the element boundary. Comments within an 1933 /// This only looks for comments at the element boundary. Comments within an
1740 /// element are ignored. 1934 /// element are ignored.
1741 bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) { 1935 bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) {
1742 hasLineCommentBefore(token) { 1936 hasLineCommentBefore(token) {
1743 var comment = token.precedingComments; 1937 var comment = token.precedingComments;
1744 for (; comment != null; comment = comment.next) { 1938 for (; comment != null; comment = comment.next) {
1745 if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true; 1939 if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true;
(...skipping 12 matching lines...) Expand all
1758 } 1952 }
1759 1953
1760 /// Begins writing a literal body: a collection or block-bodied function 1954 /// Begins writing a literal body: a collection or block-bodied function
1761 /// expression. 1955 /// expression.
1762 /// 1956 ///
1763 /// Writes the delimiter and then creates the [Rule] that handles splitting 1957 /// Writes the delimiter and then creates the [Rule] that handles splitting
1764 /// the body. 1958 /// the body.
1765 void _startLiteralBody(Token leftBracket) { 1959 void _startLiteralBody(Token leftBracket) {
1766 token(leftBracket); 1960 token(leftBracket);
1767 1961
1768 // Split the literal. Use the explicitly given rule if we have one. 1962 // See if this literal is associated with an argument list that wants to
1769 // Otherwise, create a new rule. 1963 // handle splitting and indenting it. If not, we'll use a default rule.
1770 var rule = _nextLiteralBodyRule; 1964 var rule;
1771 _nextLiteralBodyRule = null; 1965 var argumentChunk;
1966 if (_collectionArgumentLists.containsKey(leftBracket)) {
1967 var argumentList = _collectionArgumentLists[leftBracket];
1968 rule = argumentList.collectionRule;
1969 argumentChunk = argumentList.previousSplit;
1970 }
1772 1971
1773 // Create a rule for whether or not to split the block contents. 1972 // Create a rule for whether or not to split the block contents.
1774 builder.startRule(rule); 1973 builder.startRule(rule);
1775 1974
1776 // Process the collection contents as a separate set of chunks. 1975 // Process the collection contents as a separate set of chunks.
1777 builder = builder.startBlock(); 1976 builder = builder.startBlock(argumentChunk);
1778 } 1977 }
1779 1978
1780 /// Ends the literal body started by a call to [_startLiteralBody()]. 1979 /// Ends the literal body started by a call to [_startLiteralBody()].
1781 /// 1980 ///
1782 /// If [forceSplit] is `true`, forces the body to split. If [ignoredRule] is 1981 /// If [forceSplit] is `true`, forces the body to split. If [ignoredRule] is
1783 /// given, ignores that rule inside the body when determining if it should 1982 /// given, ignores that rule inside the body when determining if it should
1784 /// split. 1983 /// split.
1785 void _endLiteralBody(Token rightBracket, 1984 void _endLiteralBody(Token rightBracket,
1786 {Rule ignoredRule, bool forceSplit}) { 1985 {Rule ignoredRule, bool forceSplit}) {
1787 if (forceSplit == null) forceSplit = false; 1986 if (forceSplit == null) forceSplit = false;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1827 void _simpleStatement(AstNode node, body()) { 2026 void _simpleStatement(AstNode node, body()) {
1828 builder.nestExpression(); 2027 builder.nestExpression();
1829 body(); 2028 body();
1830 2029
1831 // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base 2030 // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base
1832 // type? 2031 // type?
1833 token((node as dynamic).semicolon); 2032 token((node as dynamic).semicolon);
1834 builder.unnest(); 2033 builder.unnest();
1835 } 2034 }
1836 2035
1837 /// Makes [rule] the rule that will be used for the contents of a collection 2036 /// Marks the collection literal that starts with [leftBracket] as being
1838 /// or function literal body that are about to be visited. 2037 /// controlled by [argumentList].
1839 void setNextLiteralBodyRule(Rule rule) { 2038 ///
1840 _nextLiteralBodyRule = rule; 2039 /// When the collection is visited, [argumentList] will determine the
2040 /// indentation and splitting rule for the collection.
2041 void beforeCollection(Token leftBracket, ArgumentSublist argumentList) {
2042 _collectionArgumentLists[leftBracket] = argumentList;
1841 } 2043 }
1842 2044
1843 /// Writes an bracket-delimited body and handles indenting and starting the 2045 /// Writes an bracket-delimited body and handles indenting and starting the
1844 /// rule used to split the contents. 2046 /// rule used to split the contents.
1845 /// 2047 ///
1846 /// If [space] is `true`, then the contents and delimiters will have a space 2048 /// If [space] is `true`, then the contents and delimiters will have a space
1847 /// between then when unsplit. 2049 /// between then when unsplit.
1848 void _writeBody(Token leftBracket, Token rightBracket, 2050 void _writeBody(Token leftBracket, Token rightBracket,
1849 {bool space: false, body()}) { 2051 {bool space: false, body()}) {
1850 token(leftBracket); 2052 token(leftBracket);
1851 2053
1852 // Indent the body. 2054 // Indent the body.
1853 builder.indent(); 2055 builder.indent();
1854 2056
1855 // Split after the bracket. 2057 // Split after the bracket.
1856 builder.startRule(); 2058 builder.startRule();
1857 builder.blockSplit(space: space, isDouble: false); 2059 builder.split(isDouble: false, nest: false, space: space);
1858 2060
1859 body(); 2061 body();
1860 2062
1861 token(rightBracket, before: () { 2063 token(rightBracket, before: () {
1862 // Split before the closing bracket character. 2064 // Split before the closing bracket character.
1863 builder.unindent(); 2065 builder.unindent();
1864 builder.blockSplit(space: space); 2066 builder.split(nest: false, space: space);
1865 }); 2067 });
1866 2068
1867 builder.endRule(); 2069 builder.endRule();
1868 } 2070 }
1869 2071
1870 /// Returns `true` if [node] is immediately contained within an anonymous 2072 /// Returns `true` if [node] is immediately contained within an anonymous
1871 /// [FunctionExpression]. 2073 /// [FunctionExpression].
1872 bool _isInLambda(AstNode node) => node.parent is FunctionExpression && 2074 bool _isInLambda(AstNode node) =>
2075 node.parent is FunctionExpression &&
1873 node.parent.parent is! FunctionDeclaration; 2076 node.parent.parent is! FunctionDeclaration;
1874 2077
1875 /// Writes the string literal [string] to the output. 2078 /// Writes the string literal [string] to the output.
1876 /// 2079 ///
1877 /// Splits multiline strings into separate chunks so that the line splitter 2080 /// Splits multiline strings into separate chunks so that the line splitter
1878 /// can handle them correctly. 2081 /// can handle them correctly.
1879 void _writeStringLiteral(String string, int offset) { 2082 void _writeStringLiteral(String string, int offset) {
1880 // Split each line of a multiline string into separate chunks. 2083 // Split each line of a multiline string into separate chunks.
1881 var lines = string.split(_formatter.lineEnding); 2084 var lines = string.split(_formatter.lineEnding);
1882 2085
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1938 /// Returns the chunk the split was applied to. 2141 /// Returns the chunk the split was applied to.
1939 Chunk split() => builder.split(space: true); 2142 Chunk split() => builder.split(space: true);
1940 2143
1941 /// Writes a zero-space split owned by the current rule. 2144 /// Writes a zero-space split owned by the current rule.
1942 /// 2145 ///
1943 /// Returns the chunk the split was applied to. 2146 /// Returns the chunk the split was applied to.
1944 Chunk zeroSplit() => builder.split(); 2147 Chunk zeroSplit() => builder.split();
1945 2148
1946 /// Writes a single space split with its own rule. 2149 /// Writes a single space split with its own rule.
1947 void soloSplit([int cost]) { 2150 void soloSplit([int cost]) {
1948 builder.startRule(new SimpleRule(cost: cost)); 2151 builder.startRule(new Rule(cost));
1949 split(); 2152 split();
1950 builder.endRule(); 2153 builder.endRule();
1951 } 2154 }
1952 2155
1953 /// Writes a zero-space split with its own rule. 2156 /// Writes a zero-space split with its own rule.
1954 void soloZeroSplit() { 2157 void soloZeroSplit() {
1955 builder.startRule(); 2158 builder.startRule();
1956 builder.split(); 2159 builder.split();
1957 builder.endRule(); 2160 builder.endRule();
1958 } 2161 }
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
2159 /// Gets the 1-based line number that the beginning of [token] lies on. 2362 /// Gets the 1-based line number that the beginning of [token] lies on.
2160 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber; 2363 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
2161 2364
2162 /// Gets the 1-based line number that the end of [token] lies on. 2365 /// Gets the 1-based line number that the end of [token] lies on.
2163 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber; 2366 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
2164 2367
2165 /// Gets the 1-based column number that the beginning of [token] lies on. 2368 /// Gets the 1-based column number that the beginning of [token] lies on.
2166 int _startColumn(Token token) => 2369 int _startColumn(Token token) =>
2167 _lineInfo.getLocation(token.offset).columnNumber; 2370 _lineInfo.getLocation(token.offset).columnNumber;
2168 } 2371 }
OLDNEW
« no previous file with comments | « packages/dart_style/lib/src/rule/type_argument.dart ('k') | packages/dart_style/lib/src/whitespace.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698