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

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

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months 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; 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
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
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 }
OLDNEW
« no previous file with comments | « packages/dart_style/dist/dart-style.d.ts ('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