anchor.diff:      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);
  
positive.code:     }
    }
  
    /// Gets the cost to split at an assignment (or `:` in the case of a named
    /// default value) with the given [rightHandSide].
    ///
    /// "Block-like" expressions (collections and cascades) bind a bit tighter
    /// because it looks better to have code like:
    ///
    ///     var list = [
    ///       element,
    ///       element,
    ///       element
    ///     ];
    ///
    ///     var builder = new SomeBuilderClass()
    ///       ..method()
    ///       ..method();
    ///
    /// over:
    ///
    ///     var list =
    ///         [element, element, element];
    ///
    ///     var builder =
    ///         new SomeBuilderClass()..method()..method();
    int _assignmentCost(Expression rightHandSide) {
      if (rightHandSide is ListLiteral) return Cost.assignBlock;
      if (rightHandSide is MapLiteral) return Cost.assignBlock;
      if (rightHandSide is CascadeExpression) return Cost.assignBlock;
  
      return Cost.assign;
    }
  
    /// Returns `true` if the collection withs [elements] delimited by
    /// [rightBracket] contains any line comments.
    ///
    /// This only looks for comments at the element boundary. Comments within an
    /// element are ignored.
    bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) {
      hasLineCommentBefore(token) {
        var comment = token.precedingComments;
        for (; comment != null; comment = comment.next) {
          if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true;
        }
  
        return false;
      }
  
      // Look before each element.
      for (var element in elements) {
        if (hasLineCommentBefore(element.beginToken)) return true;
      }
  
      // Look before the closing bracket.
      return hasLineCommentBefore(rightBracket);
    }
  
    /// Begins writing a literal body: a collection or block-bodied function
    /// expression.
    ///
    /// Writes the delimiter and then creates the [Rule] that handles splitting
    /// the body.
    void _startLiteralBody(Token leftBracket) {
      token(leftBracket);
  
      // See if this literal is associated with an argument list that wants to
      // handle splitting and indenting it. If not, we'll use a default rule.
      var rule;
      var argumentChunk;
      if (_collectionArgumentLists.containsKey(leftBracket)) {
        var argumentList = _collectionArgumentLists[leftBracket];
        rule = argumentList.collectionRule;
        argumentChunk = argumentList.previousSplit;
      }
  
      // Create a rule for whether or not to split the block contents.
      builder.startRule(rule);
  
      // Process the collection contents as a separate set of chunks.
      builder = builder.startBlock(argumentChunk);
    }
  
    /// Ends the literal body started by a call to [_startLiteralBody()].
    ///
    /// If [forceSplit] is `true`, forces the body to split. If [ignoredRule] is
    /// given, ignores that rule inside the body when determining if it should
    /// split.
    void _endLiteralBody(Token rightBracket,
        {Rule ignoredRule, bool forceSplit}) {
      if (forceSplit == null) forceSplit = false;
  
      // Put comments before the closing delimiter inside the block.
      var hasLeadingNewline = writePrecedingCommentsAndNewlines(rightBracket);
  
      builder = builder.endBlock(ignoredRule,
          forceSplit: hasLeadingNewline || forceSplit);
  
      builder.endRule();
  
      // Now write the delimiter itself.
      _writeText(rightBracket.lexeme, rightBracket.offset);
    }
  
    /// Visits a list of configurations in an import or export directive.
    void _visitConfigurations(NodeList<Configuration> configurations) {
      if (configurations.isEmpty) return;
  
      builder.startRule();
  
      for (var configuration in configurations) {
        split();
        visit(configuration);
      }
  
      builder.endRule();
    }
  
    /// Visits a "combinator".
    ///
    /// This is a [keyword] followed by a list of [nodes], with specific line
    /// splitting rules. As the name implies, this is used for [HideCombinator]
    /// and [ShowCombinator], but it also used for "with" and "implements"
    /// clauses in class declarations, which are formatted the same way.
    ///
    /// This assumes the current rule is a [CombinatorRule].
    void _visitCombinator(Token keyword, Iterable<AstNode> nodes) {
      // Allow splitting before the keyword.
      var rule = builder.rule as CombinatorRule;
      rule.addCombinator(split());
  
      builder.nestExpression();
      token(keyword);
  
      rule.addName(split());
      visitCommaSeparatedNodes(nodes, between: () => rule.addName(split()));
  
      builder.unnest();
    }
  
    /// Writes the simple statement or semicolon-delimited top-level declaration.
    ///
    /// Handles nesting if a line break occurs in the statement and writes the
    /// terminating semicolon. Invokes [body] which should write statement itself.
    void _simpleStatement(AstNode node, body()) {
      builder.nestExpression();
      body();
  
      // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base
      // type?
      token((node as dynamic).semicolon);
      builder.unnest();
    }
  
    /// 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;
    }
  
    /// Writes the beginning of a brace-delimited body and handles indenting and
    /// starting the rule used to split the contents.
    void _beginBody(Token leftBracket, {bool space: false}) {
      token(leftBracket);
  
      // Indent the body.
      builder.indent();
  
      // Split after the bracket.
      builder.startRule();
      builder.split(isDouble: false, nest: false, space: space);
    }
  
    /// Finishes the body started by a call to [_beginBody].
    void _endBody(Token rightBracket, {bool space: false}) {
      token(rightBracket, before: () {
        // Split before the closing bracket character.
        builder.unindent();
        builder.split(nest: false, space: space);
      });
  
      builder.endRule();
    }
  
    /// Returns `true` if [node] is immediately contained within an anonymous
    /// [FunctionExpression].
    bool _isInLambda(AstNode node) =>
        node.parent is FunctionExpression &&
        node.parent.parent is! FunctionDeclaration;
  
    /// Writes the string literal [string] to the output.
    ///
    /// Splits multiline strings into separate chunks so that the line splitter
  
