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

Side by Side Diff: packages/dart_style/lib/src/argument_list_visitor.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.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.argument_list_visitor; 5 library dart_style.src.argument_list_visitor;
6 6
7 import 'dart:math' as math;
8
7 import 'package:analyzer/analyzer.dart'; 9 import 'package:analyzer/analyzer.dart';
10 import 'package:analyzer/src/generated/scanner.dart';
8 11
9 import 'chunk.dart'; 12 import 'chunk.dart';
10 import 'rule/argument.dart'; 13 import 'rule/argument.dart';
11 import 'rule/rule.dart'; 14 import 'rule/rule.dart';
12 import 'source_visitor.dart'; 15 import 'source_visitor.dart';
13 16
14 /// Helper class for [SourceVisitor] that handles visiting and writing an 17 /// Helper class for [SourceVisitor] that handles visiting and writing an
15 /// [ArgumentList], including all of the special code needed to handle function 18 /// [ArgumentList], including all of the special code needed to handle function
16 /// and collection arguments. 19 /// and collection arguments.
17 class ArgumentListVisitor { 20 class ArgumentListVisitor {
(...skipping 12 matching lines...) Expand all
30 /// If there are block function arguments, this is the arguments after them. 33 /// If there are block function arguments, this is the arguments after them.
31 /// 34 ///
32 /// Otherwise, this is `null`. 35 /// Otherwise, this is `null`.
33 final ArgumentSublist _argumentsAfterFunctions; 36 final ArgumentSublist _argumentsAfterFunctions;
34 37
35 /// Returns `true` if there is only a single positional argument. 38 /// Returns `true` if there is only a single positional argument.
36 bool get _isSingle => 39 bool get _isSingle =>
37 _node.arguments.length == 1 && _node.arguments.single is! NamedExpression; 40 _node.arguments.length == 1 && _node.arguments.single is! NamedExpression;
38 41
39 /// Whether this argument list has any collection or block function arguments. 42 /// Whether this argument list has any collection or block function arguments.
43 // 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
45 // actually split which can lead to a method chain that's allowed to break
46 // where it shouldn't.
40 bool get hasBlockArguments => 47 bool get hasBlockArguments =>
41 _arguments._collections.isNotEmpty || _functions != null; 48 _arguments._collections.isNotEmpty || _functions != null;
42 49
43 /// Whether this argument list should force the containing method chain to
44 /// add a level of block nesting.
45 bool get nestMethodArguments {
46 // If there are block arguments, we don't want the method to force them to
47 // the right.
48 if (hasBlockArguments) return false;
49
50 // Corner case: If there is just a single argument, don't bump the nesting.
51 // This lets us avoid spurious indentation in cases like:
52 //
53 // object.method(function(() {
54 // body;
55 // }));
56 return _node.arguments.length > 1;
57 }
58
59 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) { 50 factory ArgumentListVisitor(SourceVisitor visitor, ArgumentList node) {
60 // Look for a single contiguous range of block function arguments. 51 // Look for a single contiguous range of block function arguments.
61 var functionsStart; 52 var functionsStart;
62 var functionsEnd; 53 var functionsEnd;
63 54
64 for (var i = 0; i < node.arguments.length; i++) { 55 for (var i = 0; i < node.arguments.length; i++) {
65 var argument = node.arguments[i]; 56 var argument = node.arguments[i];
66 if (_isBlockFunction(argument)) { 57 if (_isBlockFunction(argument)) {
67 if (functionsStart == null) functionsStart = i; 58 if (functionsStart == null) functionsStart = i;
68 59
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 _visitor.builder.endSpan(); 135 _visitor.builder.endSpan();
145 } 136 }
146 137
147 _visitor.token(_node.rightParenthesis); 138 _visitor.token(_node.rightParenthesis);
148 139
149 _visitor.builder.unnest(); 140 _visitor.builder.unnest();
150 141
151 if (_isSingle) _visitor.builder.endSpan(); 142 if (_isSingle) _visitor.builder.endSpan();
152 } 143 }
153 144
154 /// Returns `true` if [expression] is a [FunctionExpression] with a block 145 /// Returns `true` if [expression] is a [FunctionExpression] with a non-empty
155 /// body. 146 /// block body.
156 static bool _isBlockFunction(Expression expression) { 147 static bool _isBlockFunction(Expression expression) {
157 if (expression is NamedExpression) { 148 if (expression is NamedExpression) {
158 expression = (expression as NamedExpression).expression; 149 expression = (expression as NamedExpression).expression;
159 } 150 }
160 151
161 // Allow functions wrapped in dotted method calls like "a.b.c(() { ... })". 152 // Allow functions wrapped in dotted method calls like "a.b.c(() { ... })".
162 if (expression is MethodInvocation) { 153 if (expression is MethodInvocation) {
163 if (!_isValidWrappingTarget(expression.target)) return false; 154 if (!_isValidWrappingTarget(expression.target)) return false;
164 if (expression.argumentList.arguments.length != 1) return false; 155 if (expression.argumentList.arguments.length != 1) return false;
165 156
166 return _isBlockFunction(expression.argumentList.arguments.single); 157 return _isBlockFunction(expression.argumentList.arguments.single);
167 } 158 }
168 159
169 // Curly body functions are. 160 if (expression is InstanceCreationExpression) {
161 if (expression.argumentList.arguments.length != 1) return false;
162
163 return _isBlockFunction(expression.argumentList.arguments.single);
164 }
165
166 // Must be a function.
170 if (expression is! FunctionExpression) return false; 167 if (expression is! FunctionExpression) return false;
168
169 // With a curly body.
171 var function = expression as FunctionExpression; 170 var function = expression as FunctionExpression;
172 return function.body is BlockFunctionBody; 171 if (function.body is! BlockFunctionBody) return false;
172
173 // That isn't empty.
174 var body = function.body as BlockFunctionBody;
175 return body.block.statements.isNotEmpty ||
176 body.block.rightBracket.precedingComments != null;
173 } 177 }
174 178
175 /// Returns `true` if [expression] is a valid method invocation target for 179 /// Returns `true` if [expression] is a valid method invocation target for
176 /// an invocation that wraps a function literal argument. 180 /// an invocation that wraps a function literal argument.
177 static bool _isValidWrappingTarget(Expression expression) { 181 static bool _isValidWrappingTarget(Expression expression) {
178 // Allow bare function calls. 182 // Allow bare function calls.
179 if (expression == null) return true; 183 if (expression == null) return true;
180 184
181 // Allow property accesses. 185 // Allow property accesses.
182 while (expression is PropertyAccess) { 186 while (expression is PropertyAccess) {
(...skipping 16 matching lines...) Expand all
199 class ArgumentSublist { 203 class ArgumentSublist {
200 /// The full argument list from the AST. 204 /// The full argument list from the AST.
201 final List<Expression> _allArguments; 205 final List<Expression> _allArguments;
202 206
203 /// The positional arguments, in order. 207 /// The positional arguments, in order.
204 final List<Expression> _positional; 208 final List<Expression> _positional;
205 209
206 /// The named arguments, in order. 210 /// The named arguments, in order.
207 final List<Expression> _named; 211 final List<Expression> _named;
208 212
209 /// The arguments that are collection literals that get special formatting. 213 /// Maps each argument that is a collection literal that get special
210 final Set<Expression> _collections; 214 /// formatting to the token for the collection's open bracket.
215 final Map<Expression, Token> _collections;
211 216
212 /// The number of leading collections. 217 /// The number of leading collections.
213 /// 218 ///
214 /// If all arguments are collections, this counts them. 219 /// If all arguments are collections, this counts them.
215 final int _leadingCollections; 220 final int _leadingCollections;
216 221
217 /// The number of trailing collections. 222 /// The number of trailing collections.
218 /// 223 ///
219 /// If all arguments are collections, this is zero. 224 /// If all arguments are collections, this is zero.
220 final int _trailingCollections; 225 final int _trailingCollections;
221 226
222 /// The rule used to split the bodies of all of the collection arguments. 227 /// The rule used to split the bodies of all of the collection arguments.
223 Rule get _collectionRule { 228 Rule get collectionRule => _collectionRule;
224 // Lazy initialize. 229 Rule _collectionRule;
225 if (_collectionRuleField == null && _collections.isNotEmpty) {
226 _collectionRuleField = new SimpleRule(cost: Cost.splitCollections);
227 }
228 230
229 return _collectionRuleField; 231 /// The most recent chunk that split before an argument.
230 } 232 Chunk get previousSplit => _previousSplit;
231 233 Chunk _previousSplit;
232 Rule _collectionRuleField;
233 234
234 bool get _hasMultipleArguments => _positional.length + _named.length > 1; 235 bool get _hasMultipleArguments => _positional.length + _named.length > 1;
235 236
236 factory ArgumentSublist( 237 factory ArgumentSublist(
237 List<Expression> allArguments, List<Expression> arguments) { 238 List<Expression> allArguments, List<Expression> arguments) {
238 // Assumes named arguments follow all positional ones. 239 // Assumes named arguments follow all positional ones.
239 var positional = 240 var positional =
240 arguments.takeWhile((arg) => arg is! NamedExpression).toList(); 241 arguments.takeWhile((arg) => arg is! NamedExpression).toList();
241 var named = arguments.skip(positional.length).toList(); 242 var named = arguments.skip(positional.length).toList();
242 243
243 var collections = arguments.where(_isCollectionArgument).toSet(); 244 var collections = {};
245 for (var argument in arguments) {
246 var bracket = _getCollectionBracket(argument);
247 if (bracket != null) collections[argument] = bracket;
248 }
244 249
245 // Count the leading arguments that are collection literals. 250 // Count the leading arguments that are collection literals.
246 var leadingCollections = 0; 251 var leadingCollections = 0;
247 for (var argument in arguments) { 252 for (var argument in arguments) {
248 if (!collections.contains(argument)) break; 253 if (!collections.containsKey(argument)) break;
249 leadingCollections++; 254 leadingCollections++;
250 } 255 }
251 256
252 // Count the trailing arguments that are collection literals. 257 // Count the trailing arguments that are collection literals.
253 var trailingCollections = 0; 258 var trailingCollections = 0;
254 if (leadingCollections != arguments.length) { 259 if (leadingCollections != arguments.length) {
255 for (var argument in arguments.reversed) { 260 for (var argument in arguments.reversed) {
256 if (!collections.contains(argument)) break; 261 if (!collections.containsKey(argument)) break;
257 trailingCollections++; 262 trailingCollections++;
258 } 263 }
259 } 264 }
260 265
261 // If only some of the named arguments are collections, treat none of them
262 // specially. Avoids cases like:
263 //
264 // function(
265 // a: arg,
266 // b: [
267 // ...
268 // ]);
269 if (trailingCollections < named.length) trailingCollections = 0;
270
271 // Collections must all be a prefix or suffix of the argument list (and not 266 // Collections must all be a prefix or suffix of the argument list (and not
272 // both). 267 // both).
273 if (leadingCollections != collections.length) leadingCollections = 0; 268 if (leadingCollections != collections.length) leadingCollections = 0;
274 if (trailingCollections != collections.length) trailingCollections = 0; 269 if (trailingCollections != collections.length) trailingCollections = 0;
275 270
276 // Ignore any collections in the middle of the argument list. 271 // Ignore any collections in the middle of the argument list.
277 if (leadingCollections == 0 && trailingCollections == 0) { 272 if (leadingCollections == 0 && trailingCollections == 0) {
278 collections.clear(); 273 collections.clear();
279 } 274 }
280 275
281 return new ArgumentSublist._(allArguments, positional, named, collections, 276 return new ArgumentSublist._(allArguments, positional, named, collections,
282 leadingCollections, trailingCollections); 277 leadingCollections, trailingCollections);
283 } 278 }
284 279
285 ArgumentSublist._(this._allArguments, this._positional, this._named, 280 ArgumentSublist._(this._allArguments, this._positional, this._named,
286 this._collections, this._leadingCollections, this._trailingCollections); 281 this._collections, this._leadingCollections, this._trailingCollections);
287 282
288 void visit(SourceVisitor visitor) { 283 void visit(SourceVisitor visitor) {
284 if (_collections.isNotEmpty) {
285 _collectionRule = new Rule(Cost.splitCollections);
286 }
287
289 var rule = _visitPositional(visitor); 288 var rule = _visitPositional(visitor);
290 _visitNamed(visitor, rule); 289 _visitNamed(visitor, rule);
291 } 290 }
292 291
293 /// Writes the positional arguments, if any. 292 /// Writes the positional arguments, if any.
294 PositionalRule _visitPositional(SourceVisitor visitor) { 293 PositionalRule _visitPositional(SourceVisitor visitor) {
295 if (_positional.isEmpty) return null; 294 if (_positional.isEmpty) return null;
296 295
297 // Allow splitting after "(". 296 // Allow splitting after "(".
298 var rule; 297 var rule;
299 if (_positional.length == 1) { 298 if (_positional.length == 1) {
300 rule = new SinglePositionalRule(_collectionRule, 299 rule = new SinglePositionalRule(_collectionRule,
301 splitsOnInnerRules: _allArguments.length > 1 && 300 splitsOnInnerRules: _allArguments.length > 1 &&
302 !_isCollectionArgument(_positional.first)); 301 !_collections.containsKey(_positional.first));
303 } else { 302 } else {
304 // Only count the positional bodies in the positional rule. 303 // Only count the collections in the positional rule.
305 var leadingPositional = _leadingCollections; 304 var leadingCollections =
306 if (_leadingCollections == _positional.length + _named.length) { 305 math.min(_leadingCollections, _positional.length);
307 leadingPositional -= _named.length; 306 var trailingCollections =
308 } 307 math.max(_trailingCollections - _named.length, 0);
309
310 var trailingPositional = _trailingCollections - _named.length;
311 rule = new MultiplePositionalRule( 308 rule = new MultiplePositionalRule(
312 _collectionRule, leadingPositional, trailingPositional); 309 _collectionRule, leadingCollections, trailingCollections);
313 } 310 }
314 311
315 visitor.builder.startRule(rule); 312 _visitArguments(visitor, _positional, rule);
316
317 var chunk;
318 if (_isFirstArgument(_positional.first)) {
319 chunk = visitor.zeroSplit();
320 } else {
321 chunk = visitor.split();
322 }
323 rule.beforeArgument(chunk);
324
325 // Try to not split the arguments.
326 visitor.builder.startSpan(Cost.positionalArguments);
327
328 for (var argument in _positional) {
329 _visitArgument(visitor, rule, argument);
330
331 // Positional arguments split independently.
332 if (argument != _positional.last) {
333 rule.beforeArgument(visitor.split());
334 }
335 }
336
337 visitor.builder.endSpan();
338 visitor.builder.endRule();
339
340 return rule; 313 return rule;
341 } 314 }
342 315
343 /// Writes the named arguments, if any. 316 /// Writes the named arguments, if any.
344 void _visitNamed(SourceVisitor visitor, PositionalRule rule) { 317 void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) {
345 if (_named.isEmpty) return; 318 if (_named.isEmpty) return;
346 319
347 var positionalRule = rule; 320 // Only count the collections in the named rule.
348 var namedRule = new NamedRule(_collectionRule); 321 var leadingCollections =
349 visitor.builder.startRule(namedRule); 322 math.max(_leadingCollections - _positional.length, 0);
323 var trailingCollections = math.min(_trailingCollections, _named.length);
324 var namedRule =
325 new NamedRule(_collectionRule, leadingCollections, trailingCollections);
350 326
351 // Let the positional args force the named ones to split. 327 // Let the positional args force the named ones to split.
352 if (positionalRule != null) { 328 if (positionalRule != null) {
353 positionalRule.setNamedArgsRule(namedRule); 329 positionalRule.setNamedArgsRule(namedRule);
354 } 330 }
355 331
356 // Split before the first named argument. 332 _visitArguments(visitor, _named, namedRule);
357 namedRule.beforeArguments( 333 }
358 visitor.builder.split(space: !_isFirstArgument(_named.first)));
359 334
360 for (var argument in _named) { 335 void _visitArguments(
361 _visitArgument(visitor, namedRule, argument); 336 SourceVisitor visitor, List<Expression> arguments, ArgumentRule rule) {
337 visitor.builder.startRule(rule);
338
339 // Split before the first argument.
340 _previousSplit =
341 visitor.builder.split(space: !_isFirstArgument(arguments.first));
342 rule.beforeArgument(_previousSplit);
343
344 // Try to not split the positional arguments.
345 if (arguments == _positional) {
346 visitor.builder.startSpan(Cost.positionalArguments);
347 }
348
349 for (var argument in arguments) {
350 _visitArgument(visitor, rule, argument);
362 351
363 // Write the split. 352 // Write the split.
364 if (argument != _named.last) visitor.split(); 353 if (argument != arguments.last) {
354 _previousSplit = visitor.split();
355 rule.beforeArgument(_previousSplit);
356 }
365 } 357 }
366 358
359 if (arguments == _positional) visitor.builder.endSpan();
360
367 visitor.builder.endRule(); 361 visitor.builder.endRule();
368 } 362 }
369 363
370 void _visitArgument( 364 void _visitArgument(
371 SourceVisitor visitor, ArgumentRule rule, Expression argument) { 365 SourceVisitor visitor, ArgumentRule rule, Expression argument) {
372 // If we're about to write a collection argument, handle it specially. 366 // If we're about to write a collection argument, handle it specially.
373 if (_collections.contains(argument)) { 367 if (_collections.containsKey(argument)) {
374 if (rule != null) rule.beforeCollection(); 368 if (rule != null) rule.beforeCollection();
375 369
376 // Tell it to use the rule we've already created. 370 // Tell it to use the rule we've already created.
377 visitor.setNextLiteralBodyRule(_collectionRule); 371 visitor.beforeCollection(_collections[argument], this);
378 } else if (_hasMultipleArguments) { 372 } else if (_hasMultipleArguments) {
379 // Corner case: If there is just a single argument, don't bump the 373 // Edge case: If there is just a single argument, don't bump the nesting.
380 // nesting. This lets us avoid spurious indentation in cases like: 374 // This lets us avoid spurious indentation in cases like:
381 // 375 //
382 // function(function(() { 376 // function(function(() {
383 // body; 377 // body;
384 // })); 378 // }));
385 visitor.builder.startBlockArgumentNesting(); 379 visitor.builder.startBlockArgumentNesting();
386 } 380 }
387 381
388 visitor.visit(argument); 382 visitor.visit(argument);
389 383
390 if (_collections.contains(argument)) { 384 if (_collections.containsKey(argument)) {
391 if (rule != null) rule.afterCollection(); 385 if (rule != null) rule.afterCollection();
392 } else if (_hasMultipleArguments) { 386 } else if (_hasMultipleArguments) {
393 visitor.builder.endBlockArgumentNesting(); 387 visitor.builder.endBlockArgumentNesting();
394 } 388 }
395 389
396 // Write the trailing comma. 390 // Write the trailing comma.
397 if (!_isLastArgument(argument)) { 391 if (!_isLastArgument(argument)) {
398 visitor.token(argument.endToken.next); 392 visitor.token(argument.endToken.next);
399 } 393 }
400 } 394 }
401 395
402 bool _isFirstArgument(Expression argument) => argument == _allArguments.first; 396 bool _isFirstArgument(Expression argument) => argument == _allArguments.first;
403 397
404 bool _isLastArgument(Expression argument) => argument == _allArguments.last; 398 bool _isLastArgument(Expression argument) => argument == _allArguments.last;
405 399
406 /// Returns true if [expression] denotes a collection literal argument. 400 /// Returns the token for the left bracket if [expression] denotes a
401 /// collection literal argument.
407 /// 402 ///
408 /// Similar to block functions, collection arguments can get special 403 /// Similar to block functions, collection arguments can get special
409 /// indentation to make them look more statement-like. 404 /// indentation to make them look more statement-like.
410 static bool _isCollectionArgument(Expression expression) { 405 static Token _getCollectionBracket(Expression expression) {
411 if (expression is NamedExpression) { 406 if (expression is NamedExpression) {
412 expression = (expression as NamedExpression).expression; 407 expression = (expression as NamedExpression).expression;
413 } 408 }
414 409
415 // TODO(rnystrom): Should we step into parenthesized expressions? 410 // TODO(rnystrom): Should we step into parenthesized expressions?
416 411
417 return expression is ListLiteral || expression is MapLiteral; 412 if (expression is ListLiteral) return expression.leftBracket;
413 if (expression is MapLiteral) return expression.leftBracket;
414
415 // Not a collection literal.
416 return null;
418 } 417 }
419 } 418 }
OLDNEW
« no previous file with comments | « packages/dart_style/lib/src/._formatter_options.dart ('k') | packages/dart_style/lib/src/call_chain_visitor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698