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