| Index: packages/dart_style/lib/src/chunk_builder.dart
|
| diff --git a/packages/dart_style/lib/src/chunk_builder.dart b/packages/dart_style/lib/src/chunk_builder.dart
|
| index c7ff91bb200efe5ed2b961456910e6b34da1e193..3aa95a5caf60adbf4b0aabede74e244216916081 100644
|
| --- a/packages/dart_style/lib/src/chunk_builder.dart
|
| +++ b/packages/dart_style/lib/src/chunk_builder.dart
|
| @@ -61,9 +61,6 @@ class ChunkBuilder {
|
| /// written before they start.
|
| final _lazyRules = <Rule>[];
|
|
|
| - /// The indexes of the chunks owned by each rule (except for hard splits).
|
| - final _ruleChunks = <Rule, List<int>>{};
|
| -
|
| /// The nested stack of spans that are currently being written.
|
| final _openSpans = <OpenSpan>[];
|
|
|
| @@ -101,8 +98,8 @@ class ChunkBuilder {
|
| /// token pair.
|
| bool get needsToPreserveNewlines =>
|
| _pendingWhitespace == Whitespace.oneOrTwoNewlines ||
|
| - _pendingWhitespace == Whitespace.spaceOrNewline ||
|
| - _pendingWhitespace == Whitespace.splitOrNewline;
|
| + _pendingWhitespace == Whitespace.spaceOrNewline ||
|
| + _pendingWhitespace == Whitespace.splitOrNewline;
|
|
|
| /// The number of characters of code that can fit in a single line.
|
| int get pageWidth => _formatter.pageWidth;
|
| @@ -151,25 +148,18 @@ class ChunkBuilder {
|
|
|
| /// Write a split owned by the current innermost rule.
|
| ///
|
| - /// If [nesting] is given, uses that. Otherwise, uses the current nesting
|
| - /// level. If unsplit, it expands to a space if [space] is `true`.
|
| - ///
|
| /// If [flushLeft] is `true`, then forces the next line to start at column
|
| /// one regardless of any indentation or nesting.
|
| ///
|
| /// If [isDouble] is passed, forces the split to either be a single or double
|
| /// newline. Otherwise, leaves it indeterminate.
|
| - Chunk split({bool space, bool isDouble, bool flushLeft}) =>
|
| - _writeSplit(_rules.last, null,
|
| - flushLeft: flushLeft, isDouble: isDouble, spaceWhenUnsplit: space);
|
| -
|
| - /// Write a split owned by the current innermost rule.
|
| ///
|
| - /// Unlike [split()], this ignores any current expression nesting. It always
|
| - /// indents the next line at the statement level.
|
| - Chunk blockSplit({bool space, bool isDouble}) =>
|
| - _writeSplit(_rules.last, _nesting.blockNesting,
|
| - isDouble: isDouble, spaceWhenUnsplit: space);
|
| + /// If [nest] is `false`, ignores any current expression nesting. Otherwise,
|
| + /// uses the current nesting level. If unsplit, it expands to a space if
|
| + /// [space] is `true`.
|
| + Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) =>
|
| + _writeSplit(_rules.last,
|
| + flushLeft: flushLeft, isDouble: isDouble, nest: nest, space: space);
|
|
|
| /// Outputs the series of [comments] and associated whitespace that appear
|
| /// before [token] (which is not written by this).
|
| @@ -257,9 +247,9 @@ class ChunkBuilder {
|
| } else {
|
| // The comment starts a line, so make sure it stays on its own line.
|
| _writeHardSplit(
|
| - nest: true,
|
| flushLeft: comment.flushLeft,
|
| - double: comment.linesBefore > 1);
|
| + isDouble: comment.linesBefore > 1,
|
| + nest: true);
|
| }
|
|
|
| _writeText(comment.text);
|
| @@ -291,7 +281,7 @@ class ChunkBuilder {
|
| }
|
| }
|
|
|
| - if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1);
|
| + if (linesAfter > 0) _writeHardSplit(isDouble: linesAfter > 1, nest: true);
|
| }
|
|
|
| // If the comment has text following it (aside from a grouping character),
|
| @@ -368,15 +358,15 @@ class ChunkBuilder {
|
| var span = new Span(openSpan.cost);
|
| for (var i = openSpan.start; i < end; i++) {
|
| var chunk = _chunks[i];
|
| - if (!chunk.isHardSplit) chunk.spans.add(span);
|
| + if (!chunk.rule.isHardened) chunk.spans.add(span);
|
| }
|
| }
|
|
|
| /// Starts a new [Rule].
|
| ///
|
| - /// If omitted, defaults to a new [SimpleRule].
|
| + /// If omitted, defaults to a new [Rule].
|
| void startRule([Rule rule]) {
|
| - if (rule == null) rule = new SimpleRule();
|
| + if (rule == null) rule = new Rule();
|
|
|
| // See if any of the rules that contain this one care if it splits.
|
| _rules.forEach((outer) => outer.contain(rule));
|
| @@ -390,9 +380,9 @@ class ChunkBuilder {
|
| /// first operand but not get forced to split if a comment appears before the
|
| /// entire expression.
|
| ///
|
| - /// If [rule] is omitted, defaults to a new [SimpleRule].
|
| + /// If [rule] is omitted, defaults to a new [Rule].
|
| void startLazyRule([Rule rule]) {
|
| - if (rule == null) rule = new SimpleRule();
|
| + if (rule == null) rule = new Rule();
|
|
|
| _lazyRules.add(rule);
|
| }
|
| @@ -431,8 +421,14 @@ class ChunkBuilder {
|
| ///
|
| /// Expressions that are more nested will get increased indentation when split
|
| /// if the previous line has a lower level of nesting.
|
| - void unnest() {
|
| + ///
|
| + /// If [now] is `false`, does not commit the nesting change until after the
|
| + /// next chunk of text is written.
|
| + void unnest({bool now}) {
|
| + if (now == null) now = true;
|
| +
|
| _nesting.unnest();
|
| + if (now) _nesting.commitNesting();
|
| }
|
|
|
| /// Marks the selection starting point as occurring [fromEnd] characters to
|
| @@ -469,9 +465,12 @@ class ChunkBuilder {
|
| /// Starts a new block as a child of the current chunk.
|
| ///
|
| /// Nested blocks are handled using their own independent [LineWriter].
|
| - ChunkBuilder startBlock() {
|
| + ChunkBuilder startBlock(Chunk argumentChunk) {
|
| + var chunk = _chunks.last;
|
| + chunk.makeBlock(argumentChunk);
|
| +
|
| var builder =
|
| - new ChunkBuilder._(this, _formatter, _source, _chunks.last.blockChunks);
|
| + new ChunkBuilder._(this, _formatter, _source, chunk.block.chunks);
|
|
|
| // A block always starts off indented one level.
|
| builder.indent();
|
| @@ -488,7 +487,7 @@ class ChunkBuilder {
|
| /// `true`, the block is considered to always split.
|
| ///
|
| /// Returns the previous writer for the surrounding block.
|
| - ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) {
|
| + ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
|
| _divideChunks();
|
|
|
| // If we don't already know if the block is going to split, see if it
|
| @@ -502,21 +501,33 @@ class ChunkBuilder {
|
| break;
|
| }
|
|
|
| - if (chunk.isHardSplit && chunk.rule != ignoredSplit) {
|
| + if (chunk.rule != null &&
|
| + chunk.rule.isHardened &&
|
| + chunk.rule != ignoredSplit) {
|
| forceSplit = true;
|
| break;
|
| }
|
| }
|
| }
|
|
|
| + _parent._endChildBlock(
|
| + firstFlushLeft: _firstFlushLeft, forceSplit: forceSplit);
|
| +
|
| + return _parent;
|
| + }
|
| +
|
| + /// Finishes off the last chunk in a child block of this parent.
|
| + void _endChildBlock({bool firstFlushLeft, bool forceSplit}) {
|
| // If there is a hard newline within the block, force the surrounding rule
|
| // for it so that we apply that constraint.
|
| - if (forceSplit) _parent.forceRules();
|
| + if (forceSplit) forceRules();
|
|
|
| // Write the split for the block contents themselves.
|
| - _parent._writeSplit(_parent._rules.last, _parent._blockArgumentNesting.last,
|
| - flushLeft: _firstFlushLeft);
|
| - return _parent;
|
| + var chunk = _chunks.last;
|
| + chunk.applySplit(rule, _nesting.indentation, _blockArgumentNesting.last,
|
| + flushLeft: firstFlushLeft);
|
| +
|
| + if (chunk.rule.isHardened) _handleHardSplit();
|
| }
|
|
|
| /// Finishes writing and returns a [SourceCode] containing the final output
|
| @@ -577,11 +588,11 @@ class ChunkBuilder {
|
| break;
|
|
|
| case Whitespace.newlineFlushLeft:
|
| - _writeHardSplit(nest: true, flushLeft: true);
|
| + _writeHardSplit(flushLeft: true, nest: true);
|
| break;
|
|
|
| case Whitespace.twoNewlines:
|
| - _writeHardSplit(double: true);
|
| + _writeHardSplit(isDouble: true);
|
| break;
|
|
|
| case Whitespace.spaceOrNewline:
|
| @@ -661,46 +672,38 @@ class ChunkBuilder {
|
| /// Appends a hard split with the current indentation and nesting (the latter
|
| /// only if [nest] is `true`).
|
| ///
|
| - /// If [double] is `true` or `false`, forces a since or double line to be
|
| + /// If [double] is `true` or `false`, forces a single or double line to be
|
| /// output. Otherwise, it is left indeterminate.
|
| ///
|
| /// If [flushLeft] is `true`, then the split will always cause the next line
|
| /// to be at column zero. Otherwise, it uses the normal indentation and
|
| /// nesting behavior.
|
| - void _writeHardSplit({bool nest: false, bool double, bool flushLeft}) {
|
| + void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
|
| // A hard split overrides any other whitespace.
|
| _pendingWhitespace = null;
|
| - _writeSplit(new HardSplitRule(), nest ? null : _nesting.blockNesting,
|
| - flushLeft: flushLeft, isDouble: double);
|
| + _writeSplit(new Rule.hard(),
|
| + flushLeft: flushLeft, isDouble: isDouble, nest: nest);
|
| }
|
|
|
| /// Ends the current chunk (if any) with the given split information.
|
| ///
|
| /// Returns the chunk.
|
| - Chunk _writeSplit(Rule rule, NestingLevel nesting,
|
| - {bool flushLeft, bool isDouble, bool spaceWhenUnsplit}) {
|
| + Chunk _writeSplit(Rule rule,
|
| + {bool flushLeft, bool isDouble, bool nest, bool space}) {
|
| + if (nest == null) nest = true;
|
| +
|
| if (_chunks.isEmpty) {
|
| if (flushLeft != null) _firstFlushLeft = flushLeft;
|
|
|
| return null;
|
| }
|
|
|
| - if (nesting == null) nesting = _nesting.nesting;
|
| + _chunks.last.applySplit(rule, _nesting.indentation,
|
| + nest ? _nesting.nesting : new NestingLevel(),
|
| + flushLeft: flushLeft, isDouble: isDouble, space: space);
|
|
|
| - var chunk = _chunks.last;
|
| - chunk.applySplit(rule, _nesting.indentation, nesting,
|
| - flushLeft: flushLeft,
|
| - isDouble: isDouble,
|
| - spaceWhenUnsplit: spaceWhenUnsplit);
|
| -
|
| - // Keep track of which chunks are owned by the rule.
|
| - if (rule is! HardSplitRule) {
|
| - _ruleChunks.putIfAbsent(rule, () => []).add(_chunks.length - 1);
|
| - }
|
| -
|
| - if (chunk.isHardSplit) _handleHardSplit();
|
| -
|
| - return chunk;
|
| + if (_chunks.last.rule.isHardened) _handleHardSplit();
|
| + return _chunks.last;
|
| }
|
|
|
| /// Writes [text] to either the current chunk or a new one if the current
|
| @@ -716,14 +719,13 @@ class ChunkBuilder {
|
| /// Returns true if we can divide the chunks at [index] and line split the
|
| /// ones before and after that separately.
|
| bool _canDivideAt(int i) {
|
| + // Don't divide after the last chunk.
|
| + if (i == _chunks.length - 1) return false;
|
| +
|
| var chunk = _chunks[i];
|
| - if (!chunk.isHardSplit) return false;
|
| + if (!chunk.rule.isHardened) return false;
|
| if (chunk.nesting.isNested) return false;
|
| - if (chunk.blockChunks.isNotEmpty) return false;
|
| -
|
| - // Make sure we don't split the line in the middle of a rule.
|
| - var chunks = _ruleChunks[chunk.rule];
|
| - if (chunks != null && chunks.any((other) => other > i)) return false;
|
| + if (chunk.isBlock) return false;
|
|
|
| return true;
|
| }
|
| @@ -765,18 +767,16 @@ class ChunkBuilder {
|
| void _hardenRules() {
|
| if (_hardSplitRules.isEmpty) return;
|
|
|
| - // Harden all of the rules that are constrained by [rules] as well.
|
| - var hardenedRules = new Set();
|
| walkConstraints(rule) {
|
| - if (hardenedRules.contains(rule)) return;
|
| - hardenedRules.add(rule);
|
| + rule.harden();
|
|
|
| // Follow this rule's constraints, recursively.
|
| - for (var other in _ruleChunks.keys) {
|
| + for (var other in rule.constrainedRules) {
|
| if (other == rule) continue;
|
|
|
| - if (rule.constrain(rule.fullySplitValue, other) ==
|
| - other.fullySplitValue) {
|
| + if (!other.isHardened &&
|
| + rule.constrain(rule.fullySplitValue, other) ==
|
| + other.fullySplitValue) {
|
| walkConstraints(other);
|
| }
|
| }
|
| @@ -786,10 +786,11 @@ class ChunkBuilder {
|
| walkConstraints(rule);
|
| }
|
|
|
| - // Harden every chunk that uses one of these rules.
|
| + // Discard spans in hardened chunks since we know for certain they will
|
| + // split anyway.
|
| for (var chunk in _chunks) {
|
| - if (hardenedRules.contains(chunk.rule)) {
|
| - chunk.harden();
|
| + if (chunk.rule != null && chunk.rule.isHardened) {
|
| + chunk.spans.clear();
|
| }
|
| }
|
| }
|
|
|