| 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'; |
| 11 import 'rule/rule.dart'; | 11 import 'rule/rule.dart'; |
| 12 import 'source_visitor.dart'; | 12 import 'source_visitor.dart'; |
| 13 | 13 |
| 14 /// Helper class for [SourceVisitor] that handles visiting and writing an | 14 /// Helper class for [SourceVisitor] that handles visiting and writing an |
| 15 /// [ArgumentList], including all of the special code needed to handle block | 15 /// [ArgumentList], including all of the special code needed to handle function |
| 16 /// arguments. | 16 /// and collection arguments. |
| 17 class ArgumentListVisitor { | 17 class ArgumentListVisitor { |
| 18 final SourceVisitor _visitor; | 18 final SourceVisitor _visitor; |
| 19 | 19 |
| 20 final ArgumentList _node; | 20 final ArgumentList _node; |
| 21 | 21 |
| 22 /// The positional arguments, in order. | 22 /// The normal arguments preceding any block function arguments. |
| 23 final List<Expression> _positional; | 23 final ArgumentSublist _arguments; |
| 24 | 24 |
| 25 /// The named arguments, in order. | 25 /// The contiguous list of block function arguments, if any. |
| 26 final List<Expression> _named; | 26 /// |
| 27 /// Otherwise, this is `null`. |
| 28 final List<Expression> _functions; |
| 27 | 29 |
| 28 /// The set of arguments that are valid block literals. | 30 /// If there are block function arguments, this is the arguments after them. |
| 29 final Set<Expression> _blockArguments; | |
| 30 | |
| 31 /// The number of leading block arguments. | |
| 32 /// | 31 /// |
| 33 /// If all arguments are block arguments, this counts them. | 32 /// Otherwise, this is `null`. |
| 34 final int _leadingBlockArguments; | 33 final ArgumentSublist _argumentsAfterFunctions; |
| 35 | |
| 36 /// The number of trailing block arguments. | |
| 37 /// | |
| 38 /// If all arguments are block arguments, this is zero. | |
| 39 final int _trailingBlockArguments; | |
| 40 | |
| 41 /// The rule used to split the bodies of all of the block arguments. | |
| 42 Rule get _blockArgumentRule { | |
| 43 // Lazy initialize. | |
| 44 if (_blockRule == null && _blockArguments.isNotEmpty) { | |
| 45 _blockRule = new SimpleRule(cost: Cost.splitBlocks); | |
| 46 } | |
| 47 | |
| 48 return _blockRule; | |
| 49 } | |
| 50 | |
| 51 Rule _blockRule; | |
| 52 | 34 |
| 53 /// Returns `true` if there is only a single positional argument. | 35 /// Returns `true` if there is only a single positional argument. |
| 54 bool get _isSingle => _positional.length == 1 && _named.isEmpty; | 36 bool get _isSingle => |
| 37 _node.arguments.length == 1 && _node.arguments.single is! NamedExpression; |
| 55 | 38 |
| 56 /// Whether this argument list has any block arguments that are functions. | 39 /// Whether this argument list has any collection or block function arguments. |
| 57 bool get hasFunctionBlockArguments => _blockArguments.any(_isBlockFunction); | 40 bool get hasBlockArguments => |
| 58 | 41 _arguments._collections.isNotEmpty || _functions != null; |
| 59 bool get hasBlockArguments => _blockArguments.isNotEmpty; | |
| 60 | 42 |
| 61 /// Whether this argument list should force the containing method chain to | 43 /// Whether this argument list should force the containing method chain to |
| 62 /// add a level of block nesting. | 44 /// add a level of block nesting. |
| 63 bool get nestMethodArguments { | 45 bool get nestMethodArguments { |
| 64 // If there are block arguments, we don't want the method to force them to | 46 // If there are block arguments, we don't want the method to force them to |
| 65 // the right. | 47 // the right. |
| 66 if (_blockArguments.isNotEmpty) return false; | 48 if (hasBlockArguments) return false; |
| 67 | 49 |
| 68 // Corner case: If there is just a single argument, don't bump the nesting. | 50 // Corner case: If there is just a single argument, don't bump the nesting. |
| 69 // This lets us avoid spurious indentation in cases like: | 51 // This lets us avoid spurious indentation in cases like: |
| 70 // | 52 // |
| 71 // object.method(function(() { | 53 // object.method(function(() { |
| 72 // body; | 54 // body; |
| 73 // })); | 55 // })); |
| 74 return _node.arguments.length > 1; | 56 return _node.arguments.length > 1; |
| 75 } | 57 } |
| 76 | 58 |
| 77 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) { | 59 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) { |
| 78 // Assumes named arguments follow all positional ones. | 60 // Look for a single contiguous range of block function arguments. |
| 79 var positional = | 61 var functionsStart; |
| 80 node.arguments.takeWhile((arg) => arg is! NamedExpression).toList(); | 62 var functionsEnd; |
| 81 var named = node.arguments.skip(positional.length).toList(); | |
| 82 | 63 |
| 83 var blocks = node.arguments.where(_isBlockArgument).toSet(); | 64 for (var i = 0; i < node.arguments.length; i++) { |
| 65 var argument = node.arguments[i]; |
| 66 if (_isBlockFunction(argument)) { |
| 67 if (functionsStart == null) functionsStart = i; |
| 84 | 68 |
| 85 // Count the leading arguments that are block literals. | 69 // The functions must be one contiguous section. |
| 86 var leadingBlocks = 0; | 70 if (functionsEnd != null && functionsEnd != i) { |
| 87 for (var argument in node.arguments) { | 71 functionsStart = null; |
| 88 if (!blocks.contains(argument)) break; | 72 functionsEnd = null; |
| 89 leadingBlocks++; | 73 break; |
| 90 } | 74 } |
| 91 | 75 |
| 92 // Count the trailing arguments that are block literals. | 76 functionsEnd = i + 1; |
| 93 var trailingBlocks = 0; | |
| 94 if (leadingBlocks != node.arguments.length) { | |
| 95 for (var argument in node.arguments.reversed) { | |
| 96 if (!blocks.contains(argument)) break; | |
| 97 trailingBlocks++; | |
| 98 } | 77 } |
| 99 } | 78 } |
| 100 | 79 |
| 101 // If only some of the named arguments are blocks, treat none of them as | 80 if (functionsStart == null) { |
| 102 // blocks. Avoids cases like: | 81 // No functions, so there is just a single argument list. |
| 103 // | 82 return new ArgumentListVisitor._(visitor, node, |
| 104 // function( | 83 new ArgumentSublist(node.arguments, node.arguments), null, null); |
| 105 // a: arg, | |
| 106 // b: [ | |
| 107 // ... | |
| 108 // ]); | |
| 109 if (trailingBlocks < named.length) trailingBlocks = 0; | |
| 110 | |
| 111 // Blocks must all be a prefix or suffix of the argument list (and not | |
| 112 // both). | |
| 113 if (leadingBlocks != blocks.length) leadingBlocks = 0; | |
| 114 if (trailingBlocks != blocks.length) trailingBlocks = 0; | |
| 115 | |
| 116 // Ignore any blocks in the middle of the argument list. | |
| 117 if (leadingBlocks == 0 && trailingBlocks == 0) { | |
| 118 blocks.clear(); | |
| 119 } | 84 } |
| 120 | 85 |
| 121 return new ArgumentListVisitor._(visitor, node, positional, named, blocks, | 86 // Split the arguments into two independent argument lists with the |
| 122 leadingBlocks, trailingBlocks); | 87 // functions in the middle. |
| 88 var argumentsBefore = node.arguments.take(functionsStart).toList(); |
| 89 var functions = node.arguments.sublist(functionsStart, functionsEnd); |
| 90 var argumentsAfter = node.arguments.skip(functionsEnd).toList(); |
| 91 |
| 92 return new ArgumentListVisitor._( |
| 93 visitor, |
| 94 node, |
| 95 new ArgumentSublist(node.arguments, argumentsBefore), |
| 96 functions, |
| 97 new ArgumentSublist(node.arguments, argumentsAfter)); |
| 123 } | 98 } |
| 124 | 99 |
| 125 ArgumentListVisitor._( | 100 ArgumentListVisitor._( |
| 126 this._visitor, | 101 this._visitor, |
| 127 this._node, | 102 this._node, |
| 128 this._positional, | 103 this._arguments, |
| 129 this._named, | 104 this._functions, |
| 130 this._blockArguments, | 105 this._argumentsAfterFunctions); |
| 131 this._leadingBlockArguments, | |
| 132 this._trailingBlockArguments); | |
| 133 | 106 |
| 134 /// Builds chunks for the call chain. | 107 /// Builds chunks for the call chain. |
| 135 void visit() { | 108 void visit() { |
| 136 // If there is just one positional argument, it tends to look weird to | 109 // If there is just one positional argument, it tends to look weird to |
| 137 // split before it, so try not to. | 110 // split before it, so try not to. |
| 138 if (_isSingle) _visitor.builder.startSpan(); | 111 if (_isSingle) _visitor.builder.startSpan(); |
| 139 | 112 |
| 140 // Nest around the parentheses in case there are comments before or after | 113 // Nest around the parentheses in case there are comments before or after |
| 141 // them. | 114 // them. |
| 142 _visitor.builder.nestExpression(); | 115 _visitor.builder.nestExpression(); |
| 143 _visitor.builder.startSpan(); | 116 _visitor.builder.startSpan(); |
| 144 _visitor.token(_node.leftParenthesis); | 117 _visitor.token(_node.leftParenthesis); |
| 145 | 118 |
| 146 var rule = _writePositional(); | 119 _arguments.visit(_visitor); |
| 147 _writeNamed(rule); | 120 |
| 121 _visitor.builder.endSpan(); |
| 122 |
| 123 if (_functions != null) { |
| 124 // TODO(rnystrom): It might look better to treat the parameter list of the |
| 125 // first function as if it were an argument in the preceding argument list |
| 126 // instead of just having this little solo split here. That would try to |
| 127 // keep the parameter list with other arguments when possible, and, I |
| 128 // think, generally look nicer. |
| 129 if (_functions.first == _node.arguments.first) { |
| 130 _visitor.soloZeroSplit(); |
| 131 } else { |
| 132 _visitor.soloSplit(); |
| 133 } |
| 134 |
| 135 for (var argument in _functions) { |
| 136 if (argument != _functions.first) _visitor.space(); |
| 137 |
| 138 _visitor.visit(argument); |
| 139 |
| 140 // Write the trailing comma. |
| 141 if (argument != _node.arguments.last) { |
| 142 _visitor.token(argument.endToken.next); |
| 143 } |
| 144 } |
| 145 |
| 146 _visitor.builder.startSpan(); |
| 147 _argumentsAfterFunctions.visit(_visitor); |
| 148 _visitor.builder.endSpan(); |
| 149 } |
| 148 | 150 |
| 149 _visitor.token(_node.rightParenthesis); | 151 _visitor.token(_node.rightParenthesis); |
| 150 | 152 |
| 151 _visitor.builder.endSpan(); | |
| 152 _visitor.builder.unnest(); | 153 _visitor.builder.unnest(); |
| 153 | 154 |
| 154 if (_isSingle) _visitor.builder.endSpan(); | 155 if (_isSingle) _visitor.builder.endSpan(); |
| 155 } | 156 } |
| 156 | 157 |
| 158 /// Returns `true` if [expression] is a [FunctionExpression] with a block |
| 159 /// body. |
| 160 static bool _isBlockFunction(Expression expression) { |
| 161 if (expression is NamedExpression) { |
| 162 expression = (expression as NamedExpression).expression; |
| 163 } |
| 164 |
| 165 // Curly body functions are. |
| 166 if (expression is! FunctionExpression) return false; |
| 167 var function = expression as FunctionExpression; |
| 168 return function.body is BlockFunctionBody; |
| 169 } |
| 170 } |
| 171 |
| 172 /// A range of arguments from a complete argument list. |
| 173 /// |
| 174 /// 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 |
| 176 /// before and after the functions are treated as separate independent lists. |
| 177 /// In that case, there will be two of these. |
| 178 class ArgumentSublist { |
| 179 /// The full argument list from the AST. |
| 180 final List<Expression> _allArguments; |
| 181 |
| 182 /// The positional arguments, in order. |
| 183 final List<Expression> _positional; |
| 184 |
| 185 /// The named arguments, in order. |
| 186 final List<Expression> _named; |
| 187 |
| 188 /// The arguments that are collection literals that get special formatting. |
| 189 final Set<Expression> _collections; |
| 190 |
| 191 /// The number of leading collections. |
| 192 /// |
| 193 /// If all arguments are collections, this counts them. |
| 194 final int _leadingCollections; |
| 195 |
| 196 /// The number of trailing collections. |
| 197 /// |
| 198 /// If all arguments are collections, this is zero. |
| 199 final int _trailingCollections; |
| 200 |
| 201 /// The rule used to split the bodies of all of the collection arguments. |
| 202 Rule get _collectionRule { |
| 203 // Lazy initialize. |
| 204 if (_collectionRuleField == null && _collections.isNotEmpty) { |
| 205 _collectionRuleField = new SimpleRule(cost: Cost.splitCollections); |
| 206 } |
| 207 |
| 208 return _collectionRuleField; |
| 209 } |
| 210 |
| 211 Rule _collectionRuleField; |
| 212 |
| 213 bool get _hasMultipleArguments => _positional.length + _named.length > 1; |
| 214 |
| 215 factory ArgumentSublist( |
| 216 List<Expression> allArguments, List<Expression> arguments) { |
| 217 // Assumes named arguments follow all positional ones. |
| 218 var positional = |
| 219 arguments.takeWhile((arg) => arg is! NamedExpression).toList(); |
| 220 var named = arguments.skip(positional.length).toList(); |
| 221 |
| 222 var collections = arguments.where(_isCollectionArgument).toSet(); |
| 223 |
| 224 // Count the leading arguments that are collection literals. |
| 225 var leadingCollections = 0; |
| 226 for (var argument in arguments) { |
| 227 if (!collections.contains(argument)) break; |
| 228 leadingCollections++; |
| 229 } |
| 230 |
| 231 // Count the trailing arguments that are collection literals. |
| 232 var trailingCollections = 0; |
| 233 if (leadingCollections != arguments.length) { |
| 234 for (var argument in arguments.reversed) { |
| 235 if (!collections.contains(argument)) break; |
| 236 trailingCollections++; |
| 237 } |
| 238 } |
| 239 |
| 240 // If only some of the named arguments are collections, treat none of them |
| 241 // specially. Avoids cases like: |
| 242 // |
| 243 // function( |
| 244 // a: arg, |
| 245 // b: [ |
| 246 // ... |
| 247 // ]); |
| 248 if (trailingCollections < named.length) trailingCollections = 0; |
| 249 |
| 250 // Collections must all be a prefix or suffix of the argument list (and not |
| 251 // both). |
| 252 if (leadingCollections != collections.length) leadingCollections = 0; |
| 253 if (trailingCollections != collections.length) trailingCollections = 0; |
| 254 |
| 255 // Ignore any collections in the middle of the argument list. |
| 256 if (leadingCollections == 0 && trailingCollections == 0) { |
| 257 collections.clear(); |
| 258 } |
| 259 |
| 260 return new ArgumentSublist._(allArguments, positional, named, collections, |
| 261 leadingCollections, trailingCollections); |
| 262 } |
| 263 |
| 264 ArgumentSublist._(this._allArguments, this._positional, this._named, |
| 265 this._collections, this._leadingCollections, this._trailingCollections); |
| 266 |
| 267 void visit(SourceVisitor visitor) { |
| 268 var rule = _visitPositional(visitor); |
| 269 _visitNamed(visitor, rule); |
| 270 } |
| 271 |
| 157 /// Writes the positional arguments, if any. | 272 /// Writes the positional arguments, if any. |
| 158 PositionalRule _writePositional() { | 273 PositionalRule _visitPositional(SourceVisitor visitor) { |
| 159 if (_positional.isEmpty) return null; | 274 if (_positional.isEmpty) return null; |
| 160 | 275 |
| 161 // Allow splitting after "(". | 276 // Allow splitting after "(". |
| 162 var rule; | 277 var rule; |
| 163 if (_positional.length == 1) { | 278 if (_positional.length == 1) { |
| 164 rule = new SinglePositionalRule(_blockArgumentRule); | 279 rule = new SinglePositionalRule(_collectionRule, |
| 280 splitsOnInnerRules: _allArguments.length > 1 && |
| 281 !_isCollectionArgument(_positional.first)); |
| 165 } else { | 282 } else { |
| 166 // Only count the positional bodies in the positional rule. | 283 // Only count the positional bodies in the positional rule. |
| 167 var leadingPositional = _leadingBlockArguments; | 284 var leadingPositional = _leadingCollections; |
| 168 if (_leadingBlockArguments == _node.arguments.length) { | 285 if (_leadingCollections == _positional.length + _named.length) { |
| 169 leadingPositional -= _named.length; | 286 leadingPositional -= _named.length; |
| 170 } | 287 } |
| 171 | 288 |
| 172 var trailingPositional = _trailingBlockArguments - _named.length; | 289 var trailingPositional = _trailingCollections - _named.length; |
| 173 rule = new MultiplePositionalRule( | 290 rule = new MultiplePositionalRule( |
| 174 _blockArgumentRule, leadingPositional, trailingPositional); | 291 _collectionRule, leadingPositional, trailingPositional); |
| 175 } | 292 } |
| 176 | 293 |
| 177 _visitor.builder.startRule(rule); | 294 visitor.builder.startRule(rule); |
| 178 rule.beforeArgument(_visitor.zeroSplit()); | 295 |
| 296 var chunk; |
| 297 if (_isFirstArgument(_positional.first)) { |
| 298 chunk = visitor.zeroSplit(); |
| 299 } else { |
| 300 chunk = visitor.split(); |
| 301 } |
| 302 rule.beforeArgument(chunk); |
| 179 | 303 |
| 180 // Try to not split the arguments. | 304 // Try to not split the arguments. |
| 181 _visitor.builder.startSpan(Cost.positionalArguments); | 305 visitor.builder.startSpan(Cost.positionalArguments); |
| 182 | 306 |
| 183 for (var argument in _positional) { | 307 for (var argument in _positional) { |
| 184 _writeArgument(rule, argument); | 308 _visitArgument(visitor, rule, argument); |
| 185 | 309 |
| 186 // Positional arguments split independently. | 310 // Positional arguments split independently. |
| 187 if (argument != _positional.last) { | 311 if (argument != _positional.last) { |
| 188 rule.beforeArgument(_visitor.split()); | 312 rule.beforeArgument(visitor.split()); |
| 189 } | 313 } |
| 190 } | 314 } |
| 191 | 315 |
| 192 _visitor.builder.endSpan(); | 316 visitor.builder.endSpan(); |
| 193 _visitor.builder.endRule(); | 317 visitor.builder.endRule(); |
| 194 | 318 |
| 195 return rule; | 319 return rule; |
| 196 } | 320 } |
| 197 | 321 |
| 198 /// Writes the named arguments, if any. | 322 /// Writes the named arguments, if any. |
| 199 void _writeNamed(PositionalRule rule) { | 323 void _visitNamed(SourceVisitor visitor, PositionalRule rule) { |
| 200 if (_named.isEmpty) return; | 324 if (_named.isEmpty) return; |
| 201 | 325 |
| 202 var positionalRule = rule; | 326 var positionalRule = rule; |
| 203 var namedRule = new NamedRule(_blockArgumentRule); | 327 var namedRule = new NamedRule(_collectionRule); |
| 204 _visitor.builder.startRule(namedRule); | 328 visitor.builder.startRule(namedRule); |
| 205 | 329 |
| 206 // Let the positional args force the named ones to split. | 330 // Let the positional args force the named ones to split. |
| 207 if (positionalRule != null) { | 331 if (positionalRule != null) { |
| 208 positionalRule.setNamedArgsRule(namedRule); | 332 positionalRule.setNamedArgsRule(namedRule); |
| 209 } | 333 } |
| 210 | 334 |
| 211 // Split before the first named argument. | 335 // Split before the first named argument. |
| 212 namedRule | 336 namedRule.beforeArguments(visitor.builder.split( |
| 213 .beforeArguments(_visitor.builder.split(space: _positional.isNotEmpty)); | 337 space: !_isFirstArgument(_named.first))); |
| 214 | 338 |
| 215 for (var argument in _named) { | 339 for (var argument in _named) { |
| 216 _writeArgument(namedRule, argument); | 340 _visitArgument(visitor, namedRule, argument); |
| 217 | 341 |
| 218 // Write the split. | 342 // Write the split. |
| 219 if (argument != _named.last) _visitor.split(); | 343 if (argument != _named.last) visitor.split(); |
| 220 } | 344 } |
| 221 | 345 |
| 222 _visitor.builder.endRule(); | 346 visitor.builder.endRule(); |
| 223 } | 347 } |
| 224 | 348 |
| 225 void _writeArgument(ArgumentRule rule, Expression argument) { | 349 void _visitArgument(SourceVisitor visitor, ArgumentRule rule, Expression argum
ent) { |
| 226 // If we're about to write a block argument, handle it specially. | 350 // If we're about to write a collection argument, handle it specially. |
| 227 if (_blockArguments.contains(argument)) { | 351 if (_collections.contains(argument)) { |
| 228 if (rule != null) rule.beforeBlockArgument(); | 352 if (rule != null) rule.beforeCollection(); |
| 229 | 353 |
| 230 // Tell it to use the rule we've already created. | 354 // Tell it to use the rule we've already created. |
| 231 _visitor.setNextLiteralBodyRule(_blockArgumentRule); | 355 visitor.setNextLiteralBodyRule(_collectionRule); |
| 232 } else if (_node.arguments.length > 1) { | 356 } else if (_hasMultipleArguments) { |
| 233 // Corner case: If there is just a single argument, don't bump the | 357 // Corner case: If there is just a single argument, don't bump the |
| 234 // nesting. This lets us avoid spurious indentation in cases like: | 358 // nesting. This lets us avoid spurious indentation in cases like: |
| 235 // | 359 // |
| 236 // function(function(() { | 360 // function(function(() { |
| 237 // body; | 361 // body; |
| 238 // })); | 362 // })); |
| 239 _visitor.builder.startBlockArgumentNesting(); | 363 visitor.builder.startBlockArgumentNesting(); |
| 240 } | 364 } |
| 241 | 365 |
| 242 _visitor.visit(argument); | 366 visitor.visit(argument); |
| 243 | 367 |
| 244 if (_blockArguments.contains(argument)) { | 368 if (_collections.contains(argument)) { |
| 245 if (rule != null) rule.afterBlockArgument(); | 369 if (rule != null) rule.afterCollection(); |
| 246 } else if (_node.arguments.length > 1) { | 370 } else if (_hasMultipleArguments) { |
| 247 _visitor.builder.endBlockArgumentNesting(); | 371 visitor.builder.endBlockArgumentNesting(); |
| 248 } | 372 } |
| 249 | 373 |
| 250 // Write the trailing comma. | 374 // Write the trailing comma. |
| 251 if (argument != _node.arguments.last) { | 375 if (!_isLastArgument(argument)) { |
| 252 _visitor.token(argument.endToken.next); | 376 visitor.token(argument.endToken.next); |
| 253 } | 377 } |
| 254 } | 378 } |
| 255 | 379 |
| 256 /// Returns true if [expression] denotes a block argument. | 380 bool _isFirstArgument(Expression argument) => argument == _allArguments.first; |
| 381 |
| 382 bool _isLastArgument(Expression argument) => argument == _allArguments.last; |
| 383 |
| 384 /// Returns true if [expression] denotes a collection literal argument. |
| 257 /// | 385 /// |
| 258 /// That means a collection literal or a function expression with a block | 386 /// Similar to block functions, collection arguments can get special |
| 259 /// body. Block arguments can get special indentation to make them look more | 387 /// indentation to make them look more statement-like. |
| 260 /// statement-like. | 388 static bool _isCollectionArgument(Expression expression) { |
| 261 static bool _isBlockArgument(Expression expression) { | |
| 262 if (expression is NamedExpression) { | 389 if (expression is NamedExpression) { |
| 263 expression = (expression as NamedExpression).expression; | 390 expression = (expression as NamedExpression).expression; |
| 264 } | 391 } |
| 265 | 392 |
| 266 // TODO(rnystrom): Should we step into parenthesized expressions? | 393 // TODO(rnystrom): Should we step into parenthesized expressions? |
| 267 | 394 |
| 268 // Collections are bodies. | 395 return expression is ListLiteral || expression is MapLiteral; |
| 269 if (expression is ListLiteral) return true; | |
| 270 if (expression is MapLiteral) return true; | |
| 271 | |
| 272 // Curly body functions are. | |
| 273 if (expression is! FunctionExpression) return false; | |
| 274 var function = expression as FunctionExpression; | |
| 275 return function.body is BlockFunctionBody; | |
| 276 } | 396 } |
| 277 | 397 } |
| 278 /// Returns `true` if [expression] is a [FunctionExpression] with a block | |
| 279 /// body. | |
| 280 static bool _isBlockFunction(Expression expression) { | |
| 281 if (expression is NamedExpression) { | |
| 282 expression = (expression as NamedExpression).expression; | |
| 283 } | |
| 284 | |
| 285 // Curly body functions are. | |
| 286 if (expression is! FunctionExpression) return false; | |
| 287 var function = expression as FunctionExpression; | |
| 288 return function.body is BlockFunctionBody; | |
| 289 } | |
| 290 } | |
| OLD | NEW |