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

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

Issue 1475873002: More sophisticated handling of parameter metadata. (Closed) Base URL: https://github.com/dart-lang/dart_style.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 24 matching lines...) Expand all
52 /// Each entry corresponds to a collection currently being visited and the 53 /// Each entry corresponds to a collection currently being visited and the
53 /// 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
54 /// 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`
55 /// then it pushes `false` for itself. 56 /// then it pushes `false` for itself.
56 /// 57 ///
57 /// 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
58 /// `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
59 /// split. 60 /// split.
60 final List<bool> _collectionSplits = []; 61 final List<bool> _collectionSplits = [];
61 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
62 /// The mapping for collection literals that are managed by the argument 71 /// The mapping for collection literals that are managed by the argument
63 /// list that contains them. 72 /// list that contains them.
64 /// 73 ///
65 /// When a collection literal appears inside an [ArgumentSublist], the 74 /// When a collection literal appears inside an [ArgumentSublist], the
66 /// argument list provides a rule for the body to split to ensure that all 75 /// argument list provides a rule for the body to split to ensure that all
67 /// collections split in unison. It also tracks the chunk before the 76 /// collections split in unison. It also tracks the chunk before the
68 /// argument that determines whether or not the collection body is indented 77 /// argument that determines whether or not the collection body is indented
69 /// like an expression or a statement. 78 /// like an expression or a statement.
70 /// 79 ///
71 /// Before a collection literal argument is visited, [ArgumentSublist] binds 80 /// Before a collection literal argument is visited, [ArgumentSublist] binds
(...skipping 710 matching lines...) Expand 10 before | Expand all | Expand 10 after
782 var requiredParams = node.parameters 791 var requiredParams = node.parameters
783 .where((param) => param is! DefaultFormalParameter) 792 .where((param) => param is! DefaultFormalParameter)
784 .toList(); 793 .toList();
785 var optionalParams = node.parameters 794 var optionalParams = node.parameters
786 .where((param) => param is DefaultFormalParameter) 795 .where((param) => param is DefaultFormalParameter)
787 .toList(); 796 .toList();
788 797
789 builder.nestExpression(); 798 builder.nestExpression();
790 token(node.leftParenthesis); 799 token(node.leftParenthesis);
791 800
801 _metadataRules.add(new MetadataRule());
802
792 var rule; 803 var rule;
793 if (requiredParams.isNotEmpty) { 804 if (requiredParams.isNotEmpty) {
794 if (requiredParams.length > 1) { 805 if (requiredParams.length > 1) {
795 rule = new MultiplePositionalRule(null, 0, 0); 806 rule = new MultiplePositionalRule(null, 0, 0);
796 } else { 807 } else {
797 rule = new SinglePositionalRule(null); 808 rule = new SinglePositionalRule(null);
798 } 809 }
799 810
811 _metadataRules.last.bindPositionalRule(rule);
812
800 builder.startRule(rule); 813 builder.startRule(rule);
801 if (_isInLambda(node)) { 814 if (_isInLambda(node)) {
802 // Don't allow splitting before the first argument (i.e. right after 815 // Don't allow splitting before the first argument (i.e. right after
803 // the bare "(" in a lambda. Instead, just stuff a null chunk in there 816 // the bare "(" in a lambda. Instead, just stuff a null chunk in there
804 // to avoid confusing the arg rule. 817 // to avoid confusing the arg rule.
805 rule.beforeArgument(null); 818 rule.beforeArgument(null);
806 } else { 819 } else {
807 // Split before the first argument. 820 // Split before the first argument.
808 rule.beforeArgument(zeroSplit()); 821 rule.beforeArgument(zeroSplit());
809 } 822 }
(...skipping 10 matching lines...) Expand all
820 } 833 }
821 834
822 builder.endSpan(); 835 builder.endSpan();
823 builder.endRule(); 836 builder.endRule();
824 } 837 }
825 838
826 if (optionalParams.isNotEmpty) { 839 if (optionalParams.isNotEmpty) {
827 var namedRule = new NamedRule(); 840 var namedRule = new NamedRule();
828 if (rule != null) rule.setNamedArgsRule(namedRule); 841 if (rule != null) rule.setNamedArgsRule(namedRule);
829 842
843 _metadataRules.last.bindNamedRule(namedRule);
844
830 builder.startRule(namedRule); 845 builder.startRule(namedRule);
831 846
832 // Make sure multi-line default values are indented. 847 // Make sure multi-line default values are indented.
833 builder.startBlockArgumentNesting(); 848 builder.startBlockArgumentNesting();
834 849
835 namedRule 850 namedRule
836 .beforeArguments(builder.split(space: requiredParams.isNotEmpty)); 851 .beforeArguments(builder.split(space: requiredParams.isNotEmpty));
837 852
838 // "[" or "{" for optional parameters. 853 // "[" or "{" for optional parameters.
839 token(node.leftDelimiter); 854 token(node.leftDelimiter);
840 855
841 for (var param in optionalParams) { 856 for (var param in optionalParams) {
842 visit(param); 857 visit(param);
843 858
844 // Write the trailing comma. 859 // Write the trailing comma.
845 if (param != node.parameters.last) token(param.endToken.next); 860 if (param != node.parameters.last) token(param.endToken.next);
846 if (param != optionalParams.last) split(); 861 if (param != optionalParams.last) split();
847 } 862 }
848 863
849 builder.endBlockArgumentNesting(); 864 builder.endBlockArgumentNesting();
850 builder.endRule(); 865 builder.endRule();
851 866
852 // "]" or "}" for optional parameters. 867 // "]" or "}" for optional parameters.
853 token(node.rightDelimiter); 868 token(node.rightDelimiter);
854 } 869 }
855 870
871 _metadataRules.removeLast();
872
856 token(node.rightParenthesis); 873 token(node.rightParenthesis);
857 builder.unnest(); 874 builder.unnest();
858 } 875 }
859 876
860 visitForStatement(ForStatement node) { 877 visitForStatement(ForStatement node) {
861 builder.nestExpression(); 878 builder.nestExpression();
862 token(node.forKeyword); 879 token(node.forKeyword);
863 space(); 880 space();
864 token(node.leftParenthesis); 881 token(node.leftParenthesis);
865 882
(...skipping 592 matching lines...) Expand 10 before | Expand all | Expand 10 after
1458 1475
1459 visitTypeParameter(TypeParameter node) { 1476 visitTypeParameter(TypeParameter node) {
1460 visitParameterMetadata(node.metadata, () { 1477 visitParameterMetadata(node.metadata, () {
1461 visit(node.name); 1478 visit(node.name);
1462 token(node.extendsKeyword, before: space, after: space); 1479 token(node.extendsKeyword, before: space, after: space);
1463 visit(node.bound); 1480 visit(node.bound);
1464 }); 1481 });
1465 } 1482 }
1466 1483
1467 visitTypeParameterList(TypeParameterList node) { 1484 visitTypeParameterList(TypeParameterList node) {
1485 _metadataRules.add(new MetadataRule());
1486
1468 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters); 1487 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
1488
1489 _metadataRules.removeLast();
1469 } 1490 }
1470 1491
1471 visitVariableDeclaration(VariableDeclaration node) { 1492 visitVariableDeclaration(VariableDeclaration node) {
1472 visit(node.name); 1493 visit(node.name);
1473 if (node.initializer == null) return; 1494 if (node.initializer == null) return;
1474 1495
1475 _visitAssignment(node.equals, node.initializer); 1496 _visitAssignment(node.equals, node.initializer);
1476 } 1497 }
1477 1498
1478 visitVariableDeclarationList(VariableDeclarationList node) { 1499 visitVariableDeclarationList(VariableDeclarationList node) {
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1563 } else { 1584 } else {
1564 visitNodes(metadata, between: space, after: spaceOrNewline); 1585 visitNodes(metadata, between: space, after: spaceOrNewline);
1565 } 1586 }
1566 } 1587 }
1567 1588
1568 /// Visits metadata annotations on parameters and type parameters. 1589 /// Visits metadata annotations on parameters and type parameters.
1569 /// 1590 ///
1570 /// These are always on the same line as the parameter. 1591 /// These are always on the same line as the parameter.
1571 void visitParameterMetadata( 1592 void visitParameterMetadata(
1572 NodeList<Annotation> metadata, void visitParameter()) { 1593 NodeList<Annotation> metadata, void visitParameter()) {
1594 if (metadata == null || metadata.isEmpty) {
1595 visitParameter();
1596 return;
1597 }
1598
1573 // Split before all of the annotations or none. 1599 // Split before all of the annotations or none.
1574 builder.startRule(); 1600 builder.startLazyRule(_metadataRules.last);
1575 visitNodes(metadata, between: split, after: split); 1601
1602 visitNodes(metadata, between: split, after: () {
1603 // Don't nest until right before the last metadata. Ensures we only
1604 // indent the parameter and not any of the metadata:
1605 //
1606 // function(
1607 // @LongAnnotation
1608 // @LongAnnotation
1609 // indentedParameter) {}
1610 builder.nestExpression(now: true);
1611 split();
1612 });
1576 visitParameter(); 1613 visitParameter();
1577 1614
1615 builder.unnest();
1616
1578 // Wrap the rule around the parameter too. If it splits, we want to force 1617 // Wrap the rule around the parameter too. If it splits, we want to force
1579 // the annotations to split as well. 1618 // the annotations to split as well.
1580 builder.endRule(); 1619 builder.endRule();
1581 } 1620 }
1582 1621
1583 /// Visits the `=` and the following expression in any place where an `=` 1622 /// Visits the `=` and the following expression in any place where an `=`
1584 /// appears: 1623 /// appears:
1585 /// 1624 ///
1586 /// * Assignment 1625 /// * Assignment
1587 /// * Variable declaration 1626 /// * Variable declaration
1588 /// * Constructor initialization 1627 /// * Constructor initialization
1589 void _visitAssignment(Token equalsOperator, Expression rightHandSide) { 1628 void _visitAssignment(Token equalsOperator, Expression rightHandSide) {
1590 space(); 1629 space();
1591 token(equalsOperator); 1630 token(equalsOperator);
1592 soloSplit(_assignmentCost(rightHandSide)); 1631 soloSplit(_assignmentCost(rightHandSide));
1593 builder.startSpan(); 1632 builder.startSpan();
1594 visit(rightHandSide); 1633 visit(rightHandSide);
1595 builder.endSpan(); 1634 builder.endSpan();
1596 } 1635 }
1597 1636
1598 /// Visits a type parameter or type argument list. 1637 /// Visits a type parameter or type argument list.
1599 void _visitGenericList( 1638 void _visitGenericList(
1600 Token leftBracket, Token rightBracket, List<AstNode> nodes) { 1639 Token leftBracket, Token rightBracket, List<AstNode> nodes) {
1601 var rule = new TypeArgumentRule(); 1640 var rule = new TypeArgumentRule();
1602 builder.startRule(rule); 1641 builder.startRule(rule);
1603 builder.startSpan(); 1642 builder.startSpan();
1604 builder.nestExpression(); 1643 builder.nestExpression();
1605 1644
1645 // TODO(rnystrom): Should the MetadataRule constrain a TypeArgumentRule
1646 // as well?
nweiz 2015/11/30 19:17:01 The doc comment for MetadataRule implies that it d
Bob Nystrom 2015/11/30 21:36:14 Done.
1647
1606 token(leftBracket); 1648 token(leftBracket);
1607 rule.beforeArgument(zeroSplit()); 1649 rule.beforeArgument(zeroSplit());
1608 1650
1609 for (var node in nodes) { 1651 for (var node in nodes) {
1610 visit(node); 1652 visit(node);
1611 1653
1612 // Write the trailing comma. 1654 // Write the trailing comma.
1613 if (node != nodes.last) { 1655 if (node != nodes.last) {
1614 token(node.endToken.next); 1656 token(node.endToken.next);
1615 rule.beforeArgument(split()); 1657 rule.beforeArgument(split());
(...skipping 677 matching lines...) Expand 10 before | Expand all | Expand 10 after
2293 /// Gets the 1-based line number that the beginning of [token] lies on. 2335 /// Gets the 1-based line number that the beginning of [token] lies on.
2294 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber; 2336 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
2295 2337
2296 /// Gets the 1-based line number that the end of [token] lies on. 2338 /// Gets the 1-based line number that the end of [token] lies on.
2297 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber; 2339 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
2298 2340
2299 /// Gets the 1-based column number that the beginning of [token] lies on. 2341 /// Gets the 1-based column number that the beginning of [token] lies on.
2300 int _startColumn(Token token) => 2342 int _startColumn(Token token) =>
2301 _lineInfo.getLocation(token.offset).columnNumber; 2343 _lineInfo.getLocation(token.offset).columnNumber;
2302 } 2344 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698