anchor.diff:        builder.endSelectionFromEnd(text.length - end);
       }
     }
  +
  +  /// Returns the number of characters past [offset] in the source where the
  +  /// selection start appears if it appears before `offset + length`.
  +  ///
  +  /// Returns `null` if the selection start has already been processed or is
  +  /// not within that range.
  +  int _getSelectionStartWithin(int offset, int length) {
  +    // If there is no selection, do nothing.
  +    if (_source.selectionStart == null) return null;
  +
  +    // If we've already passed it, don't consider it again.
  +    if (_passedSelectionStart) return null;
  +
  +    var start = _source.selectionStart - offset;
  +
  +    // If it started in whitespace before this text, push it forward to the
  +    // beginning of the non-whitespace text.
  +    if (start < 0) start = 0;
  +
  +    // If we haven't reached it yet, don't consider it.
  +    if (start >= length) return null;
  +
  +    // We found it.
  +    _passedSelectionStart = true;
  +
  +    return start;
  +  }
  +
  +  /// Returns the number of characters past [offset] in the source where the
  +  /// selection endpoint appears if it appears before `offset + length`.
  +  ///
  +  /// Returns `null` if the selection endpoint has already been processed or is
  +  /// not within that range.
  +  int _getSelectionEndWithin(int offset, int length) {
  +    // If there is no selection, do nothing.
  +    if (_source.selectionLength == null) return null;
  +
  +    // If we've already passed it, don't consider it again.
  +    if (_passedSelectionEnd) return null;
  +
  +    var end = _findSelectionEnd() - offset;
  +
  +    // If it started in whitespace before this text, push it forward to the
  +    // beginning of the non-whitespace text.
  +    if (end < 0) end = 0;
  +
  +    // If we haven't reached it yet, don't consider it.
  +    if (end > length) return null;
  +
  +    if (end == length && _findSelectionEnd() == _source.selectionStart) {
  +      return null;
  +    }
  +
  +    // We found it.
  +    _passedSelectionEnd = true;
  +
  +    return end;
  +  }
  +
  +  /// Calculates the character offset in the source text of the end of the
  +  /// selection.
  +  ///
  +  /// Removes any trailing whitespace from the selection.
  +  int _findSelectionEnd() {
  +    if (_selectionEnd != null) return _selectionEnd;
  +
  +    _selectionEnd = _source.selectionStart + _source.selectionLength;
  +
  +    // If the selection bumps to the end of the source, pin it there.
  +    if (_selectionEnd == _source.text.length) return _selectionEnd;
  +
  +    // Trim off any trailing whitespace. We want the selection to "rubberband"
  +    // around the selected non-whitespace tokens since the whitespace will
  +    // be munged by the formatter itself.
  +    while (_selectionEnd > _source.selectionStart) {
  +      // Stop if we hit anything other than space, tab, newline or carriage
  +      // return.
  +      var char = _source.text.codeUnitAt(_selectionEnd - 1);
  +      if (char != 0x20 && char != 0x09 && char != 0x0a && char != 0x0d) {
  +        break;
  +      }
  +
  +      _selectionEnd--;
  +    }
  +
  +    return _selectionEnd;
  +  }
  +
  +  /// Gets the 1-based line number that the beginning of [token] lies on.
  +  int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
  +
  +  /// Gets the 1-based line number that the end of [token] lies on.
  +  int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
  +
  +  /// Gets the 1-based column number that the beginning of [token] lies on.
  +  int _startColumn(Token token) =>
  +      _lineInfo.getLocation(token.offset).columnNumber;
   }
  
positive.code:     builder = new ChunkBuilder(_formatter, _source);
    }
  
    /// Marks the collection literal that starts with [leftBracket] as being
    /// controlled by [argumentList].
    ///
    /// When the collection is visited, [argumentList] will determine the
    /// indentation and splitting rule for the collection.
    void beforeCollection(Token leftBracket, ArgumentSublist argumentList) {
      _collectionArgumentLists[leftBracket] = argumentList;
    }
  
    /// Visit the index part of [node], excluding the target.
    ///
    /// Called by [CallChainVisitor] to handle index expressions in the middle of
    /// call chains.
    void finishIndexExpression(IndexExpression node) {
      if (node.target is IndexExpression) {
        // Edge case: On a chain of [] accesses, allow splitting between them.
        // Produces nicer output in cases like:
        //
        //     someJson['property']['property']['property']['property']...
        soloZeroSplit();
      }
  
      builder.startSpan();
      token(node.leftBracket);
      soloZeroSplit();
      visit(node.index);
      token(node.rightBracket);
      builder.endSpan();
    }
  
    /// Emit the given [modifier] if it's non null, followed by non-breaking
    /// whitespace.
    void modifier(Token modifier) {
      token(modifier, after: space);
    }
  
    /// Emit a single mandatory newline.
    void newline() {
      builder.writeWhitespace(Whitespace.newline);
    }
  
    /// Allow either one or two newlines to be emitted before the next
    /// non-whitespace token based on whether more than one newline exists in the
    /// source between the last token and the next one.
    void oneOrTwoNewlines() {
      builder.writeWhitespace(Whitespace.oneOrTwoNewlines);
    }
  
    /// Runs the visitor on [node], formatting its contents.
    ///
    /// Returns a [SourceCode] containing the resulting formatted source and
  
negative.code:     if (node.target is MethodInvocation) builder.unnest();
    }
  
    /// Whether a cascade should be allowed to be inline as opposed to one
    /// expression per line.
    bool _allowInlineCascade(List<Expression> sections) {
      if (sections.length < 2) return true;
  
      var name;
      // We could be more forgiving about what constitutes sections with
      // consistent names but for now we require all sections to have the same
      // method name.
      for (var expression in sections) {
        if (expression is MethodInvocation) {
          if (name == null) {
            name = expression.methodName.name;
          } else if (name != expression.methodName.name) {
            return false;
          }
        } else {
          return false;
        }
      }
      return true;
    }
  
    visitCatchClause(CatchClause node) {
      token(node.onKeyword, after: space);
      visit(node.exceptionType);
  