| // 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. |
| import '../chunk.dart'; |
| import 'rule.dart'; |
| |
| /// Base class for a rule that handles argument or parameter lists. |
| abstract class ArgumentRule extends SplitContainingRule { |
| /// The chunks prior to each positional argument. |
| final List<Chunk?> _arguments = []; |
| |
| /// Remembers [chunk] as containing the split that occurs right before an |
| /// argument in the list. |
| void beforeArgument(Chunk? chunk) { |
| _arguments.add(chunk); |
| } |
| } |
| |
| /// Rule for handling positional argument lists. |
| /// |
| /// 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 PositionalRule extends ArgumentRule { |
| /// 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; |
| |
| /// Creates a new rule for a positional argument list. |
| /// |
| /// [argumentCount] is the number of arguments that will be added to the rule |
| /// by later calls to [beforeArgument()]. |
| /// |
| /// If [collectionRule] is given, it is the rule used to split the collection |
| /// arguments in the list. It must be provided if [leadingCollections] or |
| /// [trailingCollections] is non-zero. |
| PositionalRule(Rule? collectionRule, |
| {required int argumentCount, |
| int leadingCollections = 0, |
| int trailingCollections = 0}) |
| : _leadingCollections = leadingCollections, |
| _trailingCollections = trailingCollections { |
| // Don't split inside collections if there are leading collections and |
| // we split before the first argument. |
| if (leadingCollections > 0) { |
| addConstraint(1, collectionRule!, Rule.unsplit); |
| } |
| |
| // If we're only splitting before the non-collection arguments, the |
| // intent is to split inside the collections, so force that here. |
| if (leadingCollections > 0 || trailingCollections > 0) { |
| addConstraint(argumentCount + 1, collectionRule!, 1); |
| } |
| |
| // Split before a single argument. If it's in the middle of the collection |
| // arguments, don't allow them to split. |
| for (var argument = 0; argument < leadingCollections; argument++) { |
| var value = argumentCount - argument + 1; |
| addConstraint(value, collectionRule!, Rule.unsplit); |
| } |
| |
| for (var argument = argumentCount - trailingCollections; |
| argument < argumentCount; |
| argument++) { |
| var value = argumentCount - argument + 1; |
| addConstraint(value, collectionRule!, Rule.unsplit); |
| } |
| } |
| |
| @override |
| int get numValues { |
| // Can split before any one argument or none. |
| var result = _arguments.length + 1; |
| |
| // If there are multiple arguments, can split before all of them. |
| if (_arguments.length > 1) result++; |
| |
| // 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; |
| } |
| |
| @override |
| bool isSplitAtValue(int value, Chunk chunk) { |
| // 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; |
| } |
| |
| /// Builds any constraints from this positional argument rule onto the [rule] |
| /// used for the subsequent named arguments in the same argument list. |
| /// |
| /// The [rule] is normally a [NamedRule] but [PositionalRule] is also used for |
| /// the property accesses at the beginning of a call chain, in which case this |
| /// is just a [SimpleRule]. |
| void addNamedArgsConstraints(Rule rule) { |
| // If the positional args are one-per-line, the named args are too. |
| constrainWhenFullySplit(rule); |
| |
| // Otherwise, if there is any split in the positional arguments, don't |
| // allow the named arguments on the same line as them. |
| addRangeConstraint(1, fullySplitValue, rule, Rule.mustSplit); |
| } |
| |
| @override |
| String toString() => 'Pos${super.toString()}'; |
| } |
| |
| /// Splitting rule for a list of named arguments or parameters. Its values mean: |
| /// |
| /// * Do not split at all. |
| /// * Split only before first argument. |
| /// * Split before all arguments. |
| class NamedRule extends ArgumentRule { |
| @override |
| int get numValues => 3; |
| |
| /// Creates a new rule for a named argument list. |
| /// |
| /// [argumentCount] is the number of arguments that will be added to the rule |
| /// by later calls to [beforeArgument()]. |
| /// |
| /// If [collectionRule] is given, it is the rule used to split the collection |
| /// arguments in the list. It must be provided if [leadingCollections] or |
| /// [trailingCollections] is non-zero. |
| NamedRule( |
| Rule? collectionRule, int leadingCollections, int trailingCollections) { |
| if (leadingCollections > 0 || trailingCollections > 0) { |
| // Split only before the first argument. Don't allow the collections to |
| // split. |
| addConstraint(1, collectionRule!, Rule.unsplit); |
| } |
| } |
| |
| @override |
| bool isSplitAtValue(int value, Chunk chunk) { |
| // Move all arguments to the second line as a unit. |
| if (value == 1) return chunk == _arguments.first; |
| |
| // Otherwise, split before all arguments. |
| return true; |
| } |
| |
| @override |
| String toString() => 'Named${super.toString()}'; |
| } |