| 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/dart/ast/token.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 |
| 67 functionsEnd = i + 1; | 84 functionsEnd = i + 1; |
| 68 } | 85 } |
| 69 } | 86 } |
| 70 | 87 |
| 88 // Edge case: If all of the arguments are named, but they aren't all |
| 89 // functions, then don't handle the functions specially. A function with a |
| 90 // bunch of named arguments tends to look best when they are all lined up, |
| 91 // even the function ones (unless they are all functions). |
| 92 // |
| 93 // Prefers: |
| 94 // |
| 95 // function( |
| 96 // named: () { |
| 97 // something(); |
| 98 // }, |
| 99 // another: argument); |
| 100 // |
| 101 // Over: |
| 102 // |
| 103 // function(named: () { |
| 104 // something(); |
| 105 // } |
| 106 // another: argument); |
| 107 if (functionsStart != null && |
| 108 arguments[0] is NamedExpression && |
| 109 (functionsStart > 0 || functionsEnd < arguments.length)) { |
| 110 functionsStart = null; |
| 111 } |
| 112 |
| 71 if (functionsStart == null) { | 113 if (functionsStart == null) { |
| 72 // No functions, so there is just a single argument list. | 114 // No functions, so there is just a single argument list. |
| 73 return new ArgumentListVisitor._(visitor, node, | 115 return new ArgumentListVisitor._( |
| 74 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); |
| 75 } | 123 } |
| 76 | 124 |
| 77 // Split the arguments into two independent argument lists with the | 125 // Split the arguments into two independent argument lists with the |
| 78 // functions in the middle. | 126 // functions in the middle. |
| 79 var argumentsBefore = node.arguments.take(functionsStart).toList(); | 127 var argumentsBefore = arguments.take(functionsStart).toList(); |
| 80 var functions = node.arguments.sublist(functionsStart, functionsEnd); | 128 var functions = arguments.sublist(functionsStart, functionsEnd); |
| 81 var argumentsAfter = node.arguments.skip(functionsEnd).toList(); | 129 var argumentsAfter = arguments.skip(functionsEnd).toList(); |
| 82 | 130 |
| 83 return new ArgumentListVisitor._( | 131 return new ArgumentListVisitor._( |
| 84 visitor, | 132 visitor, |
| 85 node, | 133 leftParenthesis, |
| 86 new ArgumentSublist(node.arguments, argumentsBefore), | 134 rightParenthesis, |
| 135 arguments, |
| 136 new ArgumentSublist(arguments, argumentsBefore), |
| 87 functions, | 137 functions, |
| 88 new ArgumentSublist(node.arguments, argumentsAfter)); | 138 new ArgumentSublist(arguments, argumentsAfter)); |
| 89 } | 139 } |
| 90 | 140 |
| 91 ArgumentListVisitor._(this._visitor, this._node, this._arguments, | 141 ArgumentListVisitor._( |
| 92 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); |
| 93 | 149 |
| 94 /// Builds chunks for the call chain. | 150 /// Builds chunks for the argument list. |
| 95 void visit() { | 151 void visit() { |
| 96 // 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 |
| 97 // split before it, so try not to. | 153 // split before it, so try not to. |
| 98 if (_isSingle) _visitor.builder.startSpan(); | 154 if (_isSingle) _visitor.builder.startSpan(); |
| 99 | 155 |
| 100 // Nest around the parentheses in case there are comments before or after | |
| 101 // them. | |
| 102 _visitor.builder.nestExpression(); | |
| 103 _visitor.builder.startSpan(); | 156 _visitor.builder.startSpan(); |
| 104 _visitor.token(_node.leftParenthesis); | 157 _visitor.token(_leftParenthesis); |
| 105 | 158 |
| 106 _arguments.visit(_visitor); | 159 _arguments.visit(_visitor); |
| 107 | 160 |
| 108 _visitor.builder.endSpan(); | 161 _visitor.builder.endSpan(); |
| 109 | 162 |
| 110 if (_functions != null) { | 163 if (_functions != null) { |
| 111 // TODO(rnystrom): It might look better to treat the parameter list of the | 164 // TODO(rnystrom): It might look better to treat the parameter list of the |
| 112 // first function as if it were an argument in the preceding argument list | 165 // first function as if it were an argument in the preceding argument list |
| 113 // instead of just having this little solo split here. That would try to | 166 // instead of just having this little solo split here. That would try to |
| 114 // keep the parameter list with other arguments when possible, and, I | 167 // keep the parameter list with other arguments when possible, and, I |
| 115 // think, generally look nicer. | 168 // think, generally look nicer. |
| 116 if (_functions.first == _node.arguments.first) { | 169 if (_functions.first == _allArguments.first) { |
| 117 _visitor.soloZeroSplit(); | 170 _visitor.soloZeroSplit(); |
| 118 } else { | 171 } else { |
| 119 _visitor.soloSplit(); | 172 _visitor.soloSplit(); |
| 120 } | 173 } |
| 121 | 174 |
| 122 for (var argument in _functions) { | 175 for (var argument in _functions) { |
| 123 if (argument != _functions.first) _visitor.space(); | 176 if (argument != _functions.first) _visitor.space(); |
| 124 | 177 |
| 125 _visitor.visit(argument); | 178 _visitor.visit(argument); |
| 126 | 179 |
| 127 // Write the trailing comma. | 180 // Write the following comma. |
| 128 if (argument != _node.arguments.last) { | 181 if (argument.endToken.next.type == TokenType.COMMA) { |
| 129 _visitor.token(argument.endToken.next); | 182 _visitor.token(argument.endToken.next); |
| 130 } | 183 } |
| 131 } | 184 } |
| 132 | 185 |
| 133 _visitor.builder.startSpan(); | 186 _visitor.builder.startSpan(); |
| 134 _argumentsAfterFunctions.visit(_visitor); | 187 _argumentsAfterFunctions.visit(_visitor); |
| 135 _visitor.builder.endSpan(); | 188 _visitor.builder.endSpan(); |
| 136 } | 189 } |
| 137 | 190 |
| 138 _visitor.token(_node.rightParenthesis); | 191 _visitor.token(_rightParenthesis); |
| 139 | |
| 140 _visitor.builder.unnest(); | |
| 141 | 192 |
| 142 if (_isSingle) _visitor.builder.endSpan(); | 193 if (_isSingle) _visitor.builder.endSpan(); |
| 143 } | 194 } |
| 144 | 195 |
| 145 /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty | 196 /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty |
| 146 /// block body. | 197 /// block body. |
| 147 static bool _isBlockFunction(Expression expression) { | 198 static bool _isBlockFunction(Expression expression) { |
| 148 if (expression is NamedExpression) { | 199 if (expression is NamedExpression) { |
| 149 expression = (expression as NamedExpression).expression; | 200 expression = (expression as NamedExpression).expression; |
| 150 } | 201 } |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 final int _trailingCollections; | 276 final int _trailingCollections; |
| 226 | 277 |
| 227 /// The rule used to split the bodies of all of the collection arguments. | 278 /// The rule used to split the bodies of all of the collection arguments. |
| 228 Rule get collectionRule => _collectionRule; | 279 Rule get collectionRule => _collectionRule; |
| 229 Rule _collectionRule; | 280 Rule _collectionRule; |
| 230 | 281 |
| 231 /// The most recent chunk that split before an argument. | 282 /// The most recent chunk that split before an argument. |
| 232 Chunk get previousSplit => _previousSplit; | 283 Chunk get previousSplit => _previousSplit; |
| 233 Chunk _previousSplit; | 284 Chunk _previousSplit; |
| 234 | 285 |
| 235 bool get _hasMultipleArguments => _positional.length + _named.length > 1; | |
| 236 | |
| 237 factory ArgumentSublist( | 286 factory ArgumentSublist( |
| 238 List<Expression> allArguments, List<Expression> arguments) { | 287 List<Expression> allArguments, List<Expression> arguments) { |
| 239 // Assumes named arguments follow all positional ones. | 288 // Assumes named arguments follow all positional ones. |
| 240 var positional = | 289 var positional = |
| 241 arguments.takeWhile((arg) => arg is! NamedExpression).toList(); | 290 arguments.takeWhile((arg) => arg is! NamedExpression).toList(); |
| 242 var named = arguments.skip(positional.length).toList(); | 291 var named = arguments.skip(positional.length).toList(); |
| 243 | 292 |
| 244 var collections = {}; | 293 var collections = <Expression, Token>{}; |
| 245 for (var argument in arguments) { | 294 for (var argument in arguments) { |
| 246 var bracket = _getCollectionBracket(argument); | 295 var bracket = _getCollectionBracket(argument); |
| 247 if (bracket != null) collections[argument] = bracket; | 296 if (bracket != null) collections[argument] = bracket; |
| 248 } | 297 } |
| 249 | 298 |
| 250 // Count the leading arguments that are collection literals. | 299 // Count the leading arguments that are collection literals. |
| 251 var leadingCollections = 0; | 300 var leadingCollections = 0; |
| 252 for (var argument in arguments) { | 301 for (var argument in arguments) { |
| 253 if (!collections.containsKey(argument)) break; | 302 if (!collections.containsKey(argument)) break; |
| 254 leadingCollections++; | 303 leadingCollections++; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 287 | 336 |
| 288 var rule = _visitPositional(visitor); | 337 var rule = _visitPositional(visitor); |
| 289 _visitNamed(visitor, rule); | 338 _visitNamed(visitor, rule); |
| 290 } | 339 } |
| 291 | 340 |
| 292 /// Writes the positional arguments, if any. | 341 /// Writes the positional arguments, if any. |
| 293 PositionalRule _visitPositional(SourceVisitor visitor) { | 342 PositionalRule _visitPositional(SourceVisitor visitor) { |
| 294 if (_positional.isEmpty) return null; | 343 if (_positional.isEmpty) return null; |
| 295 | 344 |
| 296 // Allow splitting after "(". | 345 // Allow splitting after "(". |
| 297 var rule; | 346 // Only count the collections in the positional rule. |
| 298 if (_positional.length == 1) { | 347 var leadingCollections = math.min(_leadingCollections, _positional.length); |
| 299 rule = new SinglePositionalRule(_collectionRule, | 348 var trailingCollections = math.max(_trailingCollections - _named.length, 0); |
| 300 splitsOnInnerRules: _allArguments.length > 1 && | 349 var rule = new PositionalRule( |
| 301 !_collections.containsKey(_positional.first)); | 350 _collectionRule, leadingCollections, trailingCollections); |
| 302 } else { | 351 _visitArguments(visitor, _positional, rule); |
| 303 // Only count the collections in the positional rule. | |
| 304 var leadingCollections = | |
| 305 math.min(_leadingCollections, _positional.length); | |
| 306 var trailingCollections = | |
| 307 math.max(_trailingCollections - _named.length, 0); | |
| 308 rule = new MultiplePositionalRule( | |
| 309 _collectionRule, leadingCollections, trailingCollections); | |
| 310 } | |
| 311 | 352 |
| 312 _visitArguments(visitor, _positional, rule); | |
| 313 return rule; | 353 return rule; |
| 314 } | 354 } |
| 315 | 355 |
| 316 /// Writes the named arguments, if any. | 356 /// Writes the named arguments, if any. |
| 317 void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) { | 357 void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) { |
| 318 if (_named.isEmpty) return; | 358 if (_named.isEmpty) return; |
| 319 | 359 |
| 320 // Only count the collections in the named rule. | 360 // Only count the collections in the named rule. |
| 321 var leadingCollections = | 361 var leadingCollections = |
| 322 math.max(_leadingCollections - _positional.length, 0); | 362 math.max(_leadingCollections - _positional.length, 0); |
| 323 var trailingCollections = math.min(_trailingCollections, _named.length); | 363 var trailingCollections = math.min(_trailingCollections, _named.length); |
| 324 var namedRule = | 364 var namedRule = |
| 325 new NamedRule(_collectionRule, leadingCollections, trailingCollections); | 365 new NamedRule(_collectionRule, leadingCollections, trailingCollections); |
| 326 | 366 |
| 327 // Let the positional args force the named ones to split. | 367 // Let the positional args force the named ones to split. |
| 328 if (positionalRule != null) { | 368 if (positionalRule != null) { |
| 329 positionalRule.setNamedArgsRule(namedRule); | 369 positionalRule.setNamedArgsRule(namedRule); |
| 330 } | 370 } |
| 331 | 371 |
| 332 _visitArguments(visitor, _named, namedRule); | 372 _visitArguments(visitor, _named, namedRule); |
| 333 } | 373 } |
| 334 | 374 |
| 335 void _visitArguments( | 375 void _visitArguments( |
| 336 SourceVisitor visitor, List<Expression> arguments, ArgumentRule rule) { | 376 SourceVisitor visitor, List<Expression> arguments, ArgumentRule rule) { |
| 337 visitor.builder.startRule(rule); | 377 visitor.builder.startRule(rule); |
| 338 | 378 |
| 339 // Split before the first argument. | 379 // Split before the first argument. |
| 340 _previousSplit = | 380 _previousSplit = |
| 341 visitor.builder.split(space: !_isFirstArgument(arguments.first)); | 381 visitor.builder.split(space: arguments.first != _allArguments.first); |
| 342 rule.beforeArgument(_previousSplit); | 382 rule.beforeArgument(_previousSplit); |
| 343 | 383 |
| 344 // Try to not split the positional arguments. | 384 // Try to not split the positional arguments. |
| 345 if (arguments == _positional) { | 385 if (arguments == _positional) { |
| 346 visitor.builder.startSpan(Cost.positionalArguments); | 386 visitor.builder.startSpan(Cost.positionalArguments); |
| 347 } | 387 } |
| 348 | 388 |
| 349 for (var argument in arguments) { | 389 for (var argument in arguments) { |
| 350 _visitArgument(visitor, rule, argument); | 390 _visitArgument(visitor, rule, argument); |
| 351 | 391 |
| 352 // Write the split. | 392 // Write the split. |
| 353 if (argument != arguments.last) { | 393 if (argument != arguments.last) { |
| 354 _previousSplit = visitor.split(); | 394 _previousSplit = visitor.split(); |
| 355 rule.beforeArgument(_previousSplit); | 395 rule.beforeArgument(_previousSplit); |
| 356 } | 396 } |
| 357 } | 397 } |
| 358 | 398 |
| 359 if (arguments == _positional) visitor.builder.endSpan(); | 399 if (arguments == _positional) visitor.builder.endSpan(); |
| 360 | 400 |
| 361 visitor.builder.endRule(); | 401 visitor.builder.endRule(); |
| 362 } | 402 } |
| 363 | 403 |
| 364 void _visitArgument( | 404 void _visitArgument( |
| 365 SourceVisitor visitor, ArgumentRule rule, Expression argument) { | 405 SourceVisitor visitor, ArgumentRule rule, Expression argument) { |
| 366 // If we're about to write a collection argument, handle it specially. | 406 // If we're about to write a collection argument, handle it specially. |
| 367 if (_collections.containsKey(argument)) { | 407 if (_collections.containsKey(argument)) { |
| 368 if (rule != null) rule.beforeCollection(); | 408 rule.disableSplitOnInnerRules(); |
| 369 | 409 |
| 370 // Tell it to use the rule we've already created. | 410 // Tell it to use the rule we've already created. |
| 371 visitor.beforeCollection(_collections[argument], this); | 411 visitor.beforeCollection(_collections[argument], this); |
| 372 } else if (_hasMultipleArguments) { | 412 } else if (_allArguments.length > 1) { |
| 373 // Edge case: If there is just a single argument, don't bump the nesting. | 413 // Edge case: Only bump the nesting if there are multiple arguments. This |
| 374 // This lets us avoid spurious indentation in cases like: | 414 // lets us avoid spurious indentation in cases like: |
| 375 // | 415 // |
| 376 // function(function(() { | 416 // function(function(() { |
| 377 // body; | 417 // body; |
| 378 // })); | 418 // })); |
| 379 visitor.builder.startBlockArgumentNesting(); | 419 visitor.builder.startBlockArgumentNesting(); |
| 420 } else if (argument is! NamedExpression) { |
| 421 // Edge case: Likewise, don't force the argument to split if there is |
| 422 // only a single positional one, like: |
| 423 // |
| 424 // outer(inner( |
| 425 // longArgument)); |
| 426 rule.disableSplitOnInnerRules(); |
| 380 } | 427 } |
| 381 | 428 |
| 382 visitor.visit(argument); | 429 if (argument is NamedExpression) { |
| 430 visitor.visitNamedArgument(argument, rule as NamedRule); |
| 431 } else { |
| 432 visitor.visit(argument); |
| 433 } |
| 383 | 434 |
| 384 if (_collections.containsKey(argument)) { | 435 if (_collections.containsKey(argument)) { |
| 385 if (rule != null) rule.afterCollection(); | 436 rule.enableSplitOnInnerRules(); |
| 386 } else if (_hasMultipleArguments) { | 437 } else if (_allArguments.length > 1) { |
| 387 visitor.builder.endBlockArgumentNesting(); | 438 visitor.builder.endBlockArgumentNesting(); |
| 439 } else if (argument is! NamedExpression) { |
| 440 rule.enableSplitOnInnerRules(); |
| 388 } | 441 } |
| 389 | 442 |
| 390 // Write the trailing comma. | 443 // Write the following comma. |
| 391 if (!_isLastArgument(argument)) { | 444 if (argument.endToken.next.type == TokenType.COMMA) { |
| 392 visitor.token(argument.endToken.next); | 445 visitor.token(argument.endToken.next); |
| 393 } | 446 } |
| 394 } | 447 } |
| 395 | 448 |
| 396 bool _isFirstArgument(Expression argument) => argument == _allArguments.first; | |
| 397 | |
| 398 bool _isLastArgument(Expression argument) => argument == _allArguments.last; | |
| 399 | |
| 400 /// Returns the token for the left bracket if [expression] denotes a | 449 /// Returns the token for the left bracket if [expression] denotes a |
| 401 /// collection literal argument. | 450 /// collection literal argument. |
| 402 /// | 451 /// |
| 403 /// Similar to block functions, collection arguments can get special | 452 /// Similar to block functions, collection arguments can get special |
| 404 /// indentation to make them look more statement-like. | 453 /// indentation to make them look more statement-like. |
| 405 static Token _getCollectionBracket(Expression expression) { | 454 static Token _getCollectionBracket(Expression expression) { |
| 406 if (expression is NamedExpression) { | 455 if (expression is NamedExpression) { |
| 407 expression = (expression as NamedExpression).expression; | 456 expression = (expression as NamedExpression).expression; |
| 408 } | 457 } |
| 409 | 458 |
| 410 // TODO(rnystrom): Should we step into parenthesized expressions? | 459 // TODO(rnystrom): Should we step into parenthesized expressions? |
| 411 | 460 |
| 412 if (expression is ListLiteral) return expression.leftBracket; | 461 if (expression is ListLiteral) return expression.leftBracket; |
| 413 if (expression is MapLiteral) return expression.leftBracket; | 462 if (expression is MapLiteral) return expression.leftBracket; |
| 414 | 463 |
| 415 // Not a collection literal. | 464 // Not a collection literal. |
| 416 return null; | 465 return null; |
| 417 } | 466 } |
| 418 } | 467 } |
| OLD | NEW |