blob: f11bad0ed00ddff9d24d498f8844883cc957a78a [file] [log] [blame]
// Copyright (c) 2014, 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.line_printer;
import 'chunk.dart';
import 'nesting.dart';
/// A prefix of a series of chunks, which in turn can be considered a key to
/// describe the suffix of the remaining chunks that follows it.
///
/// This is used by the splitter to memoize suffixes whose best splits have
/// previously been calculated. For each unique [LinePrefix], there will be a
/// single set of best splits for the remainder of the line following it.
class LinePrefix {
/// The number of chunks in the prefix.
///
/// The suffix is the remaining chunks starting at index [length].
final int length;
/// The [SplitParam]s for params that appear both in the prefix and suffix
/// and have not been set.
///
/// This is used to ensure that we honor the decisions already made in the
/// prefix when processing the suffix. It only includes params that appear in
/// the suffix to avoid storing information about irrelevant params. This is
/// critical to ensure we keep prefixes simple to maximize the reuse we get
/// from the memoization table.
///
/// This does *not* include params that appear only in the suffix. In other
/// words, it only includes params that have deliberately been chosen to not
/// be set, not params we simply haven't considered yet.
final Set<SplitParam> unsplitParams;
/// The [SplitParam]s for params that appear both in the prefix and suffix
/// and have been set.
///
/// This is used to ensure that we honor the decisions already made in the
/// prefix when processing the suffix. It only includes params that appear in
/// the suffix to avoid storing information about irrelevant params. This is
/// critical to ensure we keep prefixes simple to maximize the reuse we get
/// from the memoization table.
final Set<SplitParam> splitParams;
/// The nested expressions in the prefix that are still open at the beginning
/// of the suffix.
///
/// For example, if the line is `outer(inner(argument))`, and the prefix is
/// `outer(inner(`, the nesting stack will be two levels deep.
final NestingStack _nesting;
/// The depth of indentation caused expression nesting.
int get nestingIndent => _nesting.indent;
/// Creates a new zero-length prefix whose suffix is the entire line.
LinePrefix([int length = 0])
: this._(length, new Set(), new Set(), new NestingStack());
LinePrefix._(this.length, this.unsplitParams, this.splitParams,
this._nesting) {
assert(_nesting != null);
}
bool operator ==(other) {
if (other is! LinePrefix) return false;
if (length != other.length) return false;
if (_nesting != other._nesting) return false;
if (unsplitParams.length != other.unsplitParams.length) {
return false;
}
if (splitParams.length != other.splitParams.length) {
return false;
}
for (var param in unsplitParams) {
if (!other.unsplitParams.contains(param)) return false;
}
for (var param in splitParams) {
if (!other.splitParams.contains(param)) return false;
}
return true;
}
int get hashCode => length.hashCode ^ _nesting.hashCode;
/// Create zero or more new [LinePrefix]es starting from the same nesting
/// stack as this one but expanded to [length].
///
/// The nesting of the chunk immediately preceding the suffix modifies the
/// new prefix's nesting stack.
///
/// [unsplitParams] is the set of [SplitParam]s in the new prefix that the
/// splitter decided to *not* split (including unsplit ones also in this
/// prefix). [splitParams] is likewise the set that have been chosen to be
/// split.
///
/// Returns an empty iterable if the new split chunk results in an invalid
/// prefix. See [NestingStack.applySplit] for details.
Iterable<LinePrefix> expand(List<Chunk> chunks, Set<SplitParam> unsplitParams,
Set<SplitParam> splitParams, int length) {
var split = chunks[length - 1];
if (!split.isInExpression) {
return [
new LinePrefix._(length, unsplitParams, splitParams,
new NestingStack())
];
}
return _nesting.applySplit(split).map((nesting) =>
new LinePrefix._(
length, unsplitParams, splitParams, nesting));
}
/// Gets the leading indentation of the newline that immediately follows
/// this prefix.
///
/// Takes into account the indentation of the previous split and any
/// additional indentation from wrapped nested expressions.
int getNextLineIndent(List<Chunk> chunks, int indent) {
// TODO(rnystrom): This could be cached at construction time, which may be
// faster.
// Get the initial indentation of the line immediately after the prefix,
// ignoring any extra indentation caused by nested expressions.
if (length > 0) {
indent = chunks[length - 1].indent;
}
return indent + _nesting.indent;
}
String toString() =>
"LinePrefix(length $length, nesting $_nesting, "
"unsplit $unsplitParams, split $splitParams)";
}