| OLD | NEW |
| 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.argument_list_visitor; | 5 library dart_style.src.argument_list_visitor; |
| 6 | 6 |
| 7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/src/generated/scanner.dart'; | 10 import 'package:analyzer/src/generated/scanner.dart'; |
| 11 | 11 |
| 12 import 'chunk.dart'; | 12 import 'chunk.dart'; |
| 13 import 'rule/argument.dart'; | 13 import 'rule/argument.dart'; |
| 14 import 'rule/rule.dart'; | 14 import 'rule/rule.dart'; |
| 15 import 'source_visitor.dart'; | 15 import 'source_visitor.dart'; |
| 16 | 16 |
| 17 /// Helper class for [SourceVisitor] that handles visiting and writing an | 17 /// Helper class for [SourceVisitor] that handles visiting and writing an |
| 18 /// [ArgumentList], including all of the special code needed to handle function | 18 /// [ArgumentList], including all of the special code needed to handle function |
| 19 /// and collection arguments. | 19 /// and collection arguments. |
| 20 class ArgumentListVisitor { | 20 class ArgumentListVisitor { |
| 21 final SourceVisitor _visitor; | 21 final SourceVisitor _visitor; |
| 22 | 22 |
| 23 final ArgumentList _node; | 23 /// The "(" before the argument list. |
| 24 final Token _leftParenthesis; |
| 25 |
| 26 /// The ")" after the argument list. |
| 27 final Token _rightParenthesis; |
| 28 |
| 29 /// All of the arguments, positional, named, and functions, in the argument |
| 30 /// list. |
| 31 final List<Expression> _allArguments; |
| 24 | 32 |
| 25 /// The normal arguments preceding any block function arguments. | 33 /// The normal arguments preceding any block function arguments. |
| 26 final ArgumentSublist _arguments; | 34 final ArgumentSublist _arguments; |
| 27 | 35 |
| 28 /// The contiguous list of block function arguments, if any. | 36 /// The contiguous list of block function arguments, if any. |
| 29 /// | 37 /// |
| 30 /// Otherwise, this is `null`. | 38 /// Otherwise, this is `null`. |
| 31 final List<Expression> _functions; | 39 final List<Expression> _functions; |
| 32 | 40 |
| 33 /// If there are block function arguments, this is the arguments after them. | 41 /// If there are block function arguments, this is the arguments after them. |
| 34 /// | 42 /// |
| 35 /// Otherwise, this is `null`. | 43 /// Otherwise, this is `null`. |
| 36 final ArgumentSublist _argumentsAfterFunctions; | 44 final ArgumentSublist _argumentsAfterFunctions; |
| 37 | 45 |
| 38 /// Returns `true` if there is only a single positional argument. | 46 /// Returns `true` if there is only a single positional argument. |
| 39 bool get _isSingle => | 47 bool get _isSingle => |
| 40 _node.arguments.length == 1 && _node.arguments.single is! NamedExpression; | 48 _allArguments.length == 1 && _allArguments.single is! NamedExpression; |
| 41 | 49 |
| 42 /// Whether this argument list has any collection or block function arguments. | 50 /// Whether this argument list has any collection or block function arguments. |
| 43 // TODO(rnystrom): Returning true based on collections is non-optimal. It | 51 // TODO(rnystrom): Returning true based on collections is non-optimal. It |
| 44 // forces a method chain to break into two but the result collection may not | 52 // forces a method chain to break into two but the result collection may not |
| 45 // actually split which can lead to a method chain that's allowed to break | 53 // actually split which can lead to a method chain that's allowed to break |
| 46 // where it shouldn't. | 54 // where it shouldn't. |
| 47 bool get hasBlockArguments => | 55 bool get hasBlockArguments => |
| 48 _arguments._collections.isNotEmpty || _functions != null; | 56 _arguments._collections.isNotEmpty || _functions != null; |
| 49 | 57 |
| 50 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) { | 58 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) { |
| 59 return new ArgumentListVisitor.forArguments( |
| 60 visitor, node.leftParenthesis, node.rightParenthesis, node.arguments); |
| 61 } |
| 62 |
| 63 factory ArgumentListVisitor.forArguments( |
| 64 SourceVisitor visitor, |
| 65 Token leftParenthesis, |
| 66 Token rightParenthesis, |
| 67 List<Expression> arguments) { |
| 51 // Look for a single contiguous range of block function arguments. | 68 // Look for a single contiguous range of block function arguments. |
| 52 var functionsStart; | 69 var functionsStart; |
| 53 var functionsEnd; | 70 var functionsEnd; |
| 54 | 71 |
| 55 for (var i = 0; i < node.arguments.length; i++) { | 72 for (var i = 0; i < arguments.length; i++) { |
| 56 var argument = node.arguments[i]; | 73 var argument = arguments[i]; |
| 57 if (_isBlockFunction(argument)) { | 74 if (_isBlockFunction(argument)) { |
| 58 if (functionsStart == null) functionsStart = i; | 75 if (functionsStart == null) functionsStart = i; |
| 59 | 76 |
| 60 // The functions must be one contiguous section. | 77 // The functions must be one contiguous section. |
| 61 if (functionsEnd != null && functionsEnd != i) { | 78 if (functionsEnd != null && functionsEnd != i) { |
| 62 functionsStart = null; | 79 functionsStart = null; |
| 63 functionsEnd = null; | 80 functionsEnd = null; |
| 64 break; | 81 break; |
| 65 } | 82 } |
| 66 | 83 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 81 // }, | 98 // }, |
| 82 // another: argument); | 99 // another: argument); |
| 83 // | 100 // |
| 84 // Over: | 101 // Over: |
| 85 // | 102 // |
| 86 // function(named: () { | 103 // function(named: () { |
| 87 // something(); | 104 // something(); |
| 88 // } | 105 // } |
| 89 // another: argument); | 106 // another: argument); |
| 90 if (functionsStart != null && | 107 if (functionsStart != null && |
| 91 node.arguments[0] is NamedExpression && | 108 arguments[0] is NamedExpression && |
| 92 (functionsStart > 0 || functionsEnd < node.arguments.length)) { | 109 (functionsStart > 0 || functionsEnd < arguments.length)) { |
| 93 functionsStart = null; | 110 functionsStart = null; |
| 94 } | 111 } |
| 95 | 112 |
| 96 if (functionsStart == null) { | 113 if (functionsStart == null) { |
| 97 // No functions, so there is just a single argument list. | 114 // No functions, so there is just a single argument list. |
| 98 return new ArgumentListVisitor._(visitor, node, | 115 return new ArgumentListVisitor._( |
| 99 new ArgumentSublist(node.arguments, node.arguments), null, null); | 116 visitor, |
| 117 leftParenthesis, |
| 118 rightParenthesis, |
| 119 arguments, |
| 120 new ArgumentSublist(arguments, arguments), |
| 121 null, |
| 122 null); |
| 100 } | 123 } |
| 101 | 124 |
| 102 // Split the arguments into two independent argument lists with the | 125 // Split the arguments into two independent argument lists with the |
| 103 // functions in the middle. | 126 // functions in the middle. |
| 104 var argumentsBefore = node.arguments.take(functionsStart).toList(); | 127 var argumentsBefore = arguments.take(functionsStart).toList(); |
| 105 var functions = node.arguments.sublist(functionsStart, functionsEnd); | 128 var functions = arguments.sublist(functionsStart, functionsEnd); |
| 106 var argumentsAfter = node.arguments.skip(functionsEnd).toList(); | 129 var argumentsAfter = arguments.skip(functionsEnd).toList(); |
| 107 | 130 |
| 108 return new ArgumentListVisitor._( | 131 return new ArgumentListVisitor._( |
| 109 visitor, | 132 visitor, |
| 110 node, | 133 leftParenthesis, |
| 111 new ArgumentSublist(node.arguments, argumentsBefore), | 134 rightParenthesis, |
| 135 arguments, |
| 136 new ArgumentSublist(arguments, argumentsBefore), |
| 112 functions, | 137 functions, |
| 113 new ArgumentSublist(node.arguments, argumentsAfter)); | 138 new ArgumentSublist(arguments, argumentsAfter)); |
| 114 } | 139 } |
| 115 | 140 |
| 116 ArgumentListVisitor._(this._visitor, this._node, this._arguments, | 141 ArgumentListVisitor._( |
| 117 this._functions, this._argumentsAfterFunctions); | 142 this._visitor, |
| 143 this._leftParenthesis, |
| 144 this._rightParenthesis, |
| 145 this._allArguments, |
| 146 this._arguments, |
| 147 this._functions, |
| 148 this._argumentsAfterFunctions); |
| 118 | 149 |
| 119 /// Builds chunks for the argument list. | 150 /// Builds chunks for the argument list. |
| 120 void visit() { | 151 void visit() { |
| 121 // If there is just one positional argument, it tends to look weird to | 152 // If there is just one positional argument, it tends to look weird to |
| 122 // split before it, so try not to. | 153 // split before it, so try not to. |
| 123 if (_isSingle) _visitor.builder.startSpan(); | 154 if (_isSingle) _visitor.builder.startSpan(); |
| 124 | 155 |
| 125 // Nest around the parentheses in case there are comments before or after | 156 // Nest around the parentheses in case there are comments before or after |
| 126 // them. | 157 // them. |
| 127 _visitor.builder.nestExpression(); | 158 _visitor.builder.nestExpression(); |
| 128 _visitor.builder.startSpan(); | 159 _visitor.builder.startSpan(); |
| 129 _visitor.token(_node.leftParenthesis); | 160 _visitor.token(_leftParenthesis); |
| 130 | 161 |
| 131 _arguments.visit(_visitor); | 162 _arguments.visit(_visitor); |
| 132 | 163 |
| 133 _visitor.builder.endSpan(); | 164 _visitor.builder.endSpan(); |
| 134 | 165 |
| 135 if (_functions != null) { | 166 if (_functions != null) { |
| 136 // TODO(rnystrom): It might look better to treat the parameter list of the | 167 // TODO(rnystrom): It might look better to treat the parameter list of the |
| 137 // first function as if it were an argument in the preceding argument list | 168 // first function as if it were an argument in the preceding argument list |
| 138 // instead of just having this little solo split here. That would try to | 169 // instead of just having this little solo split here. That would try to |
| 139 // keep the parameter list with other arguments when possible, and, I | 170 // keep the parameter list with other arguments when possible, and, I |
| 140 // think, generally look nicer. | 171 // think, generally look nicer. |
| 141 if (_functions.first == _node.arguments.first) { | 172 if (_functions.first == _allArguments.first) { |
| 142 _visitor.soloZeroSplit(); | 173 _visitor.soloZeroSplit(); |
| 143 } else { | 174 } else { |
| 144 _visitor.soloSplit(); | 175 _visitor.soloSplit(); |
| 145 } | 176 } |
| 146 | 177 |
| 147 for (var argument in _functions) { | 178 for (var argument in _functions) { |
| 148 if (argument != _functions.first) _visitor.space(); | 179 if (argument != _functions.first) _visitor.space(); |
| 149 | 180 |
| 150 _visitor.visit(argument); | 181 _visitor.visit(argument); |
| 151 | 182 |
| 152 // Write the trailing comma. | 183 // Write the trailing comma. |
| 153 if (argument != _node.arguments.last) { | 184 if (argument != _allArguments.last) { |
| 154 _visitor.token(argument.endToken.next); | 185 _visitor.token(argument.endToken.next); |
| 155 } | 186 } |
| 156 } | 187 } |
| 157 | 188 |
| 158 _visitor.builder.startSpan(); | 189 _visitor.builder.startSpan(); |
| 159 _argumentsAfterFunctions.visit(_visitor); | 190 _argumentsAfterFunctions.visit(_visitor); |
| 160 _visitor.builder.endSpan(); | 191 _visitor.builder.endSpan(); |
| 161 } | 192 } |
| 162 | 193 |
| 163 _visitor.token(_node.rightParenthesis); | 194 _visitor.token(_rightParenthesis); |
| 164 | 195 |
| 165 _visitor.builder.unnest(); | 196 _visitor.builder.unnest(); |
| 166 | 197 |
| 167 if (_isSingle) _visitor.builder.endSpan(); | 198 if (_isSingle) _visitor.builder.endSpan(); |
| 168 } | 199 } |
| 169 | 200 |
| 170 /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty | 201 /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty |
| 171 /// block body. | 202 /// block body. |
| 172 static bool _isBlockFunction(Expression expression) { | 203 static bool _isBlockFunction(Expression expression) { |
| 173 if (expression is NamedExpression) { | 204 if (expression is NamedExpression) { |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 } else { | 425 } else { |
| 395 // Edge case: Likewise, don't force the argument to split if there is | 426 // Edge case: Likewise, don't force the argument to split if there is |
| 396 // only a single one, like: | 427 // only a single one, like: |
| 397 // | 428 // |
| 398 // outer(inner( | 429 // outer(inner( |
| 399 // longArgument)); | 430 // longArgument)); |
| 400 rule.disableSplitOnInnerRules(); | 431 rule.disableSplitOnInnerRules(); |
| 401 } | 432 } |
| 402 | 433 |
| 403 if (argument is NamedExpression) { | 434 if (argument is NamedExpression) { |
| 404 visitor.visitNamedArgument( | 435 visitor.visitNamedArgument(argument, rule as NamedRule); |
| 405 argument as NamedExpression, rule as NamedRule); | |
| 406 } else { | 436 } else { |
| 407 visitor.visit(argument); | 437 visitor.visit(argument); |
| 408 } | 438 } |
| 409 | 439 |
| 410 if (_collections.containsKey(argument)) { | 440 if (_collections.containsKey(argument)) { |
| 411 rule.enableSplitOnInnerRules(); | 441 rule.enableSplitOnInnerRules(); |
| 412 } else if (_allArguments.length > 1) { | 442 } else if (_allArguments.length > 1) { |
| 413 visitor.builder.endBlockArgumentNesting(); | 443 visitor.builder.endBlockArgumentNesting(); |
| 414 } else { | 444 } else { |
| 415 rule.enableSplitOnInnerRules(); | 445 rule.enableSplitOnInnerRules(); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 437 | 467 |
| 438 // TODO(rnystrom): Should we step into parenthesized expressions? | 468 // TODO(rnystrom): Should we step into parenthesized expressions? |
| 439 | 469 |
| 440 if (expression is ListLiteral) return expression.leftBracket; | 470 if (expression is ListLiteral) return expression.leftBracket; |
| 441 if (expression is MapLiteral) return expression.leftBracket; | 471 if (expression is MapLiteral) return expression.leftBracket; |
| 442 | 472 |
| 443 // Not a collection literal. | 473 // Not a collection literal. |
| 444 return null; | 474 return null; |
| 445 } | 475 } |
| 446 } | 476 } |
| OLD | NEW |