Coverage report for lib/src/rule/argument.dart

Line coverage: 68 / 72 (94.4%)

All files > lib/src/rule/argument.dart

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 chunks prior to each positional argument.
13
  final List<Chunk> _arguments = [];
14
15
  /// The rule used to split collections in the argument list, if any.
16
  Rule _collectionRule;
17
18
  /// The number of leading collection arguments.
19
  ///
20
  /// This and [_trailingCollections] cannot both be positive. If every
21
  /// argument is a collection, this will be [_arguments.length] and
22
  /// [_trailingCollections] will be 0.
23
  final int _leadingCollections;
24
25
  /// The number of trailing collections.
26
  ///
27
  /// This and [_leadingCollections] cannot both be positive.
28
  final int _trailingCollections;
29
30
  /// If true, then inner rules that are written will force this rule to split.
31
  ///
32
  /// Temporarily disabled while writing collection arguments so that they can
33
  /// be multi-line without forcing the whole argument list to split.
34
  bool _trackInnerRules = true;
35
36
  /// Don't split when an inner collection rule splits.
374
  bool get splitsOnInnerRules => _trackInnerRules;
38
393
  ArgumentRule(this._collectionRule, this._leadingCollections,
40
      this._trailingCollections);
41
422
  void addConstrainedRules(Set<Rule> rules) {
432
    super.addConstrainedRules(rules);
444
    if (_collectionRule != null) rules.add(_collectionRule);
45
  }
46
473
  void forgetUnusedRules() {
483
    super.forgetUnusedRules();
495
    if (_collectionRule != null && _collectionRule.index == null) {
501
      _collectionRule = null;
51
    }
52
  }
53
54
  /// Remembers [chunk] as containing the split that occurs right before an
55
  /// argument in the list.
563
  void beforeArgument(Chunk chunk) {
576
    _arguments.add(chunk);
58
  }
59
60
  /// Disables tracking inner rules while a collection argument is written.
613
  void disableSplitOnInnerRules() {
626
    assert(_trackInnerRules == true);
633
    _trackInnerRules = false;
64
  }
65
66
  /// Re-enables tracking inner rules.
673
  void enableSplitOnInnerRules() {
686
    assert(_trackInnerRules == false);
693
    _trackInnerRules = true;
70
  }
