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 'package:analyzer/analyzer.dart'; | 7 import 'package:analyzer/analyzer.dart'; |
8 | 8 |
9 import 'chunk.dart'; | 9 import 'chunk.dart'; |
10 import 'rule/argument.dart'; | 10 import 'rule/argument.dart'; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 var argumentsAfter = node.arguments.skip(functionsEnd).toList(); | 90 var argumentsAfter = node.arguments.skip(functionsEnd).toList(); |
91 | 91 |
92 return new ArgumentListVisitor._( | 92 return new ArgumentListVisitor._( |
93 visitor, | 93 visitor, |
94 node, | 94 node, |
95 new ArgumentSublist(node.arguments, argumentsBefore), | 95 new ArgumentSublist(node.arguments, argumentsBefore), |
96 functions, | 96 functions, |
97 new ArgumentSublist(node.arguments, argumentsAfter)); | 97 new ArgumentSublist(node.arguments, argumentsAfter)); |
98 } | 98 } |
99 | 99 |
100 ArgumentListVisitor._( | 100 ArgumentListVisitor._(this._visitor, this._node, this._arguments, |
101 this._visitor, | 101 this._functions, this._argumentsAfterFunctions); |
102 this._node, | |
103 this._arguments, | |
104 this._functions, | |
105 this._argumentsAfterFunctions); | |
106 | 102 |
107 /// Builds chunks for the call chain. | 103 /// Builds chunks for the call chain. |
108 void visit() { | 104 void visit() { |
109 // If there is just one positional argument, it tends to look weird to | 105 // If there is just one positional argument, it tends to look weird to |
110 // split before it, so try not to. | 106 // split before it, so try not to. |
111 if (_isSingle) _visitor.builder.startSpan(); | 107 if (_isSingle) _visitor.builder.startSpan(); |
112 | 108 |
113 // Nest around the parentheses in case there are comments before or after | 109 // Nest around the parentheses in case there are comments before or after |
114 // them. | 110 // them. |
115 _visitor.builder.nestExpression(); | 111 _visitor.builder.nestExpression(); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 if (_isSingle) _visitor.builder.endSpan(); | 151 if (_isSingle) _visitor.builder.endSpan(); |
156 } | 152 } |
157 | 153 |
158 /// Returns `true` if [expression] is a [FunctionExpression] with a block | 154 /// Returns `true` if [expression] is a [FunctionExpression] with a block |
159 /// body. | 155 /// body. |
160 static bool _isBlockFunction(Expression expression) { | 156 static bool _isBlockFunction(Expression expression) { |
161 if (expression is NamedExpression) { | 157 if (expression is NamedExpression) { |
162 expression = (expression as NamedExpression).expression; | 158 expression = (expression as NamedExpression).expression; |
163 } | 159 } |
164 | 160 |
| 161 // Allow functions wrapped in dotted method calls like "a.b.c(() { ... })". |
| 162 if (expression is MethodInvocation) { |
| 163 if (!_isValidWrappingTarget(expression.target)) return false; |
| 164 if (expression.argumentList.arguments.length != 1) return false; |
| 165 |
| 166 return _isBlockFunction(expression.argumentList.arguments.single); |
| 167 } |
| 168 |
165 // Curly body functions are. | 169 // Curly body functions are. |
166 if (expression is! FunctionExpression) return false; | 170 if (expression is! FunctionExpression) return false; |
167 var function = expression as FunctionExpression; | 171 var function = expression as FunctionExpression; |
168 return function.body is BlockFunctionBody; | 172 return function.body is BlockFunctionBody; |
169 } | 173 } |
| 174 |
| 175 /// Returns `true` if [expression] is a valid method invocation target for |
| 176 /// an invocation that wraps a function literal argument. |
| 177 static bool _isValidWrappingTarget(Expression expression) { |
| 178 // Allow bare function calls. |
| 179 if (expression == null) return true; |
| 180 |
| 181 // Allow property accesses. |
| 182 while (expression is PropertyAccess) { |
| 183 expression = (expression as PropertyAccess).target; |
| 184 } |
| 185 |
| 186 if (expression is PrefixedIdentifier) return true; |
| 187 if (expression is SimpleIdentifier) return true; |
| 188 |
| 189 return false; |
| 190 } |
170 } | 191 } |
171 | 192 |
172 /// A range of arguments from a complete argument list. | 193 /// A range of arguments from a complete argument list. |
173 /// | 194 /// |
174 /// One of these typically covers all of the arguments in an invocation. But, | 195 /// One of these typically covers all of the arguments in an invocation. But, |
175 /// when an argument list has block functions in the middle, the arguments | 196 /// when an argument list has block functions in the middle, the arguments |
176 /// before and after the functions are treated as separate independent lists. | 197 /// before and after the functions are treated as separate independent lists. |
177 /// In that case, there will be two of these. | 198 /// In that case, there will be two of these. |
178 class ArgumentSublist { | 199 class ArgumentSublist { |
179 /// The full argument list from the AST. | 200 /// The full argument list from the AST. |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 var positionalRule = rule; | 347 var positionalRule = rule; |
327 var namedRule = new NamedRule(_collectionRule); | 348 var namedRule = new NamedRule(_collectionRule); |
328 visitor.builder.startRule(namedRule); | 349 visitor.builder.startRule(namedRule); |
329 | 350 |
330 // Let the positional args force the named ones to split. | 351 // Let the positional args force the named ones to split. |
331 if (positionalRule != null) { | 352 if (positionalRule != null) { |
332 positionalRule.setNamedArgsRule(namedRule); | 353 positionalRule.setNamedArgsRule(namedRule); |
333 } | 354 } |
334 | 355 |
335 // Split before the first named argument. | 356 // Split before the first named argument. |
336 namedRule.beforeArguments(visitor.builder.split( | 357 namedRule.beforeArguments( |
337 space: !_isFirstArgument(_named.first))); | 358 visitor.builder.split(space: !_isFirstArgument(_named.first))); |
338 | 359 |
339 for (var argument in _named) { | 360 for (var argument in _named) { |
340 _visitArgument(visitor, namedRule, argument); | 361 _visitArgument(visitor, namedRule, argument); |
341 | 362 |
342 // Write the split. | 363 // Write the split. |
343 if (argument != _named.last) visitor.split(); | 364 if (argument != _named.last) visitor.split(); |
344 } | 365 } |
345 | 366 |
346 visitor.builder.endRule(); | 367 visitor.builder.endRule(); |
347 } | 368 } |
348 | 369 |
349 void _visitArgument(SourceVisitor visitor, ArgumentRule rule, Expression argum
ent) { | 370 void _visitArgument( |
| 371 SourceVisitor visitor, ArgumentRule rule, Expression argument) { |
350 // If we're about to write a collection argument, handle it specially. | 372 // If we're about to write a collection argument, handle it specially. |
351 if (_collections.contains(argument)) { | 373 if (_collections.contains(argument)) { |
352 if (rule != null) rule.beforeCollection(); | 374 if (rule != null) rule.beforeCollection(); |
353 | 375 |
354 // Tell it to use the rule we've already created. | 376 // Tell it to use the rule we've already created. |
355 visitor.setNextLiteralBodyRule(_collectionRule); | 377 visitor.setNextLiteralBodyRule(_collectionRule); |
356 } else if (_hasMultipleArguments) { | 378 } else if (_hasMultipleArguments) { |
357 // Corner case: If there is just a single argument, don't bump the | 379 // Corner case: If there is just a single argument, don't bump the |
358 // nesting. This lets us avoid spurious indentation in cases like: | 380 // nesting. This lets us avoid spurious indentation in cases like: |
359 // | 381 // |
(...skipping 27 matching lines...) Expand all Loading... |
387 /// indentation to make them look more statement-like. | 409 /// indentation to make them look more statement-like. |
388 static bool _isCollectionArgument(Expression expression) { | 410 static bool _isCollectionArgument(Expression expression) { |
389 if (expression is NamedExpression) { | 411 if (expression is NamedExpression) { |
390 expression = (expression as NamedExpression).expression; | 412 expression = (expression as NamedExpression).expression; |
391 } | 413 } |
392 | 414 |
393 // TODO(rnystrom): Should we step into parenthesized expressions? | 415 // TODO(rnystrom): Should we step into parenthesized expressions? |
394 | 416 |
395 return expression is ListLiteral || expression is MapLiteral; | 417 return expression is ListLiteral || expression is MapLiteral; |
396 } | 418 } |
397 } | 419 } |
OLD | NEW |