negative.code:     });
    }
  
    /// Writes all formatted whitespace and comments that appear before [token].
    bool writePrecedingCommentsAndNewlines(Token token) {
      var comment = token.precedingComments;
  
      // For performance, avoid calculating newlines between tokens unless
      // actually needed.
      if (comment == null) {
        if (builder.needsToPreserveNewlines) {
          builder.preserveNewlines(_startLine(token) - _endLine(token.previous));
        }
  
        return false;
      }
  
      var previousLine = _endLine(token.previous);
      var tokenLine = _startLine(token);
  
      // Edge case: The analyzer includes the "\n" in the script tag's lexeme,
      // which confuses some of these calculations. We don't want to allow a
      // blank line between the script tag and a following comment anyway, so
      // just override the script tag's line.
      if (token.previous.type == TokenType.SCRIPT_TAG) previousLine = tokenLine;
  
      var comments = <SourceComment>[];
      while (comment != null) {
        var commentLine = _startLine(comment);
  
        // Don't preserve newlines at the top of the file.
        if (comment == token.precedingComments &&
            token.previous.type == TokenType.EOF) {
          previousLine = commentLine;
        }
  
        var text = comment.toString().trim();
        var linesBefore = commentLine - previousLine;
        var flushLeft = _startColumn(comment) == 1;
  
        if (text.startsWith("///") && !text.startsWith("////")) {
          // Line doc comments are always indented even if they were flush left.
          flushLeft = false;
  
          // Always add a blank line (if possible) before a doc comment block.
          if (comment == token.precedingComments) linesBefore = 2;
        }
  
        var sourceComment = new SourceComment(text, linesBefore,
            isLineComment: comment.type == TokenType.SINGLE_LINE_COMMENT,
            flushLeft: flushLeft);
  
        // If this comment contains either of the selection endpoints, mark them
        // in the comment.
        var start = _getSelectionStartWithin(comment.offset, comment.length);
        if (start != null) sourceComment.startSelection(start);
  
        var end = _getSelectionEndWithin(comment.offset, comment.length);
        if (end != null) sourceComment.endSelection(end);
  
        comments.add(sourceComment);
  
        previousLine = _endLine(comment);
        comment = comment.next;
      }
  
      builder.writeComments(comments, tokenLine - previousLine, token.lexeme);
  
      // TODO(rnystrom): This is wrong. Consider:
      //
      // [/* inline comment */
      //     // line comment
      //     element];
      return comments.first.linesBefore > 0;
    }
  
    /// Writes a zero-space split owned by the current rule.
    ///
    /// Returns the chunk the split was applied to.
    Chunk zeroSplit() => builder.split();
  
    /// 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;
    }
  
    /// Gets the cost to split at an assignment (or `:` in the case of a named
    /// default value) with the given [rightHandSide].
    ///
    /// "Block-like" expressions (collections and cascades) bind a bit tighter
    /// because it looks better to have code like:
    ///
    ///     var list = [
    ///       element,
    ///       element,
    ///       element
    ///     ];
    ///
    ///     var builder = new SomeBuilderClass()
    ///       ..method()
    ///       ..method();
    ///
    /// over:
    ///
    ///     var list =
    ///         [element, element, element];
    ///
    ///     var builder =
    ///         new SomeBuilderClass()..method()..method();
    int _assignmentCost(Expression rightHandSide) {
      if (rightHandSide is ListLiteral) return Cost.assignBlock;
      if (rightHandSide is MapLiteral) return Cost.assignBlock;
      if (rightHandSide is CascadeExpression) return Cost.assignBlock;
  
      return Cost.assign;
    }
  
    /// Writes the beginning of a brace-delimited body and handles indenting and
    /// starting the rule used to split the contents.
    void _beginBody(Token leftBracket, {bool space: false}) {
      token(leftBracket);
  
      // Indent the body.
      builder.indent();
  
      // Split after the bracket.
      builder.startRule();
      builder.split(isDouble: false, nest: false, space: space);
    }
  
    /// Returns `true` if the collection withs [elements] delimited by
    /// [rightBracket] contains any line comments.
    ///
    /// This only looks for comments at the element boundary. Comments within an
    /// element are ignored.
    bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) {
      hasLineCommentBefore(token) {
        var comment = token.precedingComments;
        for (; comment != null; comment = comment.next) {
          if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true;
        }
  
        return false;
      }
  
      // Look before each element.
      for (var element in elements) {
        if (hasLineCommentBefore(element.beginToken)) return true;
      }
  
      // Look before the closing bracket.
      return hasLineCommentBefore(rightBracket);
    }
  
    /// Finishes the body started by a call to [_beginBody].
    void _endBody(Token rightBracket, {bool space: false}) {
      token(rightBracket, before: () {
        // Split before the closing bracket character.
        builder.unindent();
        builder.split(nest: false, space: space);
      });
  
      builder.endRule();
    }
  
    /// Gets the 1-based line number that the end of [token] lies on.
    int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
  
    /// Ends the literal body started by a call to [_startLiteralBody()].
    ///
    /// If [forceSplit] is `true`, forces the body to split. If [ignoredRule] is
    /// given, ignores that rule inside the body when determining if it should
    /// split.
    void _endLiteralBody(Token rightBracket,
        {Rule ignoredRule, bool forceSplit}) {
      if (forceSplit == null) forceSplit = false;
  
      // Put comments before the closing delimiter inside the block.
      var hasLeadingNewline = writePrecedingCommentsAndNewlines(rightBracket);
  
      builder = builder.endBlock(ignoredRule,
          forceSplit: hasLeadingNewline || forceSplit);
  
      builder.endRule();
  
      // Now write the delimiter itself.
      _writeText(rightBracket.lexeme, rightBracket.offset);
    }
  
    /// 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;
    }
  
    /// 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;
    }
  
    /// 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 `true` if [node] is immediately contained within an anonymous
    /// [FunctionExpression].
    bool _isInLambda(AstNode node) =>
        node.parent is FunctionExpression &&
        node.parent.parent is! FunctionDeclaration;
  
    /// Writes the simple statement or semicolon-delimited top-level declaration.
    ///
    /// Handles nesting if a line break occurs in the statement and writes the
    /// terminating semicolon. Invokes [body] which should write statement itself.
    void _simpleStatement(AstNode node, body()) {
      builder.nestExpression();
      body();
  
      // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base
      // type?
      token((node as dynamic).semicolon);
      builder.unnest();
    }
  
    /// Gets the 1-based column number that the beginning of [token] lies on.
    int _startColumn(Token token) =>
        _lineInfo.getLocation(token.offset).columnNumber;
  
    /// Gets the 1-based line number that the beginning of [token] lies on.
    int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
  
    /// Begins writing a literal body: a collection or block-bodied function
    /// expression.
    ///
    /// Writes the delimiter and then creates the [Rule] that handles splitting
    /// the body.
    void _startLiteralBody(Token leftBracket) {
      token(leftBracket);
  
      // See if this literal is associated with an argument list that wants to
      // handle splitting and indenting it. If not, we'll use a default rule.
      var rule;
      var argumentChunk;
      if (_collectionArgumentLists.containsKey(leftBracket)) {
        var argumentList = _collectionArgumentLists[leftBracket];
        rule = argumentList.collectionRule;
        argumentChunk = argumentList.previousSplit;
      }
  
      // Create a rule for whether or not to split the block contents.
      builder.startRule(rule);
  
      // Process the collection contents as a separate set of chunks.
      builder = builder.startBlock(argumentChunk);
    }
  
    /// Visits the `=` and the following expression in any place where an `=`
  