| Index: dart_style/lib/src/chunk_builder.dart | 
| diff --git a/dart_style/lib/src/chunk_builder.dart b/dart_style/lib/src/chunk_builder.dart | 
| deleted file mode 100644 | 
| index c7ff91bb200efe5ed2b961456910e6b34da1e193..0000000000000000000000000000000000000000 | 
| --- a/dart_style/lib/src/chunk_builder.dart | 
| +++ /dev/null | 
| @@ -1,796 +0,0 @@ | 
| -// 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.chunk_builder; | 
| - | 
| -import 'chunk.dart'; | 
| -import 'dart_formatter.dart'; | 
| -import 'debug.dart' as debug; | 
| -import 'line_writer.dart'; | 
| -import 'nesting_level.dart'; | 
| -import 'nesting_builder.dart'; | 
| -import 'rule/rule.dart'; | 
| -import 'source_code.dart'; | 
| -import 'whitespace.dart'; | 
| - | 
| -/// Takes the incremental serialized output of [SourceVisitor]--the source text | 
| -/// along with any comments and preserved whitespace--and produces a coherent | 
| -/// tree of [Chunk]s which can then be split into physical lines. | 
| -/// | 
| -/// Keeps track of leading indentation, expression nesting, and all of the hairy | 
| -/// code required to seamlessly integrate existing comments into the pure | 
| -/// output produced by [SourceVisitor]. | 
| -class ChunkBuilder { | 
| -  final DartFormatter _formatter; | 
| - | 
| -  /// The builder for the code surrounding the block that this writer is for, or | 
| -  /// `null` if this is writing the top-level code. | 
| -  final ChunkBuilder _parent; | 
| - | 
| -  final SourceCode _source; | 
| - | 
| -  final List<Chunk> _chunks; | 
| - | 
| -  /// The whitespace that should be written to [_chunks] before the next | 
| -  ///  non-whitespace token. | 
| -  /// | 
| -  /// This ensures that changes to indentation and nesting also apply to the | 
| -  /// most recent split, even if the visitor "creates" the split before changing | 
| -  /// indentation or nesting. | 
| -  Whitespace _pendingWhitespace = Whitespace.none; | 
| - | 
| -  /// The nested stack of rules that are currently in use. | 
| -  /// | 
| -  /// New chunks are implicitly split by the innermost rule when the chunk is | 
| -  /// ended. | 
| -  final _rules = <Rule>[]; | 
| - | 
| -  /// The set of rules known to contain hard splits that will in turn force | 
| -  /// these rules to harden. | 
| -  /// | 
| -  /// This is accumulated lazily while chunks are being built. Then, once they | 
| -  /// are all done, the rules are all hardened. We do this later because some | 
| -  /// rules may not have all of their constraints fully wired up until after | 
| -  /// the hard split appears. For example, a hard split in a positional | 
| -  /// argument list needs to force the named arguments to split too, but we | 
| -  /// don't create that rule until after the positional arguments are done. | 
| -  final _hardSplitRules = new Set<Rule>(); | 
| - | 
| -  /// The list of rules that are waiting until the next whitespace has been | 
| -  /// 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>[]; | 
| - | 
| -  /// The current state. | 
| -  final _nesting = new NestingBuilder(); | 
| - | 
| -  /// The stack of nesting levels where block arguments may start. | 
| -  /// | 
| -  /// A block argument's contents will nest at the last level in this stack. | 
| -  final _blockArgumentNesting = <NestingLevel>[]; | 
| - | 
| -  /// The index of the "current" chunk being written. | 
| -  /// | 
| -  /// If the last chunk is still being appended to, this is its index. | 
| -  /// Otherwise, it is the index of the next chunk which will be created. | 
| -  int get _currentChunkIndex { | 
| -    if (_chunks.isEmpty) return 0; | 
| -    if (_chunks.last.canAddText) return _chunks.length - 1; | 
| -    return _chunks.length; | 
| -  } | 
| - | 
| -  /// Whether or not there was a leading comment that was flush left before any | 
| -  /// other content was written. | 
| -  /// | 
| -  /// This is used when writing child blocks to make the parent chunk have the | 
| -  /// right flush left value when a comment appears immediately inside the | 
| -  /// block. | 
| -  bool _firstFlushLeft = false; | 
| - | 
| -  /// Whether there is pending whitespace that depends on the number of | 
| -  /// newlines in the source. | 
| -  /// | 
| -  /// This is used to avoid calculating the newlines between tokens unless | 
| -  /// actually needed since doing so is slow when done between every single | 
| -  /// token pair. | 
| -  bool get needsToPreserveNewlines => | 
| -      _pendingWhitespace == Whitespace.oneOrTwoNewlines || | 
| -          _pendingWhitespace == Whitespace.spaceOrNewline || | 
| -          _pendingWhitespace == Whitespace.splitOrNewline; | 
| - | 
| -  /// The number of characters of code that can fit in a single line. | 
| -  int get pageWidth => _formatter.pageWidth; | 
| - | 
| -  /// The current innermost rule. | 
| -  Rule get rule => _rules.last; | 
| - | 
| -  ChunkBuilder(this._formatter, this._source) | 
| -      : _parent = null, | 
| -        _chunks = [] { | 
| -    indent(_formatter.indent); | 
| -    startBlockArgumentNesting(); | 
| -  } | 
| - | 
| -  ChunkBuilder._(this._parent, this._formatter, this._source, this._chunks) { | 
| -    startBlockArgumentNesting(); | 
| -  } | 
| - | 
| -  /// Writes [string], the text for a single token, to the output. | 
| -  /// | 
| -  /// By default, this also implicitly adds one level of nesting if we aren't | 
| -  /// currently nested at all. We do this here so that if a comment appears | 
| -  /// after any token within a statement or top-level form and that comment | 
| -  /// leads to splitting, we correctly nest. Even pathological cases like: | 
| -  /// | 
| -  /// | 
| -  ///     import // comment | 
| -  ///         "this_gets_nested.dart"; | 
| -  /// | 
| -  /// If we didn't do this here, we'd have to call [nestExpression] after the | 
| -  /// first token of practically every grammar production. | 
| -  void write(String string) { | 
| -    _emitPendingWhitespace(); | 
| -    _writeText(string); | 
| - | 
| -    _lazyRules.forEach(startRule); | 
| -    _lazyRules.clear(); | 
| - | 
| -    _nesting.commitNesting(); | 
| -  } | 
| - | 
| -  /// Writes a [WhitespaceChunk] of [type]. | 
| -  void writeWhitespace(Whitespace type) { | 
| -    _pendingWhitespace = type; | 
| -  } | 
| - | 
| -  /// 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); | 
| - | 
| -  /// Outputs the series of [comments] and associated whitespace that appear | 
| -  /// before [token] (which is not written by this). | 
| -  /// | 
| -  /// The list contains each comment as it appeared in the source between the | 
| -  /// last token written and the next one that's about to be written. | 
| -  /// | 
| -  /// [linesBeforeToken] is the number of lines between the last comment (or | 
| -  /// previous token if there are no comments) and the next token. | 
| -  void writeComments( | 
| -      List<SourceComment> comments, int linesBeforeToken, String token) { | 
| -    // Corner case: if we require a blank line, but there exists one between | 
| -    // some of the comments, or after the last one, then we don't need to | 
| -    // enforce one before the first comment. Example: | 
| -    // | 
| -    //     library foo; | 
| -    //     // comment | 
| -    // | 
| -    //     class Bar {} | 
| -    // | 
| -    // Normally, a blank line is required after `library`, but since there is | 
| -    // one after the comment, we don't need one before it. This is mainly so | 
| -    // that commented out directives stick with their preceding group. | 
| -    if (_pendingWhitespace == Whitespace.twoNewlines && | 
| -        comments.first.linesBefore < 2) { | 
| -      if (linesBeforeToken > 1) { | 
| -        _pendingWhitespace = Whitespace.newline; | 
| -      } else { | 
| -        for (var i = 1; i < comments.length; i++) { | 
| -          if (comments[i].linesBefore > 1) { | 
| -            _pendingWhitespace = Whitespace.newline; | 
| -            break; | 
| -          } | 
| -        } | 
| -      } | 
| -    } | 
| - | 
| -    // Corner case: if the comments are completely inline (i.e. just a series | 
| -    // of block comments with no newlines before, after, or between them), then | 
| -    // they will eat any pending newlines. Make sure that doesn't happen by | 
| -    // putting the pending whitespace before the first comment and moving them | 
| -    // to their own line. Turns this: | 
| -    // | 
| -    //     library foo; /* a */ /* b */ import 'a.dart'; | 
| -    // | 
| -    // into: | 
| -    // | 
| -    //     library foo; | 
| -    // | 
| -    //     /* a */ /* b */ | 
| -    //     import 'a.dart'; | 
| -    if (linesBeforeToken == 0 && | 
| -        comments.every((comment) => comment.isInline)) { | 
| -      if (_pendingWhitespace.minimumLines > 0) { | 
| -        comments.first.linesBefore = _pendingWhitespace.minimumLines; | 
| -        linesBeforeToken = 1; | 
| -      } | 
| -    } | 
| - | 
| -    // Write each comment and the whitespace between them. | 
| -    for (var i = 0; i < comments.length; i++) { | 
| -      var comment = comments[i]; | 
| - | 
| -      preserveNewlines(comment.linesBefore); | 
| - | 
| -      // Don't emit a space because we'll handle it below. If we emit it here, | 
| -      // we may get a trailing space if the comment needs a line before it. | 
| -      if (_pendingWhitespace == Whitespace.space) { | 
| -        _pendingWhitespace = Whitespace.none; | 
| -      } | 
| -      _emitPendingWhitespace(); | 
| - | 
| -      if (comment.linesBefore == 0) { | 
| -        // If we're sitting on a split, move the comment before it to adhere it | 
| -        // to the preceding text. | 
| -        if (_shouldMoveCommentBeforeSplit(comment.text)) { | 
| -          _chunks.last.allowText(); | 
| -        } | 
| - | 
| -        // The comment follows other text, so we need to decide if it gets a | 
| -        // space before it or not. | 
| -        if (_needsSpaceBeforeComment(isLineComment: comment.isLineComment)) { | 
| -          _writeText(" "); | 
| -        } | 
| -      } 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); | 
| -      } | 
| - | 
| -      _writeText(comment.text); | 
| - | 
| -      if (comment.selectionStart != null) { | 
| -        startSelectionFromEnd(comment.text.length - comment.selectionStart); | 
| -      } | 
| - | 
| -      if (comment.selectionEnd != null) { | 
| -        endSelectionFromEnd(comment.text.length - comment.selectionEnd); | 
| -      } | 
| - | 
| -      // Make sure there is at least one newline after a line comment and allow | 
| -      // one or two after a block comment that has nothing after it. | 
| -      var linesAfter; | 
| -      if (i < comments.length - 1) { | 
| -        linesAfter = comments[i + 1].linesBefore; | 
| -      } else { | 
| -        linesAfter = linesBeforeToken; | 
| - | 
| -        // Always force a newline after multi-line block comments. Prevents | 
| -        // mistakes like: | 
| -        // | 
| -        //     /** | 
| -        //      * Some doc comment. | 
| -        //      */ someFunction() { ... } | 
| -        if (linesAfter == 0 && comments.last.text.contains("\n")) { | 
| -          linesAfter = 1; | 
| -        } | 
| -      } | 
| - | 
| -      if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1); | 
| -    } | 
| - | 
| -    // If the comment has text following it (aside from a grouping character), | 
| -    // it needs a trailing space. | 
| -    if (_needsSpaceAfterLastComment(comments, token)) { | 
| -      _pendingWhitespace = Whitespace.space; | 
| -    } | 
| - | 
| -    preserveNewlines(linesBeforeToken); | 
| -  } | 
| - | 
| -  /// If the current pending whitespace allows some source discretion, pins | 
| -  /// that down given that the source contains [numLines] newlines at that | 
| -  /// point. | 
| -  void preserveNewlines(int numLines) { | 
| -    // If we didn't know how many newlines the user authored between the last | 
| -    // token and this one, now we do. | 
| -    switch (_pendingWhitespace) { | 
| -      case Whitespace.spaceOrNewline: | 
| -        if (numLines > 0) { | 
| -          _pendingWhitespace = Whitespace.nestedNewline; | 
| -        } else { | 
| -          _pendingWhitespace = Whitespace.space; | 
| -        } | 
| -        break; | 
| - | 
| -      case Whitespace.splitOrNewline: | 
| -        if (numLines > 0) { | 
| -          _pendingWhitespace = Whitespace.nestedNewline; | 
| -        } else { | 
| -          _pendingWhitespace = Whitespace.none; | 
| -          split(space: true); | 
| -        } | 
| -        break; | 
| - | 
| -      case Whitespace.oneOrTwoNewlines: | 
| -        if (numLines > 1) { | 
| -          _pendingWhitespace = Whitespace.twoNewlines; | 
| -        } else { | 
| -          _pendingWhitespace = Whitespace.newline; | 
| -        } | 
| -        break; | 
| -    } | 
| -  } | 
| - | 
| -  /// Creates a new indentation level [spaces] deeper than the current one. | 
| -  /// | 
| -  /// If omitted, [spaces] defaults to [Indent.block]. | 
| -  void indent([int spaces]) { | 
| -    _nesting.indent(spaces); | 
| -  } | 
| - | 
| -  /// Discards the most recent indentation level. | 
| -  void unindent() { | 
| -    _nesting.unindent(); | 
| -  } | 
| - | 
| -  /// Starts a new span with [cost]. | 
| -  /// | 
| -  /// Each call to this needs a later matching call to [endSpan]. | 
| -  void startSpan([int cost = Cost.normal]) { | 
| -    _openSpans.add(new OpenSpan(_currentChunkIndex, cost)); | 
| -  } | 
| - | 
| -  /// Ends the innermost span. | 
| -  void endSpan() { | 
| -    var openSpan = _openSpans.removeLast(); | 
| - | 
| -    // A span that just covers a single chunk can't be split anyway. | 
| -    var end = _currentChunkIndex; | 
| -    if (openSpan.start == end) return; | 
| - | 
| -    // Add the span to every chunk that can split it. | 
| -    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); | 
| -    } | 
| -  } | 
| - | 
| -  /// Starts a new [Rule]. | 
| -  /// | 
| -  /// If omitted, defaults to a new [SimpleRule]. | 
| -  void startRule([Rule rule]) { | 
| -    if (rule == null) rule = new SimpleRule(); | 
| - | 
| -    // See if any of the rules that contain this one care if it splits. | 
| -    _rules.forEach((outer) => outer.contain(rule)); | 
| -    _rules.add(rule); | 
| -  } | 
| - | 
| -  /// Starts a new [Rule] that comes into play *after* the next whitespace | 
| -  /// (including comments) is written. | 
| -  /// | 
| -  /// This is used for binary operators who want to start a rule before the | 
| -  /// 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]. | 
| -  void startLazyRule([Rule rule]) { | 
| -    if (rule == null) rule = new SimpleRule(); | 
| - | 
| -    _lazyRules.add(rule); | 
| -  } | 
| - | 
| -  /// Ends the innermost rule. | 
| -  void endRule() { | 
| -    _rules.removeLast(); | 
| -  } | 
| - | 
| -  /// Pre-emptively forces all of the current rules to become hard splits. | 
| -  /// | 
| -  /// This is called by [SourceVisitor] when it can determine that a rule will | 
| -  /// will always be split. Turning it (and the surrounding rules) into hard | 
| -  /// splits lets the writer break the output into smaller pieces for the line | 
| -  /// splitter, which helps performance and avoids failing on very large input. | 
| -  /// | 
| -  /// In particular, it's easy for the visitor to know that collections with a | 
| -  /// large number of items must split. Doing that early avoids crashing the | 
| -  /// splitter when it tries to recurse on huge collection literals. | 
| -  void forceRules() => _handleHardSplit(); | 
| - | 
| -  /// Begins a new expression nesting level [indent] spaces deeper than the | 
| -  /// current one if it splits. | 
| -  /// | 
| -  /// If [indent] is omitted, defaults to [Indent.expression]. If [now] is | 
| -  /// `true`, commits the nesting change immediately instead of waiting until | 
| -  /// after the next chunk of text is written. | 
| -  void nestExpression({int indent, bool now}) { | 
| -    if (now == null) now = false; | 
| - | 
| -    _nesting.nest(indent); | 
| -    if (now) _nesting.commitNesting(); | 
| -  } | 
| - | 
| -  /// Discards the most recent level of expression nesting. | 
| -  /// | 
| -  /// Expressions that are more nested will get increased indentation when split | 
| -  /// if the previous line has a lower level of nesting. | 
| -  void unnest() { | 
| -    _nesting.unnest(); | 
| -  } | 
| - | 
| -  /// Marks the selection starting point as occurring [fromEnd] characters to | 
| -  /// the left of the end of what's currently been written. | 
| -  /// | 
| -  /// It counts backwards from the end because this is called *after* the chunk | 
| -  /// of text containing the selection has been output. | 
| -  void startSelectionFromEnd(int fromEnd) { | 
| -    assert(_chunks.isNotEmpty); | 
| -    _chunks.last.startSelectionFromEnd(fromEnd); | 
| -  } | 
| - | 
| -  /// Marks the selection ending point as occurring [fromEnd] characters to the | 
| -  /// left of the end of what's currently been written. | 
| -  /// | 
| -  /// It counts backwards from the end because this is called *after* the chunk | 
| -  /// of text containing the selection has been output. | 
| -  void endSelectionFromEnd(int fromEnd) { | 
| -    assert(_chunks.isNotEmpty); | 
| -    _chunks.last.endSelectionFromEnd(fromEnd); | 
| -  } | 
| - | 
| -  /// Captures the current nesting level as marking where subsequent block | 
| -  /// arguments should start. | 
| -  void startBlockArgumentNesting() { | 
| -    _blockArgumentNesting.add(_nesting.currentNesting); | 
| -  } | 
| - | 
| -  /// Releases the last nesting level captured by [startBlockArgumentNesting]. | 
| -  void endBlockArgumentNesting() { | 
| -    _blockArgumentNesting.removeLast(); | 
| -  } | 
| - | 
| -  /// Starts a new block as a child of the current chunk. | 
| -  /// | 
| -  /// Nested blocks are handled using their own independent [LineWriter]. | 
| -  ChunkBuilder startBlock() { | 
| -    var builder = | 
| -        new ChunkBuilder._(this, _formatter, _source, _chunks.last.blockChunks); | 
| - | 
| -    // A block always starts off indented one level. | 
| -    builder.indent(); | 
| - | 
| -    return builder; | 
| -  } | 
| - | 
| -  /// Ends this [ChunkBuilder], which must have been created by [startBlock()]. | 
| -  /// | 
| -  /// Forces the chunk that owns the block to split if it can tell that the | 
| -  /// block contents will always split. It does that by looking for hard splits | 
| -  /// in the block. If [ignoredSplit] is given, that rule will be ignored | 
| -  /// when determining if a block contains a hard split. If [forceSplit] is | 
| -  /// `true`, the block is considered to always split. | 
| -  /// | 
| -  /// Returns the previous writer for the surrounding block. | 
| -  ChunkBuilder endBlock(HardSplitRule ignoredSplit, {bool forceSplit}) { | 
| -    _divideChunks(); | 
| - | 
| -    // If we don't already know if the block is going to split, see if it | 
| -    // contains any hard splits or is longer than a page. | 
| -    if (!forceSplit) { | 
| -      var length = 0; | 
| -      for (var chunk in _chunks) { | 
| -        length += chunk.length + chunk.unsplitBlockLength; | 
| -        if (length > _formatter.pageWidth) { | 
| -          forceSplit = true; | 
| -          break; | 
| -        } | 
| - | 
| -        if (chunk.isHardSplit && chunk.rule != ignoredSplit) { | 
| -          forceSplit = true; | 
| -          break; | 
| -        } | 
| -      } | 
| -    } | 
| - | 
| -    // 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(); | 
| - | 
| -    // Write the split for the block contents themselves. | 
| -    _parent._writeSplit(_parent._rules.last, _parent._blockArgumentNesting.last, | 
| -        flushLeft: _firstFlushLeft); | 
| -    return _parent; | 
| -  } | 
| - | 
| -  /// Finishes writing and returns a [SourceCode] containing the final output | 
| -  /// and updated selection, if any. | 
| -  SourceCode end() { | 
| -    _writeHardSplit(); | 
| -    _divideChunks(); | 
| - | 
| -    if (debug.traceChunkBuilder) { | 
| -      debug.log(debug.green("\nBuilt:")); | 
| -      debug.dumpChunks(0, _chunks); | 
| -      debug.log(); | 
| -    } | 
| - | 
| -    var writer = new LineWriter(_formatter, _chunks); | 
| -    var result = writer.writeLines(_formatter.indent, | 
| -        isCompilationUnit: _source.isCompilationUnit); | 
| - | 
| -    var selectionStart; | 
| -    var selectionLength; | 
| -    if (_source.selectionStart != null) { | 
| -      selectionStart = result.selectionStart; | 
| -      var selectionEnd = result.selectionEnd; | 
| - | 
| -      // If we haven't hit the beginning and/or end of the selection yet, they | 
| -      // must be at the very end of the code. | 
| -      if (selectionStart == null) selectionStart = writer.length; | 
| -      if (selectionEnd == null) selectionEnd = writer.length; | 
| - | 
| -      selectionLength = selectionEnd - selectionStart; | 
| -    } | 
| - | 
| -    return new SourceCode(result.text, | 
| -        uri: _source.uri, | 
| -        isCompilationUnit: _source.isCompilationUnit, | 
| -        selectionStart: selectionStart, | 
| -        selectionLength: selectionLength); | 
| -  } | 
| - | 
| -  /// Writes the current pending [Whitespace] to the output, if any. | 
| -  /// | 
| -  /// This should only be called after source lines have been preserved to turn | 
| -  /// any ambiguous whitespace into a concrete choice. | 
| -  void _emitPendingWhitespace() { | 
| -    // Output any pending whitespace first now that we know it won't be | 
| -    // trailing. | 
| -    switch (_pendingWhitespace) { | 
| -      case Whitespace.space: | 
| -        _writeText(" "); | 
| -        break; | 
| - | 
| -      case Whitespace.newline: | 
| -        _writeHardSplit(); | 
| -        break; | 
| - | 
| -      case Whitespace.nestedNewline: | 
| -        _writeHardSplit(nest: true); | 
| -        break; | 
| - | 
| -      case Whitespace.newlineFlushLeft: | 
| -        _writeHardSplit(nest: true, flushLeft: true); | 
| -        break; | 
| - | 
| -      case Whitespace.twoNewlines: | 
| -        _writeHardSplit(double: true); | 
| -        break; | 
| - | 
| -      case Whitespace.spaceOrNewline: | 
| -      case Whitespace.splitOrNewline: | 
| -      case Whitespace.oneOrTwoNewlines: | 
| -        // We should have pinned these down before getting here. | 
| -        assert(false); | 
| -        break; | 
| -    } | 
| - | 
| -    _pendingWhitespace = Whitespace.none; | 
| -  } | 
| - | 
| -  /// Returns `true` if the last chunk is a split that should be moved after the | 
| -  /// comment that is about to be written. | 
| -  bool _shouldMoveCommentBeforeSplit(String comment) { | 
| -    // Not if there is nothing before it. | 
| -    if (_chunks.isEmpty) return false; | 
| - | 
| -    // Multi-line comments are always pushed to the next line. | 
| -    if (comment.contains("\n")) return false; | 
| - | 
| -    // If the text before the split is an open grouping character, we don't | 
| -    // want to adhere the comment to that. | 
| -    var text = _chunks.last.text; | 
| -    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{"); | 
| -  } | 
| - | 
| -  /// Returns `true` if a space should be output between the end of the current | 
| -  /// output and the subsequent comment which is about to be written. | 
| -  /// | 
| -  /// This is only called if the comment is trailing text in the unformatted | 
| -  /// source. In most cases, a space will be output to separate the comment | 
| -  /// from what precedes it. This returns false if: | 
| -  /// | 
| -  /// *   This comment does begin the line in the output even if it didn't in | 
| -  ///     the source. | 
| -  /// *   The comment is a block comment immediately following a grouping | 
| -  ///     character (`(`, `[`, or `{`). This is to allow `foo(/* comment */)`, | 
| -  ///     et. al. | 
| -  bool _needsSpaceBeforeComment({bool isLineComment}) { | 
| -    // Not at the start of the file. | 
| -    if (_chunks.isEmpty) return false; | 
| - | 
| -    // Not at the start of a line. | 
| -    if (!_chunks.last.canAddText) return false; | 
| - | 
| -    var text = _chunks.last.text; | 
| -    if (text.endsWith("\n")) return false; | 
| - | 
| -    // Always put a space before line comments. | 
| -    if (isLineComment) return true; | 
| - | 
| -    // Block comments do not get a space if following a grouping character. | 
| -    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{"); | 
| -  } | 
| - | 
| -  /// Returns `true` if a space should be output after the last comment which | 
| -  /// was just written and the token that will be written. | 
| -  bool _needsSpaceAfterLastComment(List<SourceComment> comments, String token) { | 
| -    // Not if there are no comments. | 
| -    if (comments.isEmpty) return false; | 
| - | 
| -    // Not at the beginning of a line. | 
| -    if (!_chunks.last.canAddText) return false; | 
| - | 
| -    // Otherwise, it gets a space if the following token is not a delimiter or | 
| -    // the empty string, for EOF. | 
| -    return token != ")" && | 
| -        token != "]" && | 
| -        token != "}" && | 
| -        token != "," && | 
| -        token != ";" && | 
| -        token != ""; | 
| -  } | 
| - | 
| -  /// 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 | 
| -  /// 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}) { | 
| -    // A hard split overrides any other whitespace. | 
| -    _pendingWhitespace = null; | 
| -    _writeSplit(new HardSplitRule(), nest ? null : _nesting.blockNesting, | 
| -        flushLeft: flushLeft, isDouble: double); | 
| -  } | 
| - | 
| -  /// 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}) { | 
| -    if (_chunks.isEmpty) { | 
| -      if (flushLeft != null) _firstFlushLeft = flushLeft; | 
| - | 
| -      return null; | 
| -    } | 
| - | 
| -    if (nesting == null) nesting = _nesting.nesting; | 
| - | 
| -    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; | 
| -  } | 
| - | 
| -  /// Writes [text] to either the current chunk or a new one if the current | 
| -  /// chunk is complete. | 
| -  void _writeText(String text) { | 
| -    if (_chunks.isNotEmpty && _chunks.last.canAddText) { | 
| -      _chunks.last.appendText(text); | 
| -    } else { | 
| -      _chunks.add(new Chunk(text)); | 
| -    } | 
| -  } | 
| - | 
| -  /// Returns true if we can divide the chunks at [index] and line split the | 
| -  /// ones before and after that separately. | 
| -  bool _canDivideAt(int i) { | 
| -    var chunk = _chunks[i]; | 
| -    if (!chunk.isHardSplit) 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; | 
| - | 
| -    return true; | 
| -  } | 
| - | 
| -  /// Pre-processes the chunks after they are done being written by the visitor | 
| -  /// but before they are run through the line splitter. | 
| -  /// | 
| -  /// Marks ranges of chunks that can be line split independently to keep the | 
| -  /// batches we send to [LineSplitter] small. | 
| -  void _divideChunks() { | 
| -    // Harden all of the rules that we know get forced by containing hard | 
| -    // splits, along with all of the other rules they constrain. | 
| -    _hardenRules(); | 
| - | 
| -    // Now that we know where all of the divided chunk sections are, mark the | 
| -    // chunks. | 
| -    for (var i = 0; i < _chunks.length; i++) { | 
| -      _chunks[i].markDivide(_canDivideAt(i)); | 
| -    } | 
| -  } | 
| - | 
| -  /// Hardens the active rules when a hard split occurs within them. | 
| -  void _handleHardSplit() { | 
| -    if (_rules.isEmpty) return; | 
| - | 
| -    // If the current rule doesn't care, it will "eat" the hard split and no | 
| -    // others will care either. | 
| -    if (!_rules.last.splitsOnInnerRules) return; | 
| - | 
| -    // Start with the innermost rule. This will traverse the other rules it | 
| -    // constrains. | 
| -    _hardSplitRules.add(_rules.last); | 
| -  } | 
| - | 
| -  /// Replaces all of the previously hardened rules with hard splits, along | 
| -  /// with every rule that those constrain to also split. | 
| -  /// | 
| -  /// This should only be called after all chunks have been written. | 
| -  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); | 
| - | 
| -      // Follow this rule's constraints, recursively. | 
| -      for (var other in _ruleChunks.keys) { | 
| -        if (other == rule) continue; | 
| - | 
| -        if (rule.constrain(rule.fullySplitValue, other) == | 
| -            other.fullySplitValue) { | 
| -          walkConstraints(other); | 
| -        } | 
| -      } | 
| -    } | 
| - | 
| -    for (var rule in _hardSplitRules) { | 
| -      walkConstraints(rule); | 
| -    } | 
| - | 
| -    // Harden every chunk that uses one of these rules. | 
| -    for (var chunk in _chunks) { | 
| -      if (hardenedRules.contains(chunk.rule)) { | 
| -        chunk.harden(); | 
| -      } | 
| -    } | 
| -  } | 
| -} | 
|  |