71
}
72
73
/// Rule for handling positional argument lists.
74
///
75
/// The number of values is based on the number of arguments and whether or not
76
/// there are bodies. The first two values are always:
77
///
78
/// * 0: Do not split at all.
79
/// * 1: Split only before the first argument.
80
///
81
/// Then there is a value for each argument, to split before that argument.
82
/// These values work back to front. So, for a two-argument list, value 2 splits
83
/// after the second argument and value 3 splits after the first.
84
///
85
/// Then there is a value that splits before every argument.
86
///
87
/// Finally, if there are collection arguments, there is another value that
88
/// splits before all of the non-collection arguments, but does not split
89
/// before the collections, so that they can split internally.
90
class PositionalRule extends ArgumentRule {
91
  /// If there are named arguments following these positional ones, this will
92
  /// be their rule.
93
  Rule _namedArgsRule;
94
95
  /// Creates a new rule for a positional argument list.
96
  ///
97
  /// If [_collectionRule] is given, it is the rule used to split the collection
98
  /// arguments in the list.
993
  PositionalRule(
100
      Rule collectionRule, int leadingCollections, int trailingCollections)
1013
      : super(collectionRule, leadingCollections, trailingCollections);
102
1032
  int get numValues {
104
    // Can split before any one argument or none.
1056
    var result = _arguments.length + 1;
106
107
    // If there are multiple arguments, can split before all of them.
1088
    if (_arguments.length > 1) result++;
109
110
    // When there are collection arguments, there are two ways we can split on
111
    // "all" arguments:
112
    //
113
    // - Split on just the non-collection arguments, and force the collection
114
    //   arguments to split internally.
115
    // - Split on all of them including the collection arguments, and do not
116
    //   allow the collection arguments to split internally.
1179
    if (_leadingCollections > 0 || _trailingCollections > 0) result++;
118
119
    return result;
120
  }
121
1222
  void addConstrainedRules(Set<Rule> rules) {
1232
    super.addConstrainedRules(rules);
1244
    if (_namedArgsRule != null) rules.add(_namedArgsRule);
125
  }
126
1273
  void forgetUnusedRules() {
1283
    super.forgetUnusedRules();
1295
    if (_namedArgsRule != null && _namedArgsRule.index == null) {
1300
      _namedArgsRule = null;
131
    }
132
  }
133
1342
  bool isSplitAtValue(int value, Chunk chunk) {
135
    // Split only before the first argument. Keep the entire argument list
136
    // together on the next line.
1378
    if (value == 1) return chunk == _arguments.first;
138
139
    // Split before a single argument. Try later arguments before earlier ones
140
    // to try to keep as much on the first line as possible.
1416
    if (value <= _arguments.length) {
1428
      var argument = _arguments.length - value + 1;
1436
      return chunk == _arguments[argument];
144
    }
145
146
    // Only split before the non-collection arguments.
1478
    if (value == _arguments.length + 1) {
1484
      for (var i = 0; i < _leadingCollections; i++) {
1491
        if (chunk == _arguments[i]) return false;
150
      }
151
1528
      for (var i = _arguments.length - _trailingCollections;
1536
          i < _arguments.length;
1540
          i++) {
1551
        if (chunk == _arguments[i]) return false;
156
      }
157
158
      return true;
159
    }
160
161
    // Split before all of the arguments, even the collections.
162
    return true;
163
  }
164
165
  /// Remembers that [rule] is the [Rule] immediately following this positional
166
  /// positional argument list.
167
  ///
168
  /// This is normally a [NamedRule] but [PositionalRule] is also used for the
169
  /// property accesses at the beginning of a call chain, in which case this
170
  /// is just a [SimpleRule].
1711
  void setNamedArgsRule(Rule rule) {
1721
    _namedArgsRule = rule;
173
  }
174
175
  /// Constrains the named argument list to at least move to the next line if
176
  /// there are any splits in the positional arguments. Prevents things like:
177
  ///
178
  ///      function(
179
  ///          argument,
180
  ///          argument, named: argument);
1812
  int constrain(int value, Rule other) {
1822
    var constrained = super.constrain(value, other);
183
    if (constrained != null) return constrained;
184
185
    // Handle the relationship between the positional and named args.
1864
    if (other == _namedArgsRule) {
187
      // If the positional args are one-per-line, the named args are too.
1884
      if (value == fullySplitValue) return _namedArgsRule.fullySplitValue;
189
190
      // Otherwise, if there is any split in the positional arguments, don't
191
      // allow the named arguments on the same line as them.
1921
      if (value != 0) return -1;
193
    }
194
195
    // Decide how to constrain the collection rule.
1964
    if (other != _collectionRule) return null;
197
198
    // If all of the collections are in the named arguments, [_collectionRule]
199
    // will not be null, but we don't have to handle it.
2004
    if (_leadingCollections == 0 && _trailingCollections == 0) return null;
201
202
    // If we aren't splitting any args, we can split the collection.
2031
    if (value == Rule.unsplit) return null;
204
205
    // Split only before the first argument.
2061
    if (value == 1) {
2072
      if (_leadingCollections > 0) {
208
        // We are splitting before a collection, so don't let it split
209
        // internally.
210
        return Rule.unsplit;
211
      } else {
212
        // The split is outside of the collections so they can split or not.
213
        return null;
214
      }
215
    }
216
217
    // Split before a single argument. If it's in the middle of the collection
218
    // arguments, don't allow them to split.
2193
    if (value <= _arguments.length) {
2204
      var argument = _arguments.length - value + 1;
2212
      if (argument < _leadingCollections ||
2225
          argument >= _arguments.length - _trailingCollections) {
223
        return Rule.unsplit;
224
      }
225
226
      return null;
227
    }
228
229
    // Only split before the non-collection arguments. This case only comes into
230
    // play when we do want to split the collection, so force that here.
2314
    if (value == _arguments.length + 1) return 1;
232
233
    // Split before all of the arguments, even the collections. We'll allow
234
    // them to split but indent their bodies if they do.
235
    return null;
236
  }
237
2380
  String toString() => "Pos${super.toString()}";
239
}
240
241
/// Splitting rule for a list of named arguments or parameters. Its values mean:
242
///
243
/// * Do not split at all.
244
/// * Split only before first argument.
245
/// * Split before all arguments.
246
class NamedRule extends ArgumentRule {
2472
  int get numValues => 3;
248
2492
  NamedRule(
250
      Rule collectionRule, int leadingCollections, int trailingCollections)
2512
      : super(collectionRule, leadingCollections, trailingCollections);
252
2532
  bool isSplitAtValue(int value, Chunk chunk) {
254
    // Move all arguments to the second line as a unit.
2558
    if (value == 1) return chunk == _arguments.first;
256
257
    // Otherwise, split before all arguments.
258
    return true;
259
  }
260
2612
  int constrain(int value, Rule other) {
2622
    var constrained = super.constrain(value, other);
263
    if (constrained != null) return constrained;
264
265
    // Decide how to constrain the collection rule.
2664
    if (other != _collectionRule) return null;
267
268
    // If all of the collections are in the named arguments, [_collectionRule]
269
    // will not be null, but we don't have to handle it.
2704
    if (_leadingCollections == 0 && _trailingCollections == 0) return null;
271
272
    // If we aren't splitting any args, we can split the collection.
2731
    if (value == Rule.unsplit) return null;
274
275
    // Split only before the first argument. Don't allow the collections to
276
    // split.
2771
    if (value == 1) return Rule.unsplit;
278
279
    // Split before all of the arguments, even the collections. We'll allow
280
    // them to split but indent their bodies if they do.
281
    return null;
282
  }
283
2840
  String toString() => "Named${super.toString()}";
285
}