OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, 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.rule.argument; | |
6 | |
7 import '../chunk.dart'; | |
8 import 'rule.dart'; | |
9 | |
10 /// Base class for a rule that handles argument or parameter lists. | |
11 abstract class ArgumentRule extends Rule { | |
12 /// The rule used to split collections in the argument list, if any. | |
13 final Rule _collectionRule; | |
14 | |
15 /// If true, then inner rules that are written will force this rule to split. | |
16 /// | |
17 /// Temporarily disabled while writing collectio arguments so that they can be | |
18 /// multi-line without forcing the whole argument list to split. | |
19 bool _trackInnerRules = true; | |
20 | |
21 /// Don't split when an inner collection rule splits. | |
22 bool get splitsOnInnerRules => _trackInnerRules; | |
23 | |
24 /// Creates a new rule for a positional argument list. | |
25 /// | |
26 /// If [_collectionRule] is given, it is the rule used to split the | |
27 /// collections in the list. | |
28 ArgumentRule(this._collectionRule); | |
29 | |
30 /// Called before a collection argument is written. | |
31 /// | |
32 /// Disables tracking inner rules while a collection argument is written. | |
33 void beforeCollection() { | |
34 assert(_trackInnerRules == true); | |
35 _trackInnerRules = false; | |
36 } | |
37 | |
38 /// Called after a collection argument is complete. | |
39 /// | |
40 /// Re-enables tracking inner rules. | |
41 void afterCollection() { | |
42 assert(_trackInnerRules == false); | |
43 _trackInnerRules = true; | |
44 } | |
45 } | |
46 | |
47 /// Base class for a rule for handling positional argument lists. | |
48 abstract class PositionalRule extends ArgumentRule { | |
49 /// The chunks prior to each positional argument. | |
50 final List<Chunk> _arguments = []; | |
51 | |
52 /// If there are named arguments following these positional ones, this will | |
53 /// be their rule. | |
54 Rule _namedArgsRule; | |
55 | |
56 /// Creates a new rule for a positional argument list. | |
57 /// | |
58 /// If [collectionRule] is given, it is the rule used to split the collection | |
59 /// arguments in the list. | |
60 PositionalRule(Rule collectionRule) : super(collectionRule); | |
61 | |
62 /// Remembers [chunk] as containing the split that occurs right before an | |
63 /// argument in the list. | |
64 void beforeArgument(Chunk chunk) { | |
65 _arguments.add(chunk); | |
66 } | |
67 | |
68 /// Remembers that [rule] is the [NamedArgsRule] immediately following this | |
69 /// positional argument list. | |
70 void setNamedArgsRule(NamedRule rule) { | |
71 _namedArgsRule = rule; | |
72 } | |
73 | |
74 /// Constrains the named argument list to at least move to the next line if | |
75 /// there are any splits in the positional arguments. Prevents things like: | |
76 /// | |
77 /// function( | |
78 /// argument, | |
79 /// argument, named: argument); | |
80 int constrain(int value, Rule other) { | |
81 var constrained = super.constrain(value, other); | |
82 if (constrained != null) return constrained; | |
83 | |
84 // Handle the relationship between the positional and named args. | |
85 if (other == _namedArgsRule) { | |
86 // If the positional args are one-per-line, the named args are too. | |
87 if (value == fullySplitValue) return _namedArgsRule.fullySplitValue; | |
88 | |
89 // Otherwise, if there is any split in the positional arguments, don't | |
90 // allow the named arguments on the same line as them. | |
91 if (value != 0) return -1; | |
92 } | |
93 | |
94 return null; | |
95 } | |
96 } | |
97 | |
98 /// Split rule for a call with a single positional argument (which may or may | |
99 /// not be a collection argument.) | |
100 class SinglePositionalRule extends PositionalRule { | |
101 int get numValues => 2; | |
102 | |
103 /// If there is only a single non-collection argument, allow it to split | |
104 /// internally without forcing a split before the argument. | |
105 final bool splitsOnInnerRules; | |
106 | |
107 bool hack = false; | |
108 | |
109 /// Creates a new rule for a positional argument list. | |
110 /// | |
111 /// If [collectionRule] is given, it is the rule used to split the | |
112 /// collections in the list. If [splitsOnInnerRules] is `true`, then we will | |
113 /// split before the argument if the argument itself contains a split. | |
114 SinglePositionalRule(Rule collectionRule, {bool splitsOnInnerRules}) | |
115 : super(collectionRule), | |
116 splitsOnInnerRules = | |
117 splitsOnInnerRules != null ? splitsOnInnerRules : false; | |
118 | |
119 bool isSplit(int value, Chunk chunk) => value == 1; | |
120 | |
121 int constrain(int value, Rule other) { | |
122 var constrained = super.constrain(value, other); | |
123 if (constrained != null) return constrained; | |
124 | |
125 if (other != _collectionRule) return null; | |
126 | |
127 // If we aren't splitting any args, we can split the collection. | |
128 if (value == 0) return null; | |
129 | |
130 // We are splitting before a collection, so don't let it split internally. | |
131 return 0; | |
132 } | |
133 | |
134 String toString() => "1Pos${super.toString()}"; | |
135 } | |
136 | |
137 /// Split rule for a call with more than one positional argument. | |
138 /// | |
139 /// The number of values is based on the number of arguments and whether or not | |
140 /// there are bodies. The first two values are always: | |
141 /// | |
142 /// * 0: Do not split at all. | |
143 /// * 1: Split only before the first argument. | |
144 /// | |
145 /// Then there is a value for each argument, to split before that argument. | |
146 /// These values work back to front. So, for a two-argument list, value 2 splits | |
147 /// after the second argument and value 3 splits after the first. | |
148 /// | |
149 /// Then there is a value that splits before every argument. | |
150 /// | |
151 /// Finally, if there are collection arguments, there is another value that | |
152 /// splits before all of the non-collection arguments, but does not split | |
153 /// before the collections, so that they can split internally. | |
154 class MultiplePositionalRule extends PositionalRule { | |
155 /// The number of leading collection arguments. | |
156 /// | |
157 /// This and [_trailingCollections] cannot both be positive. If every | |
158 /// argument is a collection, this will be [_arguments.length] and | |
159 /// [_trailingCollections] will be 0. | |
160 final int _leadingCollections; | |
161 | |
162 /// The number of trailing collections. | |
163 /// | |
164 /// This and [_leadingCollections] cannot both be positive. | |
165 final int _trailingCollections; | |
166 | |
167 int get numValues { | |
168 // Can split before any one argument, none, or all. | |
169 var result = 2 + _arguments.length; | |
170 | |
171 // When there are collection arguments, there are two ways we can split on | |
172 // "all" arguments: | |
173 // | |
174 // - Split on just the non-collection arguments, and force the collection | |
175 // arguments to split internally. | |
176 // - Split on all of them including the collection arguments, and do not | |
177 // allow the collection arguments to split internally. | |
178 if (_leadingCollections > 0 || _trailingCollections > 0) result++; | |
179 | |
180 return result; | |
181 } | |
182 | |
183 MultiplePositionalRule( | |
184 Rule collectionRule, this._leadingCollections, this._trailingCollections) | |
185 : super(collectionRule); | |
186 | |
187 String toString() => "*Pos${super.toString()}"; | |
188 | |
189 bool isSplit(int value, Chunk chunk) { | |
190 // Don't split at all. | |
191 if (value == 0) return false; | |
192 | |
193 // Split only before the first argument. Keep the entire argument list | |
194 // together on the next line. | |
195 if (value == 1) return chunk == _arguments.first; | |
196 | |
197 // Split before a single argument. Try later arguments before earlier ones | |
198 // to try to keep as much on the first line as possible. | |
199 if (value <= _arguments.length) { | |
200 var argument = _arguments.length - value + 1; | |
201 return chunk == _arguments[argument]; | |
202 } | |
203 | |
204 // Only split before the non-collection arguments. | |
205 if (value == _arguments.length + 1) { | |
206 for (var i = 0; i < _leadingCollections; i++) { | |
207 if (chunk == _arguments[i]) return false; | |
208 } | |
209 | |
210 for (var i = _arguments.length - _trailingCollections; | |
211 i < _arguments.length; | |
212 i++) { | |
213 if (chunk == _arguments[i]) return false; | |
214 } | |
215 | |
216 return true; | |
217 } | |
218 | |
219 // Split before all of the arguments, even the collections. | |
220 return true; | |
221 } | |
222 | |
223 int constrain(int value, Rule other) { | |
224 var constrained = super.constrain(value, other); | |
225 if (constrained != null) return constrained; | |
226 | |
227 if (other != _collectionRule) return null; | |
228 | |
229 // If we aren't splitting any args, we can split the collection. | |
230 if (value == 0) return null; | |
231 | |
232 // Split only before the first argument. | |
233 if (value == 1) { | |
234 if (_leadingCollections > 0) { | |
235 // We are splitting before a collection, so don't let it split | |
236 // internally. | |
237 return 0; | |
238 } else { | |
239 // The split is outside of the collections so they can split or not. | |
240 return null; | |
241 } | |
242 } | |
243 | |
244 // Split before a single argument. If it's in the middle of the collection | |
245 // arguments, don't allow them to split. | |
246 if (value <= _arguments.length) { | |
247 var argument = _arguments.length - value + 1; | |
248 if (argument < _leadingCollections) return 0; | |
249 if (argument >= _arguments.length - _trailingCollections) return 0; | |
250 | |
251 return null; | |
252 } | |
253 | |
254 // Only split before the non-collection arguments. This case only comes into | |
255 // play when we do want to split the collection, so force that here. | |
256 if (value == _arguments.length + 1) return 1; | |
257 | |
258 // Split before all of the arguments, even the collection, so don't let | |
259 // them split. | |
260 return 0; | |
261 } | |
262 } | |
263 | |
264 /// Splitting rule for a list of named arguments or parameters. Its values mean: | |
265 /// | |
266 /// * 0: Do not split at all. | |
267 /// * 1: Split only before first argument. | |
268 /// * 2: Split before all arguments, including the first. | |
269 class NamedRule extends ArgumentRule { | |
270 /// The chunk prior to the first named argument. | |
271 Chunk _first; | |
272 | |
273 int get numValues => 3; | |
274 | |
275 NamedRule(Rule collectionRule) : super(collectionRule); | |
276 | |
277 void beforeArguments(Chunk chunk) { | |
278 assert(_first == null); | |
279 _first = chunk; | |
280 } | |
281 | |
282 bool isSplit(int value, Chunk chunk) { | |
283 switch (value) { | |
284 case 0: | |
285 return false; | |
286 case 1: | |
287 return chunk == _first; | |
288 case 2: | |
289 return true; | |
290 } | |
291 | |
292 throw "unreachable"; | |
293 } | |
294 | |
295 int constrain(int value, Rule other) { | |
296 var constrained = super.constrain(value, other); | |
297 if (constrained != null) return constrained; | |
298 | |
299 if (other != _collectionRule) return null; | |
300 | |
301 // If we aren't splitting any args, we can split the collection. | |
302 if (value == 0) return null; | |
303 | |
304 // Split before all of the arguments, even the collections, so don't let | |
305 // them split. | |
306 return 0; | |
307 } | |
308 | |
309 String toString() => "Named${super.toString()}"; | |
310 } | |
OLD | NEW |