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

Side by Side Diff: dart_style/lib/src/call_chain_visitor.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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
« no previous file with comments | « dart_style/lib/src/argument_list_visitor.dart ('k') | dart_style/lib/src/chunk.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library dart_style.src.call_chain_visitor;
6
7 import 'package:analyzer/analyzer.dart';
8
9 import 'argument_list_visitor.dart';
10 import 'rule/argument.dart';
11 import 'source_visitor.dart';
12
13 /// Helper class for [SourceVisitor] that handles visiting and writing a
14 /// chained series of method invocations, property accesses, and/or prefix
15 /// expressions. In other words, anything using the "." operator.
16 class CallChainVisitor {
17 final SourceVisitor _visitor;
18
19 /// The initial target of the call chain.
20 ///
21 /// This may be any expression except [MethodInvocation], [PropertyAccess] or
22 /// [PrefixedIdentifier].
23 final Expression _target;
24
25 /// The list of dotted names ([PropertyAccess] and [PrefixedIdentifier] at
26 /// the start of the call chain.
27 ///
28 /// This will be empty if the [_target] is not a [SimpleIdentifier].
29 final List<Expression> _properties;
30
31 /// The mixed method calls and property accesses in the call chain in the
32 /// order that they appear in the source.
33 final List<Expression> _calls;
34
35 /// Whether or not a [Rule] is currently active for the call chain.
36 bool _ruleEnabled = false;
37
38 /// Whether or not the span wrapping the call chain is currently active.
39 bool _spanEnded = false;
40
41 /// Creates a new call chain visitor for [visitor] starting with [node].
42 ///
43 /// The [node] is the outermost expression containing the chained "."
44 /// operators and must be a [MethodInvocation], [PropertyAccess] or
45 /// [PrefixedIdentifier].
46 factory CallChainVisitor(SourceVisitor visitor, Expression node) {
47 var target;
48
49 // Recursively walk the chain of calls and turn the tree into a list.
50 var calls = [];
51 flatten(expression) {
52 target = expression;
53
54 if (expression is MethodInvocation && expression.target != null) {
55 flatten(expression.target);
56 calls.add(expression);
57 } else if (expression is PropertyAccess && expression.target != null) {
58 flatten(expression.target);
59 calls.add(expression);
60 } else if (expression is PrefixedIdentifier) {
61 flatten(expression.prefix);
62 calls.add(expression);
63 }
64 }
65
66 flatten(node);
67
68 // An expression that starts with a series of dotted names gets treated a
69 // little specially. We don't force leading properties to split with the
70 // rest of the chain. Allows code like:
71 //
72 // address.street.number
73 // .toString()
74 // .length;
75 var properties = [];
76 if (target is SimpleIdentifier) {
77 properties =
78 calls.takeWhile((call) => call is! MethodInvocation).toList();
79 }
80
81 calls.removeRange(0, properties.length);
82
83 return new CallChainVisitor._(visitor, target, properties, calls);
84 }
85
86 CallChainVisitor._(
87 this._visitor, this._target, this._properties, this._calls);
88
89 /// Builds chunks for the call chain.
90 ///
91 /// If [unnest] is `false` than this will not close the expression nesting
92 /// created for the call chain and the caller must end it. Used by cascades
93 /// to force a cascade after a method chain to be more deeply nested than
94 /// the methods.
95 void visit({bool unnest}) {
96 if (unnest == null) unnest = true;
97
98 _visitor.builder.nestExpression();
99
100 // Try to keep the entire method invocation one line.
101 _visitor.builder.startSpan();
102
103 _visitor.visit(_target);
104
105 // Leading properties split like positional arguments: either not at all,
106 // before one ".", or before all of them.
107 if (_properties.length == 1) {
108 _visitor.soloZeroSplit();
109 _writeCall(_properties.single);
110 } else if (_properties.length > 1) {
111 var argRule = new MultiplePositionalRule(null, 0, 0);
112 _visitor.builder.startRule(argRule);
113
114 for (var property in _properties) {
115 argRule.beforeArgument(_visitor.zeroSplit());
116 _writeCall(property);
117 }
118
119 _visitor.builder.endRule();
120 }
121
122 // The remaining chain of calls generally split atomically (either all or
123 // none), except that block arguments may split a chain into two parts.
124 for (var call in _calls) {
125 _enableRule();
126 _visitor.zeroSplit();
127 _writeCall(call);
128 }
129
130 _disableRule();
131 _endSpan();
132
133 if (unnest) _visitor.builder.unnest();
134 }
135
136 /// Writes [call], which must be one of the supported expression types.
137 void _writeCall(Expression call) {
138 if (call is MethodInvocation) {
139 _writeInvocation(call);
140 } else if (call is PropertyAccess) {
141 _writePropertyAccess(call);
142 } else if (call is PrefixedIdentifier) {
143 _writePrefixedIdentifier(call);
144 } else {
145 // Unexpected type.
146 assert(false);
147 }
148 }
149
150 void _writeInvocation(MethodInvocation invocation) {
151 _visitor.token(invocation.operator);
152 _visitor.token(invocation.methodName.token);
153
154 // If a method's argument list includes any block arguments, there's a
155 // good chance it will split. Treat the chains before and after that as
156 // separate unrelated method chains.
157 //
158 // This is kind of a hack since it treats methods before and after a
159 // collection literal argument differently even when the collection
160 // doesn't split, but it works out OK in practice.
161 //
162 // Doing something more precise would require setting up a bunch of complex
163 // constraints between various rules. You'd basically have to say "if the
164 // block argument splits then allow the chain after it to split
165 // independently, otherwise force it to follow the previous chain".
166 var args = new ArgumentListVisitor(_visitor, invocation.argumentList);
167
168 // Stop the rule after the last call, but before its arguments. This
169 // allows unsplit chains where the last argument list wraps, like:
170 //
171 // foo().bar().baz(
172 // argument, list);
173 //
174 // Also stop the rule to split the argument list at any call with
175 // block arguments. This makes for nicer chains of higher-order method
176 // calls, like:
177 //
178 // items.map((element) {
179 // ...
180 // }).where((element) {
181 // ...
182 // });
183 if (invocation == _calls.last || args.hasBlockArguments) _disableRule();
184
185 if (args.nestMethodArguments) _visitor.builder.startBlockArgumentNesting();
186
187 // For a single method call on an identifier, stop the span before the
188 // arguments to make it easier to keep the call name with the target. In
189 // other words, prefer:
190 //
191 // target.method(
192 // argument, list);
193 //
194 // Over:
195 //
196 // target
197 // .method(argument, list);
198 //
199 // Alternatively, the way to think of this is try to avoid splitting on the
200 // "." when calling a single method on a single name. This is especially
201 // important because the identifier is often a library prefix, and splitting
202 // there looks really odd.
203 if (_properties.isEmpty &&
204 _calls.length == 1 &&
205 _target is SimpleIdentifier) {
206 _endSpan();
207 }
208
209 _visitor.visit(invocation.argumentList);
210
211 if (args.nestMethodArguments) _visitor.builder.endBlockArgumentNesting();
212 }
213
214 void _writePropertyAccess(PropertyAccess property) {
215 _visitor.token(property.operator);
216 _visitor.visit(property.propertyName);
217 }
218
219 void _writePrefixedIdentifier(PrefixedIdentifier prefix) {
220 _visitor.token(prefix.period);
221 _visitor.visit(prefix.identifier);
222 }
223
224 /// If a [Rule] for the method chain is currently active, ends it.
225 void _disableRule() {
226 if (_ruleEnabled == false) return;
227
228 _visitor.builder.endRule();
229 _ruleEnabled = false;
230 }
231
232 /// Creates a new method chain [Rule] if one is not already active.
233 void _enableRule() {
234 if (_ruleEnabled) return;
235
236 _visitor.builder.startRule();
237 _ruleEnabled = true;
238 }
239
240 /// Ends the span wrapping the call chain if it hasn't ended already.
241 void _endSpan() {
242 if (_spanEnded) return;
243
244 _visitor.builder.endSpan();
245 _spanEnded = true;
246 }
247 }
OLDNEW
« no previous file with comments | « dart_style/lib/src/argument_list_visitor.dart ('k') | dart_style/lib/src/chunk.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698