Merge pull request #471 from dart-lang/readme

Merging the dartlang dartfmt page with this page.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 25e0937..b720884 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,11 +4,14 @@
 * Split the first `.` in a method chain if the target splits (#255).
 * Don't collapse states that differ by unbound rule constraints (#424).
 * Better handling for functions in method chains (#367, #398).
+* Better handling of large parameter metadata annotations (#387, #444).
+* Smarter splitting around collections in named parameters (#394).
 * Split calls if properties in a chain split (#399).
 * Don't allow splitting inside empty functions (#404).
 * Consider a rule live if it constrains a rule in the overflow line (#407).
 * Allow splitting in prefix expressions (#410).
 * Correctly constrain collections in argument lists (#420, #463, #465).
+* Better indentation of collection literals (#421, #469).
 * Only show a hidden directory once in the output (#428).
 * Allow splitting between type and variable name (#429, #439, #454).
 * Tweak splitting around assignment (#436, #437).
@@ -18,6 +21,7 @@
 * Handle loop statements without curly bodies better (#449).
 * Allow splitting before `get` and `set` (#462).
 * Add `--indent` to specify leading indent (#464).
+* Ensure collection elements line split separately (#474).
 * Handle index expressions in the middle of call chains.
 * Optimize splitting lines with many rules.
 
diff --git a/lib/src/argument_list_visitor.dart b/lib/src/argument_list_visitor.dart
index e8d3d8a..b071987 100644
--- a/lib/src/argument_list_visitor.dart
+++ b/lib/src/argument_list_visitor.dart
@@ -4,6 +4,8 @@
 
 library dart_style.src.argument_list_visitor;
 
+import 'dart:math' as math;
+
 import 'package:analyzer/analyzer.dart';
 import 'package:analyzer/src/generated/scanner.dart';
 
@@ -261,16 +263,6 @@
       }
     }
 
-    // If only some of the named arguments are collections, treat none of them
-    // specially. Avoids cases like:
-    //
-    //     function(
-    //         a: arg,
-    //         b: [
-    //       ...
-    //     ]);
-    if (trailingCollections < named.length) trailingCollections = 0;
-
     // Collections must all be a prefix or suffix of the argument list (and not
     // both).
     if (leadingCollections != collections.length) leadingCollections = 0;
@@ -290,7 +282,7 @@
 
   void visit(SourceVisitor visitor) {
     if (_collections.isNotEmpty) {
-      _collectionRule = new SimpleRule(Cost.splitCollections);
+      _collectionRule = new Rule(Cost.splitCollections);
     }
 
     var rule = _visitPositional(visitor);
@@ -308,69 +300,64 @@
           splitsOnInnerRules: _allArguments.length > 1 &&
               !_collections.containsKey(_positional.first));
     } else {
-      // Only count the positional bodies in the positional rule.
-      var leadingPositional = _leadingCollections;
-      if (_leadingCollections == _positional.length + _named.length) {
-        leadingPositional -= _named.length;
-      }
-
-      var trailingPositional = _trailingCollections - _named.length;
+      // Only count the collections in the positional rule.
+      var leadingCollections =
+          math.min(_leadingCollections, _positional.length);
+      var trailingCollections =
+          math.max(_trailingCollections - _named.length, 0);
       rule = new MultiplePositionalRule(
-          _collectionRule, leadingPositional, trailingPositional);
+          _collectionRule, leadingCollections, trailingCollections);
     }
 
-    visitor.builder.startRule(rule);
-
-    _previousSplit =
-        visitor.builder.split(space: !_isFirstArgument(_positional.first));
-    rule.beforeArgument(_previousSplit);
-
-    // Try to not split the arguments.
-    visitor.builder.startSpan(Cost.positionalArguments);
-
-    for (var argument in _positional) {
-      _visitArgument(visitor, rule, argument);
-
-      // Positional arguments split independently.
-      if (argument != _positional.last) {
-        _previousSplit = visitor.split();
-        rule.beforeArgument(_previousSplit);
-      }
-    }
-
-    visitor.builder.endSpan();
-    visitor.builder.endRule();
-
+    _visitArguments(visitor, _positional, rule);
     return rule;
   }
 
   /// Writes the named arguments, if any.
-  void _visitNamed(SourceVisitor visitor, PositionalRule rule) {
+  void _visitNamed(SourceVisitor visitor, PositionalRule positionalRule) {
     if (_named.isEmpty) return;
 
-    var positionalRule = rule;
-    var namedRule = new NamedRule(
-        containsCollections: _leadingCollections > _positional.length ||
-            _trailingCollections > 0);
-    visitor.builder.startRule(namedRule);
+    // Only count the collections in the named rule.
+    var leadingCollections =
+        math.max(_leadingCollections - _positional.length, 0);
+    var trailingCollections = math.min(_trailingCollections, _named.length);
+    var namedRule =
+        new NamedRule(_collectionRule, leadingCollections, trailingCollections);
 
     // Let the positional args force the named ones to split.
     if (positionalRule != null) {
       positionalRule.setNamedArgsRule(namedRule);
     }
 
-    // Split before the first named argument.
-    _previousSplit =
-        visitor.builder.split(space: !_isFirstArgument(_named.first));
-    namedRule.beforeArguments(_previousSplit);
+    _visitArguments(visitor, _named, namedRule);
+  }
 
-    for (var argument in _named) {
-      _visitArgument(visitor, namedRule, argument);
+  void _visitArguments(
+      SourceVisitor visitor, List<Expression> arguments, ArgumentRule rule) {
+    visitor.builder.startRule(rule);
+
+    // Split before the first argument.
+    _previousSplit =
+        visitor.builder.split(space: !_isFirstArgument(arguments.first));
+    rule.beforeArgument(_previousSplit);
+
+    // Try to not split the positional arguments.
+    if (arguments == _positional) {
+      visitor.builder.startSpan(Cost.positionalArguments);
+    }
+
+    for (var argument in arguments) {
+      _visitArgument(visitor, rule, argument);
 
       // Write the split.
-      if (argument != _named.last) _previousSplit = visitor.split();
+      if (argument != arguments.last) {
+        _previousSplit = visitor.split();
+        rule.beforeArgument(_previousSplit);
+      }
     }
 
+    if (arguments == _positional) visitor.builder.endSpan();
+
     visitor.builder.endRule();
   }
 
diff --git a/lib/src/call_chain_visitor.dart b/lib/src/call_chain_visitor.dart
index a7ab75e..7b7fb26 100644
--- a/lib/src/call_chain_visitor.dart
+++ b/lib/src/call_chain_visitor.dart
@@ -8,6 +8,7 @@
 
 import 'argument_list_visitor.dart';
 import 'rule/argument.dart';
+import 'rule/rule.dart';
 import 'source_visitor.dart';
 
 /// Helper class for [SourceVisitor] that handles visiting and writing a
@@ -307,22 +308,20 @@
 
     // Don't split right after a non-empty curly-bodied function.
     if (expression is FunctionExpression) {
-      var function = expression as FunctionExpression;
+      if (expression.body is! BlockFunctionBody) return false;
 
-      if (function.body is! BlockFunctionBody) return false;
-
-      return (function.body as BlockFunctionBody).block.statements.isEmpty;
+      return (expression.body as BlockFunctionBody).block.statements.isEmpty;
     }
 
     // If the expression ends in an argument list, base the splitting on the
     // last argument.
     var argumentList;
     if (expression is MethodInvocation) {
-      argumentList = (expression as MethodInvocation).argumentList;
+      argumentList = expression.argumentList;
     } else if (expression is InstanceCreationExpression) {
-      argumentList = (expression as InstanceCreationExpression).argumentList;
+      argumentList = expression.argumentList;
     } else if (expression is FunctionExpressionInvocation) {
-      argumentList = (expression as FunctionExpressionInvocation).argumentList;
+      argumentList = expression.argumentList;
     }
 
     // Any other kind of expression always splits.
@@ -420,7 +419,7 @@
     if (_ruleEnabled) return;
 
     // If the properties split, force the calls to split too.
-    var rule = new NamedRule();
+    var rule = new Rule();
     if (_propertyRule != null) _propertyRule.setNamedArgsRule(rule);
 
     if (lazy) {
diff --git a/lib/src/chunk.dart b/lib/src/chunk.dart
index a382501..da6d7bb 100644
--- a/lib/src/chunk.dart
+++ b/lib/src/chunk.dart
@@ -114,10 +114,6 @@
   Rule get rule => _rule;
   Rule _rule;
 
-  /// Whether this chunk is always followed by a newline or whether the line
-  /// splitter may choose to keep the next chunk on the same line.
-  bool get isHardSplit => _rule is HardSplitRule;
-
   /// Whether or not an extra blank line should be output after this chunk if
   /// it's split.
   ///
@@ -197,15 +193,6 @@
     _text += text;
   }
 
-  /// Forces this soft split to become a hard split.
-  ///
-  /// This is called on the soft splits owned by a rule that decides to harden
-  /// when it finds out another hard split occurs within its chunks.
-  void harden() {
-    _rule = new HardSplitRule();
-    spans.clear();
-  }
-
   /// Finishes off this chunk with the given [rule] and split information.
   ///
   /// This may be called multiple times on the same split since the splits
@@ -216,7 +203,7 @@
       {bool flushLeft, bool isDouble, bool space}) {
     if (flushLeft == null) flushLeft = false;
     if (space == null) space = false;
-    if (isHardSplit || rule is HardSplitRule) {
+    if (rule.isHardened) {
       // A hard split always wins.
       _rule = rule;
     } else if (_rule == null) {
@@ -271,10 +258,9 @@
 
     if (_rule == null) {
       parts.add("(no split)");
-    } else if (isHardSplit) {
-      parts.add("hard");
     } else {
       parts.add(rule.toString());
+      if (rule.isHardened) parts.add("(hard)");
 
       if (_rule.constrainedRules.isNotEmpty) {
         parts.add("-> ${_rule.constrainedRules.join(' ')}");
diff --git a/lib/src/chunk_builder.dart b/lib/src/chunk_builder.dart
index 83f62a4..61beea3 100644
--- a/lib/src/chunk_builder.dart
+++ b/lib/src/chunk_builder.dart
@@ -61,9 +61,6 @@
   /// 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>[];
 
@@ -361,15 +358,15 @@
     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));
@@ -383,9 +380,9 @@
   /// 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);
   }
@@ -490,7 +487,7 @@
   /// `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
@@ -504,7 +501,9 @@
           break;
         }
 
-        if (chunk.isHardSplit && chunk.rule != ignoredSplit) {
+        if (chunk.rule != null &&
+            chunk.rule.isHardened &&
+            chunk.rule != ignoredSplit) {
           forceSplit = true;
           break;
         }
@@ -524,11 +523,11 @@
     if (forceSplit) forceRules();
 
     // Write the split for the block contents themselves.
-    _chunks.last.applySplit(
-        rule, _nesting.indentation, _blockArgumentNesting.last,
+    var chunk = _chunks.last;
+    chunk.applySplit(rule, _nesting.indentation, _blockArgumentNesting.last,
         flushLeft: firstFlushLeft);
 
-    _afterSplit();
+    if (chunk.rule.isHardened) _handleHardSplit();
   }
 
   /// Finishes writing and returns a [SourceCode] containing the final output
@@ -682,7 +681,7 @@
   void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
     // A hard split overrides any other whitespace.
     _pendingWhitespace = null;
-    _writeSplit(new HardSplitRule(),
+    _writeSplit(new Rule.hard(),
         flushLeft: flushLeft, isDouble: isDouble, nest: nest);
   }
 
@@ -703,21 +702,8 @@
         nest ? _nesting.nesting : new NestingLevel(),
         flushLeft: flushLeft, isDouble: isDouble, space: space);
 
-    return _afterSplit();
-  }
-
-  /// Keep tracks of which chunks are owned by which rules and handles hard
-  /// splits after a chunk has been completed.
-  Chunk _afterSplit() {
-    var chunk = _chunks.last;
-
-    if (chunk.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
@@ -733,15 +719,14 @@
   /// 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.isBlock) 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;
   }
 
@@ -782,18 +767,16 @@
   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);
         }
       }
@@ -803,10 +786,11 @@
       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();
       }
     }
   }
diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart
index f344f44..428aeec 100644
--- a/lib/src/dart_formatter.dart
+++ b/lib/src/dart_formatter.dart
@@ -110,7 +110,7 @@
       // Make sure we consumed all of the source.
       var token = node.endToken.next;
       if (token.type != TokenType.EOF) {
-        var error = new AnalysisError.con2(
+        var error = new AnalysisError(
             stringSource,
             token.offset,
             math.max(token.length, 1),
diff --git a/lib/src/debug.dart b/lib/src/debug.dart
index 77590c0..5d0eb2b 100644
--- a/lib/src/debug.dart
+++ b/lib/src/debug.dart
@@ -9,7 +9,6 @@
 
 import 'chunk.dart';
 import 'line_splitting/rule_set.dart';
-import 'rule/rule.dart';
 
 /// Set this to `true` to turn on diagnostic output while building chunks.
 bool traceChunkBuilder = false;
@@ -70,7 +69,8 @@
 void dumpChunks(int start, List<Chunk> chunks) {
   if (chunks.skip(start).isEmpty) return;
 
-  // Show the spans as vertical bands over their range.
+  // Show the spans as vertical bands over their range (unless there are too
+  // many).
   var spans = new Set();
   addSpans(chunks) {
     for (var chunk in chunks) {
@@ -84,10 +84,8 @@
 
   spans = spans.toList();
 
-  var rules = chunks
-      .map((chunk) => chunk.rule)
-      .where((rule) => rule != null && rule is! HardSplitRule)
-      .toSet();
+  var rules =
+      chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
 
   var rows = [];
 
@@ -102,23 +100,25 @@
       row.add(chunk.text);
     }
 
-    var spanBars = "";
-    for (var span in spans) {
-      if (chunk.spans.contains(span)) {
-        if (index == 0 || !chunks[index - 1].spans.contains(span)) {
-          spanBars += "╖";
+    if (spans.length <= 20) {
+      var spanBars = "";
+      for (var span in spans) {
+        if (chunk.spans.contains(span)) {
+          if (index == 0 || !chunks[index - 1].spans.contains(span)) {
+            spanBars += "╖";
+          } else {
+            spanBars += "║";
+          }
         } else {
-          spanBars += "║";
-        }
-      } else {
-        if (index > 0 && chunks[index - 1].spans.contains(span)) {
-          spanBars += "╜";
-        } else {
-          spanBars += " ";
+          if (index > 0 && chunks[index - 1].spans.contains(span)) {
+            spanBars += "╜";
+          } else {
+            spanBars += " ";
+          }
         }
       }
+      row.add(spanBars);
     }
-    row.add(spanBars);
 
     writeIf(predicate, String callback()) {
       if (predicate) {
@@ -132,13 +132,12 @@
       row.add("");
       row.add("(no rule)");
       row.add("");
-    } else if (chunk.isHardSplit) {
-      row.add("");
-      row.add("(hard)");
-      row.add("");
-    } else if (chunk.rule != null) {
+    } else {
       writeIf(chunk.rule.cost != 0, () => "\$${chunk.rule.cost}");
-      row.add(chunk.rule.toString());
+
+      var ruleString = chunk.rule.toString();
+      if (chunk.rule.isHardened) ruleString += "!";
+      row.add(ruleString);
 
       var constrainedRules =
           chunk.rule.constrainedRules.toSet().intersection(rules);
@@ -154,6 +153,8 @@
 
     writeIf(chunk.flushLeft != null && chunk.flushLeft, () => "flush");
 
+    writeIf(chunk.canDivide, () => "divide");
+
     rows.add(row);
 
     if (chunk.isBlock) {
@@ -191,6 +192,33 @@
   print(buffer.toString());
 }
 
+/// Shows all of the constraints between the rules used by [chunks].
+void dumpConstraints(List<Chunk> chunks) {
+  var rules =
+      chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
+
+  for (var rule in rules) {
+    var constrainedValues = [];
+    for (var value = 0; value < rule.numValues; value++) {
+      var constraints = [];
+      for (var other in rules) {
+        if (rule == other) continue;
+
+        var constraint = rule.constrain(value, other);
+        if (constraint != null) {
+          constraints.add("$other->$constraint");
+        }
+      }
+
+      if (constraints.isNotEmpty) {
+        constrainedValues.add("$value:(${constraints.join(' ')})");
+      }
+    }
+
+    log("$rule ${constrainedValues.join(' ')}");
+  }
+}
+
 /// Convert the line to a [String] representation.
 ///
 /// It will determine how best to split it into multiple lines of output and
diff --git a/lib/src/line_splitting/line_splitter.dart b/lib/src/line_splitting/line_splitter.dart
index 78aa54d..420c509 100644
--- a/lib/src/line_splitting/line_splitter.dart
+++ b/lib/src/line_splitting/line_splitter.dart
@@ -130,10 +130,10 @@
       int firstLineIndent,
       {bool flushLeft: false})
       : chunks = chunks,
-        // Collect the set of soft rules that we need to select values for.
+        // Collect the set of rules that we need to select values for.
         rules = chunks
             .map((chunk) => chunk.rule)
-            .where((rule) => rule != null && rule is! HardSplitRule)
+            .where((rule) => rule != null)
             .toSet()
             .toList(growable: false),
         blockIndentation = blockIndentation,
diff --git a/lib/src/line_splitting/rule_set.dart b/lib/src/line_splitting/rule_set.dart
index e0cf3e1..714875d 100644
--- a/lib/src/line_splitting/rule_set.dart
+++ b/lib/src/line_splitting/rule_set.dart
@@ -23,14 +23,22 @@
   RuleSet._(this._values);
 
   /// Returns `true` of [rule] is bound in this set.
-  bool contains(Rule rule) => _values[rule.index] != null;
+  bool contains(Rule rule) {
+    // Treat hardened rules as implicitly bound.
+    if (rule.isHardened) return true;
 
-  /// Gets the bound value for [rule] or `0` if it is not bound.
+    return _values[rule.index] != null;
+  }
+
+  /// Gets the bound value for [rule] or [Rule.unsplit] if it is not bound.
   int getValue(Rule rule) {
+    // Hardened rules are implicitly bound.
+    if (rule.isHardened) return rule.fullySplitValue;
+
     var value = _values[rule.index];
     if (value != null) return value;
 
-    return 0;
+    return Rule.unsplit;
   }
 
   /// Invokes [callback] for each rule in [rules] with the rule's value, which
@@ -56,17 +64,26 @@
   /// If an unbound rule gets constrained to `-1` (meaning it must split, but
   /// can split any way it wants), invokes [onSplitRule] with it.
   bool tryBind(List<Rule> rules, Rule rule, int value, onSplitRule(Rule rule)) {
+    assert(!rule.isHardened);
+
     _values[rule.index] = value;
 
     // Test this rule against the other rules being bound.
     for (var other in rule.constrainedRules) {
-      var otherValue = _values[other.index];
+      var otherValue;
+      // Hardened rules are implicitly bound.
+      if (other.isHardened) {
+        otherValue = other.fullySplitValue;
+      } else {
+        otherValue = _values[other.index];
+      }
+
       var constraint = rule.constrain(value, other);
 
       if (otherValue == null) {
         // The other rule is unbound, so see if we can constrain it eagerly to
         // a value now.
-        if (constraint == -1) {
+        if (constraint == Rule.mustSplit) {
           // If we know the rule has to split and there's only one way it can,
           // just bind that.
           if (other.numValues == 2) {
@@ -82,16 +99,16 @@
       } else {
         // It's already bound, so see if the new rule's constraint disallows
         // that value.
-        if (constraint == -1) {
-          if (otherValue == 0) return false;
+        if (constraint == Rule.mustSplit) {
+          if (otherValue == Rule.unsplit) return false;
         } else if (constraint != null) {
           if (otherValue != constraint) return false;
         }
 
         // See if the other rule's constraint allows us to use this value.
         constraint = other.constrain(otherValue, rule);
-        if (constraint == -1) {
-          if (value == 0) return false;
+        if (constraint == Rule.mustSplit) {
+          if (value == Rule.unsplit) return false;
         } else if (constraint != null) {
           if (value != constraint) return false;
         }
diff --git a/lib/src/line_splitting/solve_state.dart b/lib/src/line_splitting/solve_state.dart
index 1a02af3..e1e9168 100644
--- a/lib/src/line_splitting/solve_state.dart
+++ b/lib/src/line_splitting/solve_state.dart
@@ -97,7 +97,7 @@
   ///
   /// It's important to track this, because we can't allow to states to overlap
   /// if one permits more values for some unbound rule than the other does.
-  Map<Rule, List<int>> _unboundConstraints;
+  Map<Rule, Set<int>> _unboundConstraints;
 
   /// The bound rules that appear inside lines also containing unbound rules.
   ///
@@ -111,13 +111,9 @@
     _calculateCost();
   }
 
-  /// Gets the value to use for [rule], either the bound value or `0` if it
-  /// isn't bound.
-  int getValue(Rule rule) {
-    if (rule is HardSplitRule) return 0;
-
-    return _ruleValues.getValue(rule);
-  }
+  /// Gets the value to use for [rule], either the bound value or
+  /// [Rule.unsplit] if it isn't bound.
+  int getValue(Rule rule) => _ruleValues.getValue(rule);
 
   /// Returns `true` if this state is a better solution to use as the final
   /// result than [other].
@@ -417,10 +413,9 @@
       }
     }
 
-    // Add the costs for the rules that split.
+    // Add the costs for the rules that have any splits.
     _ruleValues.forEach(_splitter.rules, (rule, value) {
-      // A rule may be bound to zero if another rule constrains it to not split.
-      if (value != 0) cost += rule.cost;
+      if (value != Rule.unsplit) cost += rule.cost;
     });
 
     // Add the costs for the spans containing splits.
@@ -438,7 +433,6 @@
   /// live rules were added.
   bool _addLiveRules(Rule rule) {
     if (rule == null) return false;
-    if (rule is HardSplitRule) return false;
 
     var added = false;
     for (var constrained in rule.allConstrainedRules) {
@@ -473,7 +467,7 @@
       }
 
       var rule = _splitter.chunks[i].rule;
-      if (rule != null && rule is! HardSplitRule) {
+      if (rule != null) {
         if (_ruleValues.contains(rule)) {
           boundInLine.add(rule);
         } else {
@@ -553,7 +547,9 @@
           // same bound value, one of which has a satisfied constraint, are
           // still allowed to overlap.
           if (constraint == boundValue) continue;
-          if (constraint == -1 && boundValue != 0) continue;
+          if (constraint == Rule.mustSplit && boundValue != Rule.unsplit) {
+            continue;
+          }
 
           if (disallowedValues == null) {
             disallowedValues = new Set();
diff --git a/lib/src/line_writer.dart b/lib/src/line_writer.dart
index 69f8c27..9fb5d39 100644
--- a/lib/src/line_writer.dart
+++ b/lib/src/line_writer.dart
@@ -144,7 +144,7 @@
 
     if (debug.traceLineWriter) {
       debug.log(debug.green("\nWriting:"));
-      debug.dumpChunks(start, chunks);
+      debug.dumpChunks(0, chunks);
       debug.log();
     }
 
diff --git a/lib/src/rule/argument.dart b/lib/src/rule/argument.dart
index d82d8f3..ed7758a 100644
--- a/lib/src/rule/argument.dart
+++ b/lib/src/rule/argument.dart
@@ -9,6 +9,24 @@
 
 /// Base class for a rule that handles argument or parameter lists.
 abstract class ArgumentRule extends Rule {
+  /// The chunks prior to each positional argument.
+  final List<Chunk> _arguments = [];
+
+  /// The rule used to split collections in the argument list, if any.
+  Rule _collectionRule;
+
+  /// The number of leading collection arguments.
+  ///
+  /// This and [_trailingCollections] cannot both be positive. If every
+  /// argument is a collection, this will be [_arguments.length] and
+  /// [_trailingCollections] will be 0.
+  final int _leadingCollections;
+
+  /// The number of trailing collections.
+  ///
+  /// This and [_leadingCollections] cannot both be positive.
+  final int _trailingCollections;
+
   /// If true, then inner rules that are written will force this rule to split.
   ///
   /// Temporarily disabled while writing collectio arguments so that they can be
@@ -18,6 +36,27 @@
   /// Don't split when an inner collection rule splits.
   bool get splitsOnInnerRules => _trackInnerRules;
 
+  ArgumentRule(this._collectionRule, this._leadingCollections,
+      this._trailingCollections);
+
+  void addConstrainedRules(Set<Rule> rules) {
+    super.addConstrainedRules(rules);
+    if (_collectionRule != null) rules.add(_collectionRule);
+  }
+
+  void forgetUnusedRules() {
+    super.forgetUnusedRules();
+    if (_collectionRule != null && _collectionRule.index == null) {
+      _collectionRule = null;
+    }
+  }
+
+  /// Remembers [chunk] as containing the split that occurs right before an
+  /// argument in the list.
+  void beforeArgument(Chunk chunk) {
+    _arguments.add(chunk);
+  }
+
   /// Called before a collection argument is written.
   ///
   /// Disables tracking inner rules while a collection argument is written.
@@ -37,12 +76,6 @@
 
 /// Base class for a rule for handling positional argument lists.
 abstract class PositionalRule extends ArgumentRule {
-  /// The chunks prior to each positional argument.
-  final List<Chunk> _arguments = [];
-
-  /// The rule used to split collections in the argument list, if any.
-  Rule _collectionRule;
-
   /// If there are named arguments following these positional ones, this will
   /// be their rule.
   Rule _namedArgsRule;
@@ -51,10 +84,12 @@
   ///
   /// If [_collectionRule] is given, it is the rule used to split the collection
   /// arguments in the list.
-  PositionalRule(this._collectionRule);
+  PositionalRule(
+      Rule collectionRule, int leadingCollections, int trailingCollections)
+      : super(collectionRule, leadingCollections, trailingCollections);
 
   void addConstrainedRules(Set<Rule> rules) {
-    if (_collectionRule != null) rules.add(_collectionRule);
+    super.addConstrainedRules(rules);
     if (_namedArgsRule != null) rules.add(_namedArgsRule);
   }
 
@@ -63,21 +98,15 @@
     if (_namedArgsRule != null && _namedArgsRule.index == null) {
       _namedArgsRule = null;
     }
-
-    if (_collectionRule != null && _collectionRule.index == null) {
-      _collectionRule = null;
-    }
   }
 
-  /// Remembers [chunk] as containing the split that occurs right before an
-  /// argument in the list.
-  void beforeArgument(Chunk chunk) {
-    _arguments.add(chunk);
-  }
-
-  /// Remembers that [rule] is the [NamedArgsRule] immediately following this
+  /// Remembers that [rule] is the [Rule] immediately following this positional
   /// positional argument list.
-  void setNamedArgsRule(NamedRule rule) {
+  ///
+  /// This is normally a [NamedRule] but [PositionalRule] is also used for the
+  /// property accesses at the beginning of a call chain, in which case this
+  /// is just a [SimpleRule].
+  void setNamedArgsRule(Rule rule) {
     _namedArgsRule = rule;
   }
 
@@ -120,12 +149,10 @@
   /// collections in the list. If [splitsOnInnerRules] is `true`, then we will
   /// split before the argument if the argument itself contains a split.
   SinglePositionalRule(Rule collectionRule, {bool splitsOnInnerRules})
-      : super(collectionRule),
+      : super(collectionRule, 0, 0),
         splitsOnInnerRules =
             splitsOnInnerRules != null ? splitsOnInnerRules : false;
 
-  bool isSplit(int value, Chunk chunk) => value == 1;
-
   int constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
@@ -133,10 +160,10 @@
     if (other != _collectionRule) return null;
 
     // If we aren't splitting any args, we can split the collection.
-    if (value == 0) return null;
+    if (value == Rule.unsplit) return null;
 
     // We are splitting before a collection, so don't let it split internally.
-    return 0;
+    return Rule.unsplit;
   }
 
   String toString() => "1Pos${super.toString()}";
@@ -160,18 +187,6 @@
 /// splits before all of the non-collection arguments, but does not split
 /// before the collections, so that they can split internally.
 class MultiplePositionalRule extends PositionalRule {
-  /// The number of leading collection arguments.
-  ///
-  /// This and [_trailingCollections] cannot both be positive. If every
-  /// argument is a collection, this will be [_arguments.length] and
-  /// [_trailingCollections] will be 0.
-  final int _leadingCollections;
-
-  /// The number of trailing collections.
-  ///
-  /// This and [_leadingCollections] cannot both be positive.
-  final int _trailingCollections;
-
   int get numValues {
     // Can split before any one argument, none, or all.
     var result = 2 + _arguments.length;
@@ -189,15 +204,12 @@
   }
 
   MultiplePositionalRule(
-      Rule collectionRule, this._leadingCollections, this._trailingCollections)
-      : super(collectionRule);
+      Rule collectionRule, int leadingCollections, int trailingCollections)
+      : super(collectionRule, leadingCollections, trailingCollections);
 
   String toString() => "*Pos${super.toString()}";
 
-  bool isSplit(int value, Chunk chunk) {
-    // Don't split at all.
-    if (value == 0) return false;
-
+  bool isSplitAtValue(int value, Chunk chunk) {
     // Split only before the first argument. Keep the entire argument list
     // together on the next line.
     if (value == 1) return chunk == _arguments.first;
@@ -240,14 +252,14 @@
     if (_leadingCollections == 0 && _trailingCollections == 0) return null;
 
     // If we aren't splitting any args, we can split the collection.
-    if (value == 0) return null;
+    if (value == Rule.unsplit) return null;
 
     // Split only before the first argument.
     if (value == 1) {
       if (_leadingCollections > 0) {
         // We are splitting before a collection, so don't let it split
         // internally.
-        return 0;
+        return Rule.unsplit;
       } else {
         // The split is outside of the collections so they can split or not.
         return null;
@@ -258,8 +270,10 @@
     // arguments, don't allow them to split.
     if (value <= _arguments.length) {
       var argument = _arguments.length - value + 1;
-      if (argument < _leadingCollections) return 0;
-      if (argument >= _arguments.length - _trailingCollections) return 0;
+      if (argument < _leadingCollections ||
+          argument >= _arguments.length - _trailingCollections) {
+        return Rule.unsplit;
+      }
 
       return null;
     }
@@ -276,37 +290,45 @@
 
 /// Splitting rule for a list of named arguments or parameters. Its values mean:
 ///
-/// * 0: Do not split at all.
-/// * 1: Split only before first argument.
-/// * 2: Split before all arguments, including the first.
+/// * Do not split at all.
+/// * Split only before first argument.
+/// * Split before all arguments.
 class NamedRule extends ArgumentRule {
-  /// The chunk prior to the first named argument.
-  Chunk _first;
-
-  /// Whether any of the named arguments are collection literals.
-  final bool _containsCollections;
-
   int get numValues => 3;
 
-  NamedRule({bool containsCollections})
-      : _containsCollections = containsCollections ?? false;
+  NamedRule(
+      Rule collectionRule, int leadingCollections, int trailingCollections)
+      : super(collectionRule, leadingCollections, trailingCollections);
 
-  void beforeArguments(Chunk chunk) {
-    assert(_first == null);
-    _first = chunk;
+  bool isSplitAtValue(int value, Chunk chunk) {
+    // Move all arguments to the second line as a unit.
+    if (value == 1) return chunk == _arguments.first;
+
+    // Otherwise, split before all arguments.
+    return true;
   }
 
-  bool isSplit(int value, Chunk chunk) {
-    switch (value) {
-      case 0:
-        return false;
-      case 1:
-        return chunk == _first;
-      case 2:
-        return true;
-    }
+  int constrain(int value, Rule other) {
+    var constrained = super.constrain(value, other);
+    if (constrained != null) return constrained;
 
-    throw "unreachable";
+    // Decide how to constrain the collection rule.
+    if (other != _collectionRule) return null;
+
+    // If all of the collections are in the named arguments, [_collectionRule]
+    // will not be null, but we don't have to handle it.
+    if (_leadingCollections == 0 && _trailingCollections == 0) return null;
+
+    // If we aren't splitting any args, we can split the collection.
+    if (value == Rule.unsplit) return null;
+
+    // Split only before the first argument. Don't allow the collections to
+    // split.
+    if (value == 1) return Rule.unsplit;
+
+    // Split before all of the arguments, even the collections. We'll allow
+    // them to split but indent their bodies if they do.
+    return null;
   }
 
   String toString() => "Named${super.toString()}";
diff --git a/lib/src/rule/combinator.dart b/lib/src/rule/combinator.dart
index 62ddfdb..1d73031 100644
--- a/lib/src/rule/combinator.dart
+++ b/lib/src/rule/combinator.dart
@@ -80,12 +80,8 @@
     _names.last.add(chunk);
   }
 
-  bool isSplit(int value, Chunk chunk) {
+  bool isSplitAtValue(int value, Chunk chunk) {
     switch (value) {
-      case 0:
-        // Don't split at all.
-        return false;
-
       case 1:
         // Just split at the combinators.
         return _combinators.contains(chunk);
@@ -106,11 +102,9 @@
         // Split everything.
         return true;
 
-      case 4:
+      default:
         return true;
     }
-
-    throw "unreachable";
   }
 
   /// Returns `true` if [chunk] is for a combinator or a name in the
diff --git a/lib/src/rule/metadata.dart b/lib/src/rule/metadata.dart
new file mode 100644
index 0000000..9abbe8f
--- /dev/null
+++ b/lib/src/rule/metadata.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2015, 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.rule.metadata;
+
+import 'argument.dart';
+import 'rule.dart';
+
+/// Rule for handling splits between parameter metadata annotations and the
+/// following parameter.
+///
+/// Metadata annotations for parameters (and type parameters) get some special
+/// handling. We use a single rule for all annotations in the parameter list.
+/// If any of the annotations split, they all do.
+///
+/// Also, if the annotations split, we force the entire parameter list to fully
+/// split, both named and positional.
+class MetadataRule extends Rule {
+  Rule _positionalRule;
+  Rule _namedRule;
+
+  /// Remembers that [rule] is the [PositionalRule] used by the argument list
+  /// containing the parameter metadata using this rule.
+  void bindPositionalRule(PositionalRule rule) {
+    _positionalRule = rule;
+  }
+
+  /// Remembers that [rule] is the [NamedRule] used by the argument list
+  /// containing the parameter metadata using this rule.
+  void bindNamedRule(NamedRule rule) {
+    _namedRule = rule;
+  }
+
+  /// Constrains the surrounding argument list rules to fully split if the
+  /// metadata does.
+  int constrain(int value, Rule other) {
+    var constrained = super.constrain(value, other);
+    if (constrained != null) return constrained;
+
+    // If the metadata doesn't split, we don't care what the arguments do.
+    if (value == Rule.unsplit) return null;
+
+    // Otherwise, they have to split.
+    if (other == _positionalRule) return _positionalRule.fullySplitValue;
+    if (other == _namedRule) return _namedRule.fullySplitValue;
+
+    return null;
+  }
+
+  void addConstrainedRules(Set<Rule> rules) {
+    if (_positionalRule != null) rules.add(_positionalRule);
+    if (_namedRule != null) rules.add(_namedRule);
+  }
+
+  void forgetUnusedRules() {
+    super.forgetUnusedRules();
+    if (_positionalRule != null && _positionalRule.index == null) {
+      _positionalRule = null;
+    }
+
+    if (_namedRule != null && _namedRule.index == null) {
+      _namedRule = null;
+    }
+  }
+}
diff --git a/lib/src/rule/rule.dart b/lib/src/rule/rule.dart
index 0ee44b1..e01d3b0 100644
--- a/lib/src/rule/rule.dart
+++ b/lib/src/rule/rule.dart
@@ -9,26 +9,43 @@
 
 /// A constraint that determines the different ways a related set of chunks may
 /// be split.
-abstract class Rule extends FastHash {
+class Rule extends FastHash {
+  /// Rule value that splits no chunks.
+  ///
+  /// Every rule is required to treat this value as fully unsplit.
+  static const unsplit = 0;
+
+  /// Rule constraint value that means "any value as long as something splits".
+  ///
+  /// It disallows [unsplit] but allows any other value.
+  static const mustSplit = -1;
+
   /// The number of different states this rule can be in.
   ///
   /// Each state determines which set of chunks using this rule are split and
   /// which aren't. Values range from zero to one minus this. Value zero
   /// always means "no chunks are split" and increasing values by convention
   /// mean increasingly undesirable splits.
-  int get numValues;
+  ///
+  /// By default, a rule has two values: fully unsplit and fully split.
+  int get numValues => 2;
 
   /// The rule value that forces this rule into its maximally split state.
   ///
   /// By convention, this is the highest of the range of allowed values.
   int get fullySplitValue => numValues - 1;
 
-  int get cost => Cost.normal;
+  final int cost;
 
   /// During line splitting [LineSplitter] sets this to the index of this
   /// rule in its list of rules.
   int index;
 
+  /// If `true`, the rule has been "hardened" meaning it's been placed into a
+  /// permanent "must fully split" state.
+  bool get isHardened => _isHardened;
+  bool _isHardened = false;
+
   /// The other [Rule]s that "surround" this one (and care about that fact).
   ///
   /// In many cases, if a split occurs inside an expression, surrounding rules
@@ -59,7 +76,38 @@
   /// rules.
   bool get splitsOnInnerRules => true;
 
-  bool isSplit(int value, Chunk chunk);
+  Rule([int cost]) : cost = cost ?? Cost.normal;
+
+  /// Creates a new rule that is already fully split.
+  Rule.hard() : cost = 0 {
+    // Set the cost to zero since it will always be applied, so there's no
+    // point in penalizing it.
+    //
+    // Also, this avoids doubled counting in literal blocks where there is both
+    // a split in the outer chunk containing the block and the inner hard split
+    // between the elements or statements.
+    harden();
+  }
+
+  /// Fixes this rule into a "fully split" state.
+  void harden() {
+    _isHardened = true;
+  }
+
+  /// Returns `true` if [chunk] should split when this rule has [value].
+  bool isSplit(int value, Chunk chunk) {
+    if (_isHardened) return true;
+
+    if (value == Rule.unsplit) return false;
+
+    // Let the subclass decide.
+    return isSplitAtValue(value, chunk);
+  }
+
+  /// Subclasses can override this to determine which values split which chunks.
+  ///
+  /// By default, this assumes every chunk splits.
+  bool isSplitAtValue(value, chunk) => true;
 
   /// Given that this rule has [value], determine if [other]'s value should be
   /// constrained.
@@ -69,9 +117,8 @@
   /// value. Returns -1 to allow [other] to take any non-zero value. Returns
   /// null to not constrain other.
   int constrain(int value, Rule other) {
-    // By default, any implied rule will be fully split if this one is fully
-    // split.
-    if (value == 0) return null;
+    // By default, any containing rule will be fully split if this one is split.
+    if (value == Rule.unsplit) return null;
     if (_outerRules.contains(other)) return other.fullySplitValue;
 
     return null;
@@ -137,36 +184,3 @@
 
   String toString() => "$id";
 }
-
-/// A rule that always splits a chunk.
-class HardSplitRule extends Rule {
-  int get numValues => 1;
-
-  /// It's always going to be applied, so there's no point in penalizing it.
-  ///
-  /// Also, this avoids doubled counting in literal blocks where there is both
-  /// a split in the outer chunk containing the block and the inner hard split
-  /// between the elements or statements.
-  int get cost => 0;
-
-  /// It's always split anyway.
-  bool get splitsOnInnerRules => false;
-
-  bool isSplit(int value, Chunk chunk) => true;
-
-  String toString() => "Hard";
-}
-
-/// A basic rule that has two states: unsplit or split.
-class SimpleRule extends Rule {
-  /// Two values: 0 is unsplit, 1 is split.
-  int get numValues => 2;
-
-  final int cost;
-
-  SimpleRule([int cost]) : cost = cost != null ? cost : Cost.normal;
-
-  bool isSplit(int value, Chunk chunk) => value == 1;
-
-  String toString() => "Simple${super.toString()}";
-}
diff --git a/lib/src/rule/type_argument.dart b/lib/src/rule/type_argument.dart
index 3c8e797..8fcc117 100644
--- a/lib/src/rule/type_argument.dart
+++ b/lib/src/rule/type_argument.dart
@@ -43,7 +43,7 @@
 
   bool isSplit(int value, Chunk chunk) {
     // Don't split at all.
-    if (value == 0) return false;
+    if (value == Rule.unsplit) return false;
 
     // Split before every argument.
     if (value == numValues - 1) return true;
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 3ade12d..1cdd0be 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -15,6 +15,7 @@
 import 'dart_formatter.dart';
 import 'rule/argument.dart';
 import 'rule/combinator.dart';
+import 'rule/metadata.dart';
 import 'rule/rule.dart';
 import 'rule/type_argument.dart';
 import 'source_code.dart';
@@ -59,6 +60,14 @@
   /// split.
   final List<bool> _collectionSplits = [];
 
+  /// The stack of current rules for handling parameter metadata.
+  ///
+  /// Each time a parameter (or type parameter) list is begun, a single rule
+  /// for all of the metadata annotations on parameters in that list is pushed
+  /// onto this stack. We reuse this rule for all annotations so that they split
+  /// in unison.
+  final List<MetadataRule> _metadataRules = [];
+
   /// The mapping for collection literals that are managed by the argument
   /// list that contains them.
   ///
@@ -298,7 +307,7 @@
       visitNodes(node.cascadeSections, between: zeroSplit);
       builder.endRule();
     } else {
-      builder.startRule(new HardSplitRule());
+      builder.startRule(new Rule.hard());
       zeroSplit();
       visitNodes(node.cascadeSections, between: zeroSplit);
       builder.endRule();
@@ -789,6 +798,8 @@
     builder.nestExpression();
     token(node.leftParenthesis);
 
+    _metadataRules.add(new MetadataRule());
+
     var rule;
     if (requiredParams.isNotEmpty) {
       if (requiredParams.length > 1) {
@@ -797,6 +808,8 @@
         rule = new SinglePositionalRule(null);
       }
 
+      _metadataRules.last.bindPositionalRule(rule);
+
       builder.startRule(rule);
       if (_isInLambda(node)) {
         // Don't allow splitting before the first argument (i.e. right after
@@ -824,16 +837,17 @@
     }
 
     if (optionalParams.isNotEmpty) {
-      var namedRule = new NamedRule();
+      var namedRule = new NamedRule(null, 0, 0);
       if (rule != null) rule.setNamedArgsRule(namedRule);
 
+      _metadataRules.last.bindNamedRule(namedRule);
+
       builder.startRule(namedRule);
 
       // Make sure multi-line default values are indented.
       builder.startBlockArgumentNesting();
 
-      namedRule
-          .beforeArguments(builder.split(space: requiredParams.isNotEmpty));
+      namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty));
 
       // "[" or "{" for optional parameters.
       token(node.leftDelimiter);
@@ -843,7 +857,7 @@
 
         // Write the trailing comma.
         if (param != node.parameters.last) token(param.endToken.next);
-        if (param != optionalParams.last) split();
+        if (param != optionalParams.last) namedRule.beforeArgument(split());
       }
 
       builder.endBlockArgumentNesting();
@@ -853,6 +867,8 @@
       token(node.rightDelimiter);
     }
 
+    _metadataRules.removeLast();
+
     token(node.rightParenthesis);
     builder.unnest();
   }
@@ -1187,7 +1203,16 @@
     builder.nestExpression();
     builder.startSpan();
     visit(node.name);
-    visit(node.expression, before: soloSplit);
+
+    // Don't allow a split between a name and a collection. Instead, we want
+    // the collection itself to split, or to split before the argument.
+    if (node.expression is ListLiteral || node.expression is MapLiteral) {
+      space();
+    } else {
+      soloSplit();
+    }
+
+    visit(node.expression);
     builder.endSpan();
     builder.unnest();
   }
@@ -1465,7 +1490,11 @@
   }
 
   visitTypeParameterList(TypeParameterList node) {
+    _metadataRules.add(new MetadataRule());
+
     _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
+
+    _metadataRules.removeLast();
   }
 
   visitVariableDeclaration(VariableDeclaration node) {
@@ -1570,11 +1599,29 @@
   /// These are always on the same line as the parameter.
   void visitParameterMetadata(
       NodeList<Annotation> metadata, void visitParameter()) {
+    if (metadata == null || metadata.isEmpty) {
+      visitParameter();
+      return;
+    }
+
     // Split before all of the annotations or none.
-    builder.startRule();
-    visitNodes(metadata, between: split, after: split);
+    builder.startLazyRule(_metadataRules.last);
+
+    visitNodes(metadata, between: split, after: () {
+      // Don't nest until right before the last metadata. Ensures we only
+      // indent the parameter and not any of the metadata:
+      //
+      //     function(
+      //         @LongAnnotation
+      //         @LongAnnotation
+      //             indentedParameter) {}
+      builder.nestExpression(now: true);
+      split();
+    });
     visitParameter();
 
+    builder.unnest();
+
     // Wrap the rule around the parameter too. If it splits, we want to force
     // the annotations to split as well.
     builder.endRule();
@@ -1688,7 +1735,7 @@
       builder.nestExpression();
 
       // This rule is ended by visitExpressionFunctionBody().
-      builder.startLazyRule(new SimpleRule(Cost.arrow));
+      builder.startLazyRule(new Rule(Cost.arrow));
     }
 
     if (parameters != null) {
@@ -1789,7 +1836,7 @@
     // Always use a hard rule to split the elements. The parent chunk of
     // the collection will handle the unsplit case, so this only comes
     // into play when the collection is split.
-    var rule = new HardSplitRule();
+    var rule = new Rule.hard();
     builder.startRule(rule);
 
     // If a collection contains a line comment, we assume it's a big complex
@@ -2079,7 +2126,7 @@
 
   /// Writes a single space split with its own rule.
   void soloSplit([int cost]) {
-    builder.startRule(new SimpleRule(cost));
+    builder.startRule(new Rule(cost));
     split();
     builder.endRule();
   }
diff --git a/pubspec.lock b/pubspec.lock
index 25be673..3dc00b8 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -4,7 +4,7 @@
   analyzer:
     description: analyzer
     source: hosted
-    version: "0.26.2"
+    version: "0.26.3"
   ansicolor:
     description: ansicolor
     source: hosted
@@ -16,7 +16,7 @@
   async:
     description: async
     source: hosted
-    version: "1.2.0"
+    version: "1.4.0"
   barback:
     description: barback
     source: hosted
@@ -36,7 +36,11 @@
   collection:
     description: collection
     source: hosted
-    version: "1.1.3"
+    version: "1.2.0"
+  convert:
+    description: convert
+    source: hosted
+    version: "1.0.1"
   crypto:
     description: crypto
     source: hosted
@@ -68,7 +72,7 @@
   http_parser:
     description: http_parser
     source: hosted
-    version: "0.0.2+8"
+    version: "1.1.0"
   logging:
     description: logging
     source: hosted
@@ -92,7 +96,7 @@
   path:
     description: path
     source: hosted
-    version: "1.3.6"
+    version: "1.3.8"
   plugin:
     description: plugin
     source: hosted
@@ -100,7 +104,7 @@
   pool:
     description: pool
     source: hosted
-    version: "1.1.0"
+    version: "1.2.1"
   pub_semver:
     description: pub_semver
     source: hosted
@@ -108,11 +112,11 @@
   scheduled_test:
     description: scheduled_test
     source: hosted
-    version: "0.12.2"
+    version: "0.12.4+1"
   shelf:
     description: shelf
     source: hosted
-    version: "0.6.3"
+    version: "0.6.4+2"
   shelf_static:
     description: shelf_static
     source: hosted
@@ -136,7 +140,7 @@
   stack_trace:
     description: stack_trace
     source: hosted
-    version: "1.4.2"
+    version: "1.5.0"
   string_scanner:
     description: string_scanner
     source: hosted
@@ -148,7 +152,11 @@
   test:
     description: test
     source: hosted
-    version: "0.12.3+9"
+    version: "0.12.6"
+  typed_data:
+    description: typed_data
+    source: hosted
+    version: "1.1.1"
   unscripted:
     description: unscripted
     source: hosted
diff --git a/pubspec.yaml b/pubspec.yaml
index 23468ed..5e44437 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -11,7 +11,7 @@
   path: '>=1.0.0 <2.0.0'
   source_span: '>=1.1.1 <2.0.0'
 dev_dependencies:
-  async: '>=1.0.0 <=1.2.0' # Temporary to avoid 1.3.0.
+  async: '>=1.0.0 <=2.0.0'
   browser: '>=0.10.0 <0.11.0'
   grinder: '^0.7.2'
   scheduled_test: '>=0.12.0 <0.13.0'
diff --git a/test/regression/0100/0108.unit b/test/regression/0100/0108.unit
index 1dfd334..59bb316 100644
--- a/test/regression/0100/0108.unit
+++ b/test/regression/0100/0108.unit
@@ -199,8 +199,8 @@
       _bindAssignablePropsOn.forEach((String eventName) =>
           node.addEventListener(
               eventName,
-              (_) => zone.run(() => bindAssignableProps.forEach(
-                  (propAndExp) => propAndExp[1]
+              (_) => zone.run(() => bindAssignableProps.forEach((propAndExp) =>
+                  propAndExp[1]
                       .assign(scope.context, jsNode[propAndExp[0]])))));
     }
   }
@@ -212,12 +212,13 @@
 DDC$RT.type((Iterable<Future<dynamic>> _) {}), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
 DDC$RT.type((Future<List<StyleElement>> _) {}), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:7"));
 <<<
-async.Future<List<dom.StyleElement>> call(String tag, List<String> cssUrls,
-        {Type type}) =>
+async.Future<List<dom.StyleElement>> call(String tag, List<String> cssUrls, {Type type}) =>
     (DDC$RT.cast(
         async.Future.wait((DDC$RT.cast(
-            cssUrls.map((url) =>
-                _styleElement(tag, (DDC$RT.cast(url, String, key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")), type)),
+            cssUrls.map((url) => _styleElement(
+                tag,
+                (DDC$RT.cast(url, String, key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")),
+                type)),
             DDC$RT.type((Iterable<Future<dynamic>> _) {}),
             key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
         DDC$RT.type((Future<List<StyleElement>> _) {}),
diff --git a/test/regression/0100/0162.stmt b/test/regression/0100/0162.stmt
index daa92ac..5936f6f 100644
--- a/test/regression/0100/0162.stmt
+++ b/test/regression/0100/0162.stmt
@@ -28,22 +28,19 @@
 }
 <<<
 void main() {
-  useGeneratedCode(new StaticConfiguration(
-      checkedMode: false,
-      parents: {
-        smoke_0.A: smoke_1.PolymerElement,
-        smoke_0.B: smoke_0.A,
-        smoke_0.C: smoke_0.B,
-        smoke_0.D: smoke_0.B,
-        smoke_0.E: smoke_0.C,
-      },
-      declarations: {
-        smoke_0.A: {},
-        smoke_0.B: {},
-        smoke_0.C: {},
-        smoke_0.D: {},
-        smoke_0.E: {},
-      }));
+  useGeneratedCode(new StaticConfiguration(checkedMode: false, parents: {
+    smoke_0.A: smoke_1.PolymerElement,
+    smoke_0.B: smoke_0.A,
+    smoke_0.C: smoke_0.B,
+    smoke_0.D: smoke_0.B,
+    smoke_0.E: smoke_0.C,
+  }, declarations: {
+    smoke_0.A: {},
+    smoke_0.B: {},
+    smoke_0.C: {},
+    smoke_0.D: {},
+    smoke_0.E: {},
+  }));
   new LogInjector().injectLogsFromUrl('sort_registration_test.html._buildLogs');
   configureForDeployment([
     () => Polymer.register('x-a', i0.A),
diff --git a/test/regression/0200/0221.unit b/test/regression/0200/0221.unit
index 74cf12c..ab5fb23 100644
--- a/test/regression/0200/0221.unit
+++ b/test/regression/0200/0221.unit
@@ -35,11 +35,13 @@
 void _updateChart() {
   if (_model.settings != null) {
     _chart.update((ChartSettings.builder()
-      ..ids.addAll(_model.ids)
-      ..statusFilter = StatusFilter.ALL
-      ..dateRange = chartDates.toChartDateRange(_model.settings.dateRange.value)
-      ..segmentationDimension = _model.segmentation
-      ..context = ChartContext.empty).build());
+          ..ids.addAll(_model.ids)
+          ..statusFilter = StatusFilter.ALL
+          ..dateRange =
+              chartDates.toChartDateRange(_model.settings.dateRange.value)
+          ..segmentationDimension = _model.segmentation
+          ..context = ChartContext.empty)
+        .build());
   }
 }
 >>> (indent 2)
diff --git a/test/regression/0200/0243.stmt b/test/regression/0200/0243.stmt
index d4c6fa5..b75dc65 100644
--- a/test/regression/0200/0243.stmt
+++ b/test/regression/0200/0243.stmt
@@ -7,12 +7,10 @@
               style: _content_style,
               children: appChildren)]);
 <<<
-    return new Container(
-        key: 'ChatApp',
-        children: [
-          new Container(
-              key: 'Content', style: _content_style, children: appChildren)
-        ]);
+    return new Container(key: 'ChatApp', children: [
+      new Container(
+          key: 'Content', style: _content_style, children: appChildren)
+    ]);
 >>> (indent 10)
           return new Container(
               key: 'ChatApp',
@@ -22,11 +20,7 @@
                     style: _content_style,
                     children: appChildren)]);
 <<<
-          return new Container(
-              key: 'ChatApp',
-              children: [
-                new Container(
-                    key: 'Content',
-                    style: _content_style,
-                    children: appChildren)
-              ]);
\ No newline at end of file
+          return new Container(key: 'ChatApp', children: [
+            new Container(
+                key: 'Content', style: _content_style, children: appChildren)
+          ]);
\ No newline at end of file
diff --git a/test/regression/0200/0247.unit b/test/regression/0200/0247.unit
index 0a845a7..218c2bd 100644
--- a/test/regression/0200/0247.unit
+++ b/test/regression/0200/0247.unit
@@ -10,8 +10,8 @@
 <<<
   init(
       {@Option(help: 'The git Uri containing the jefe.yaml.', abbr: 'g')
-      String gitUri,
+          String gitUri,
       @Option(help: 'The directory to install into', abbr: 'd')
-      String installDirectory: '.',
+          String installDirectory: '.',
       @Flag(help: 'Skips the checkout of the develop branch', abbr: 's')
-      bool skipCheckout: false}) async {}
\ No newline at end of file
+          bool skipCheckout: false}) async {}
\ No newline at end of file
diff --git a/test/regression/0300/0387.unit b/test/regression/0300/0387.unit
new file mode 100644
index 0000000..88fcfbc
--- /dev/null
+++ b/test/regression/0300/0387.unit
@@ -0,0 +1,19 @@
+>>>
+greet(@Rest(valueHelp: 'who', help: 'Name(s) to greet.') List<String> who,
+    {@Group.start(title: 'Output')
+    @Option(help: 'How many !\'s to append.')
+    int enthusiasm: 0,
+    @Flag(abbr: 'l', help: 'Put names on separate lines.') bool lineMode: false,
+    @Option(name: 'greeting', help: 'Alternate word to greet with e.g. "Hi".')
+    String salutation: 'Hello'}) {}
+<<<
+greet(
+    @Rest(valueHelp: 'who', help: 'Name(s) to greet.')
+        List<String> who,
+    {@Group.start(title: 'Output')
+    @Option(help: 'How many !\'s to append.')
+        int enthusiasm: 0,
+    @Flag(abbr: 'l', help: 'Put names on separate lines.')
+        bool lineMode: false,
+    @Option(name: 'greeting', help: 'Alternate word to greet with e.g. "Hi".')
+        String salutation: 'Hello'}) {}
\ No newline at end of file
diff --git a/test/regression/0300/0394.stmt b/test/regression/0300/0394.stmt
new file mode 100644
index 0000000..139851a
--- /dev/null
+++ b/test/regression/0300/0394.stmt
@@ -0,0 +1,26 @@
+>>>
+return $.Div(inner: [
+
+    $.Div(id: "container", inner: [
+        $.Canvas(width: maxD, height: maxD, clazz: "center", ref: canvas),
+        $.Form(clazz: "center", inner: [
+            $.Input(type: "range", max: 1000, value: seeds, onChange: onSliderChange)
+        ]),
+        $.Img(src: "math.png", width: "350px", height: "42px", clazz: "center")
+    ]),
+
+    $.Footer(inner: [
+        $.P(id: "notes", inner: "${seeds} seeds")
+    ]),
+]);
+<<<
+return $.Div(inner: [
+  $.Div(id: "container", inner: [
+    $.Canvas(width: maxD, height: maxD, clazz: "center", ref: canvas),
+    $.Form(clazz: "center", inner: [
+      $.Input(type: "range", max: 1000, value: seeds, onChange: onSliderChange)
+    ]),
+    $.Img(src: "math.png", width: "350px", height: "42px", clazz: "center")
+  ]),
+  $.Footer(inner: [$.P(id: "notes", inner: "${seeds} seeds")]),
+]);
\ No newline at end of file
diff --git a/test/regression/0400/0421.unit b/test/regression/0400/0421.unit
index 8097859..ebd840c 100644
--- a/test/regression/0400/0421.unit
+++ b/test/regression/0400/0421.unit
@@ -10,15 +10,13 @@
   var a = [[[1]]];
 }
 <<<
-@Component(
-    selector: 'my-selector',
-    properties: const [
-      'property-1',
-      'property-2',
-      'property-3',
-      'property-4',
-      'property-5'
-    ])
+@Component(selector: 'my-selector', properties: const [
+  'property-1',
+  'property-2',
+  'property-3',
+  'property-4',
+  'property-5'
+])
 @View(
     directives: const [
       OtherComponent1,
@@ -53,15 +51,13 @@
   var a = [[[1]]];
 }
 <<<
-@Component(
-    selector: 'my-selector',
-    properties: const [
-      'property-1',
-      'property-2',
-      'property-3',
-      'property-4',
-      'property-5'
-    ])
+@Component(selector: 'my-selector', properties: const [
+  'property-1',
+  'property-2',
+  'property-3',
+  'property-4',
+  'property-5'
+])
 @View(
     directives: const [
       OtherComponent1,
diff --git a/test/regression/0400/0444.unit b/test/regression/0400/0444.unit
new file mode 100644
index 0000000..944f0f2
--- /dev/null
+++ b/test/regression/0400/0444.unit
@@ -0,0 +1,26 @@
+>>>
+class MyComponent {
+  Object firstArgument;
+  Object superDuperLongNamedArgument;
+  Object secondArgument;
+
+  MyComponent(
+      @Inject(const MyFirstArgument()) this.firstArgument,
+      @Inject(const MySuperDuperLongNamedArgument())
+      this.superDuperLongNamedArgument, // LOOK AT ME
+      @Inject(const MySecondArgument()) this.secondArgument);
+}
+<<<
+class MyComponent {
+  Object firstArgument;
+  Object superDuperLongNamedArgument;
+  Object secondArgument;
+
+  MyComponent(
+      @Inject(const MyFirstArgument())
+          this.firstArgument,
+      @Inject(const MySuperDuperLongNamedArgument())
+          this.superDuperLongNamedArgument, // LOOK AT ME
+      @Inject(const MySecondArgument())
+          this.secondArgument);
+}
\ No newline at end of file
diff --git a/test/regression/0400/0474.unit b/test/regression/0400/0474.unit
new file mode 100644
index 0000000..5e2dc91
--- /dev/null
+++ b/test/regression/0400/0474.unit
@@ -0,0 +1,678 @@
+>>>
+Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
+  'parseCompilationUnit_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
+  'parseDirectives_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseDirectives(arg0)),
+  'parseExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseExpression(arg0)),
+  'parseStatement_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatement(arg0)),
+  'parseStatements_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatements(arg0)),
+  'parseAnnotation_0':
+      new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
+  'parseArgument_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgument()),
+  'parseArgumentList_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
+  'parseBitwiseOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseBitwiseOrExpression()),
+  'parseBlock_0':
+      new MethodTrampoline(0, (Parser target) => target.parseBlock()),
+  'parseClassMember_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseClassMember(arg0)),
+  'parseCompilationUnit_0': new MethodTrampoline(
+      0, (Parser target) => target.parseCompilationUnit2()),
+  'parseConditionalExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseConditionalExpression()),
+  'parseConstructorName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
+  'parseExpression_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
+  'parseExpressionWithoutCascade_0': new MethodTrampoline(
+      0, (Parser target) => target.parseExpressionWithoutCascade()),
+  'parseExtendsClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
+  'parseFormalParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFormalParameterList()),
+  'parseFunctionExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFunctionExpression()),
+  'parseImplementsClause_0': new MethodTrampoline(
+      0, (Parser target) => target.parseImplementsClause()),
+  'parseLabel_0':
+      new MethodTrampoline(0, (Parser target) => target.parseLabel()),
+  'parseLibraryIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLibraryIdentifier()),
+  'parseLogicalOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLogicalOrExpression()),
+  'parseMapLiteralEntry_0':
+      new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
+  'parseNormalFormalParameter_0': new MethodTrampoline(
+      0, (Parser target) => target.parseNormalFormalParameter()),
+  'parsePrefixedIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parsePrefixedIdentifier()),
+  'parseReturnType_0':
+      new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
+  'parseSimpleIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseSimpleIdentifier()),
+  'parseStatement_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
+  'parseStringLiteral_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
+  'parseTypeArgumentList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeArgumentList()),
+  'parseTypeName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeName()),
+  'parseTypeParameter_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
+  'parseTypeParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeParameterList()),
+  'parseWithClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
+  'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
+  'appendScalarValue_5': new MethodTrampoline(
+      5,
+      (Parser target, arg0, arg1, arg2, arg3, arg4) =>
+          target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
+  'computeStringValue_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._computeStringValue(arg0, arg1, arg2)),
+  'convertToFunctionDeclaration_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
+  'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
+      0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
+  'createSyntheticIdentifier_0':
+      new MethodTrampoline(0, (Parser target) => target._createSyntheticIdentifier()),
+  'createSyntheticKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
+  'createSyntheticStringLiteral_0': new MethodTrampoline(0, (Parser target) => target._createSyntheticStringLiteral()),
+  'createSyntheticToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
+  'ensureAssignable_1': new MethodTrampoline(1, (Parser target, arg0) => target._ensureAssignable(arg0)),
+  'expect_1': new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
+  'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
+  'expectKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._expectKeyword(arg0)),
+  'expectSemicolon_0': new MethodTrampoline(0, (Parser target) => target._expectSemicolon()),
+  'findRange_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._findRange(arg0, arg1)),
+  'getCodeBlockRanges_1': new MethodTrampoline(1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
+  'getEndToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._getEndToken(arg0)),
+  'injectToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._injectToken(arg0)),
+  'isFunctionDeclaration_0': new MethodTrampoline(0, (Parser target) => target._isFunctionDeclaration()),
+  'isFunctionExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+  'isHexDigit_1': new MethodTrampoline(1, (Parser target, arg0) => target._isHexDigit(arg0)),
+  'isInitializedVariableDeclaration_0': new MethodTrampoline(0, (Parser target) => target._isInitializedVariableDeclaration()),
+  'isLinkText_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
+  'isOperator_1': new MethodTrampoline(1, (Parser target, arg0) => target._isOperator(arg0)),
+  'isSwitchMember_0': new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+  'isTypedIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
+  'lockErrorListener_0': new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
+  'matches_1': new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
+  'matchesGt_0': new MethodTrampoline(0, (Parser target) => target._matchesGt()),
+  'matchesIdentifier_0': new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
+  'matchesKeyword_1': new MethodTrampoline(1, (Parser target, arg0) => target._matchesKeyword(arg0)),
+  'matchesString_1': new MethodTrampoline(1, (Parser target, arg0) => target._matchesString(arg0)),
+  'optional_1': new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
+  'parseAdditiveExpression_0': new MethodTrampoline(0, (Parser target) => target._parseAdditiveExpression()),
+  'parseAssertStatement_0': new MethodTrampoline(0, (Parser target) => target._parseAssertStatement()),
+  'parseAssignableExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+  'parseAssignableSelector_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseAssignableSelector(arg0, arg1)),
+  'parseAwaitExpression_0': new MethodTrampoline(0, (Parser target) => target._parseAwaitExpression()),
+  'parseBitwiseAndExpression_0': new MethodTrampoline(0, (Parser target) => target._parseBitwiseAndExpression()),
+  'parseBitwiseXorExpression_0': new MethodTrampoline(0, (Parser target) => target._parseBitwiseXorExpression()),
+  'parseBreakStatement_0': new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+  'parseCascadeSection_0': new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+  'parseClassDeclaration_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+  'parseClassMembers_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
+  'parseClassTypeAlias_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseClassTypeAlias(arg0, arg1, arg2)),
+  'parseCombinator_0': new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
+  'parseCombinators_0': new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+  'parseCommentAndMetadata_0': new MethodTrampoline(0, (Parser target) => target._parseCommentAndMetadata()),
+  'parseCommentReference_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+  'parseCommentReferences_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseCommentReferences(arg0)),
+  'parseCompilationUnitMember_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+  'parseConstExpression_0': new MethodTrampoline(0, (Parser target) => target._parseConstExpression()),
+  'parseConstructor_8': new MethodTrampoline(8, (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+  'parseConstructorFieldInitializer_0': new MethodTrampoline(0, (Parser target) => target._parseConstructorFieldInitializer()),
+  'parseContinueStatement_0': new MethodTrampoline(0, (Parser target) => target._parseContinueStatement()),
+  'parseDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseDirective(arg0)),
+  'parseDirectives_0': new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
+  'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) => target._parseDocumentationComment()),
+  'parseDoStatement_0': new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+  'parseEmptyStatement_0': new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+  'parseEnumConstantDeclaration_0': new MethodTrampoline(0, (Parser target) => target._parseEnumConstantDeclaration()),
+  'parseEnumDeclaration_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+  'parseEqualityExpression_0': new MethodTrampoline(0, (Parser target) => target._parseEqualityExpression()),
+  'parseExportDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseExportDirective(arg0)),
+  'parseExpressionList_0': new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+  'parseFinalConstVarOrType_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+  'parseFormalParameter_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
+  'parseForStatement_0': new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+  'parseFunctionBody_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseFunctionBody(arg0, arg1, arg2)),
+  'parseFunctionDeclaration_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseFunctionDeclaration(arg0, arg1, arg2)),
+  'parseFunctionDeclarationStatement_0': new MethodTrampoline(0, (Parser target) => target._parseFunctionDeclarationStatement()),
+  'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
+  'parseFunctionTypeAlias_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseFunctionTypeAlias(arg0, arg1)),
+  'parseGetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseGetter(arg0, arg1, arg2, arg3)),
+  'parseIdentifierList_0': new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
+  'parseIfStatement_0': new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+  'parseImportDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseImportDirective(arg0)),
+  'parseInitializedIdentifierList_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
+  'parseInstanceCreationExpression_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
+  'parseLibraryDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
+  'parseLibraryName_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
+  'parseListLiteral_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
+  'parseListOrMapLiteral_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+  'parseLogicalAndExpression_0': new MethodTrampoline(0, (Parser target) => target._parseLogicalAndExpression()),
+  'parseMapLiteral_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
+  'parseMethodDeclarationAfterParameters_7': new MethodTrampoline(7, (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) => target._parseMethodDeclarationAfterParameters(arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
+  'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
+  'parseModifiers_0': new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
+  'parseMultiplicativeExpression_0': new MethodTrampoline(0, (Parser target) => target._parseMultiplicativeExpression()),
+  'parseNativeClause_0': new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
+  'parseNewExpression_0': new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
+  'parseNonLabeledStatement_0': new MethodTrampoline(0, (Parser target) => target._parseNonLabeledStatement()),
+  'parseOperator_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseOperator(arg0, arg1, arg2)),
+  'parseOptionalReturnType_0': new MethodTrampoline(0, (Parser target) => target._parseOptionalReturnType()),
+  'parsePartDirective_1': new MethodTrampoline(1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+  'parsePostfixExpression_0': new MethodTrampoline(0, (Parser target) => target._parsePostfixExpression()),
+  'parsePrimaryExpression_0': new MethodTrampoline(0, (Parser target) => target._parsePrimaryExpression()),
+  'parseRedirectingConstructorInvocation_0': new MethodTrampoline(0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+  'parseRelationalExpression_0': new MethodTrampoline(0, (Parser target) => target._parseRelationalExpression()),
+  'parseRethrowExpression_0': new MethodTrampoline(0, (Parser target) => target._parseRethrowExpression()),
+  'parseReturnStatement_0': new MethodTrampoline(0, (Parser target) => target._parseReturnStatement()),
+  'parseSetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2, arg3) => target._parseSetter(arg0, arg1, arg2, arg3)),
+  'parseShiftExpression_0': new MethodTrampoline(0, (Parser target) => target._parseShiftExpression()),
+  'parseStatementList_0': new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
+  'parseStringInterpolation_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
+  'parseSuperConstructorInvocation_0': new MethodTrampoline(0, (Parser target) => target._parseSuperConstructorInvocation()),
+  'parseSwitchStatement_0': new MethodTrampoline(0, (Parser target) => target._parseSwitchStatement()),
+  'parseSymbolLiteral_0': new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
+  'parseThrowExpression_0': new MethodTrampoline(0, (Parser target) => target._parseThrowExpression()),
+  'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+  'parseTryStatement_0': new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+  'parseTypeAlias_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
+  'parseUnaryExpression_0': new MethodTrampoline(0, (Parser target) => target._parseUnaryExpression()),
+  'parseVariableDeclaration_0': new MethodTrampoline(0, (Parser target) => target._parseVariableDeclaration()),
+  'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseVariableDeclarationListAfterMetadata(arg0)),
+  'parseVariableDeclarationListAfterType_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+  'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(1, (Parser target, arg0) => target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+  'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
+  'parseWhileStatement_0': new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+  'parseYieldStatement_0': new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+  'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
+  'peekAt_1': new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
+  'reportError_1': new MethodTrampoline(1, (Parser target, arg0) => target._reportError(arg0)),
+  'reportErrorForCurrentToken_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._reportErrorForCurrentToken(arg0, arg1)),
+  'reportErrorForNode_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._reportErrorForNode(arg0, arg1, arg2)),
+  'reportErrorForToken_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._reportErrorForToken(arg0, arg1, arg2)),
+  'skipBlock_0': new MethodTrampoline(0, (Parser target) => target._skipBlock()),
+  'skipFinalConstVarOrType_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
+  'skipFormalParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
+  'skipPastMatchingToken_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
+  'skipPrefixedIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+  'skipReturnType_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipReturnType(arg0)),
+  'skipSimpleIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+  'skipStringInterpolation_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
+  'skipStringLiteral_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+  'skipTypeArgumentList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+  'skipTypeName_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeName(arg0)),
+  'skipTypeParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
+  'tokenMatches_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
+  'tokenMatchesIdentifier_1': new MethodTrampoline(1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
+  'tokenMatchesKeyword_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
+  'tokenMatchesString_2': new MethodTrampoline(2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
+  'translateCharacter_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._translateCharacter(arg0, arg1, arg2)),
+  'unlockErrorListener_0': new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
+  'validateFormalParameterList_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
+  'validateModifiersForClass_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
+  'validateModifiersForConstructor_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
+  'validateModifiersForEnum_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
+  'validateModifiersForField_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
+  'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForFunctionDeclarationStatement(arg0)),
+  'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForGetterOrSetterOrMethod(arg0)),
+  'validateModifiersForOperator_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
+  'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelDeclaration(arg0)),
+  'validateModifiersForTopLevelFunction_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelFunction(arg0)),
+  'validateModifiersForTopLevelVariable_1': new MethodTrampoline(1, (Parser target, arg0) => target._validateModifiersForTopLevelVariable(arg0)),
+  'validateModifiersForTypedef_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
+};
+<<<
+Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
+  'parseCompilationUnit_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
+  'parseDirectives_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseDirectives(arg0)),
+  'parseExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseExpression(arg0)),
+  'parseStatement_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatement(arg0)),
+  'parseStatements_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatements(arg0)),
+  'parseAnnotation_0':
+      new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
+  'parseArgument_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgument()),
+  'parseArgumentList_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
+  'parseBitwiseOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseBitwiseOrExpression()),
+  'parseBlock_0':
+      new MethodTrampoline(0, (Parser target) => target.parseBlock()),
+  'parseClassMember_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseClassMember(arg0)),
+  'parseCompilationUnit_0': new MethodTrampoline(
+      0, (Parser target) => target.parseCompilationUnit2()),
+  'parseConditionalExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseConditionalExpression()),
+  'parseConstructorName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
+  'parseExpression_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
+  'parseExpressionWithoutCascade_0': new MethodTrampoline(
+      0, (Parser target) => target.parseExpressionWithoutCascade()),
+  'parseExtendsClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
+  'parseFormalParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFormalParameterList()),
+  'parseFunctionExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFunctionExpression()),
+  'parseImplementsClause_0': new MethodTrampoline(
+      0, (Parser target) => target.parseImplementsClause()),
+  'parseLabel_0':
+      new MethodTrampoline(0, (Parser target) => target.parseLabel()),
+  'parseLibraryIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLibraryIdentifier()),
+  'parseLogicalOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLogicalOrExpression()),
+  'parseMapLiteralEntry_0':
+      new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
+  'parseNormalFormalParameter_0': new MethodTrampoline(
+      0, (Parser target) => target.parseNormalFormalParameter()),
+  'parsePrefixedIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parsePrefixedIdentifier()),
+  'parseReturnType_0':
+      new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
+  'parseSimpleIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseSimpleIdentifier()),
+  'parseStatement_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
+  'parseStringLiteral_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
+  'parseTypeArgumentList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeArgumentList()),
+  'parseTypeName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeName()),
+  'parseTypeParameter_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
+  'parseTypeParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeParameterList()),
+  'parseWithClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
+  'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
+  'appendScalarValue_5': new MethodTrampoline(
+      5,
+      (Parser target, arg0, arg1, arg2, arg3, arg4) =>
+          target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
+  'computeStringValue_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._computeStringValue(arg0, arg1, arg2)),
+  'convertToFunctionDeclaration_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
+  'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
+      0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
+  'createSyntheticIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target._createSyntheticIdentifier()),
+  'createSyntheticKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
+  'createSyntheticStringLiteral_0': new MethodTrampoline(
+      0, (Parser target) => target._createSyntheticStringLiteral()),
+  'createSyntheticToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
+  'ensureAssignable_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._ensureAssignable(arg0)),
+  'expect_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
+  'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
+  'expectKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._expectKeyword(arg0)),
+  'expectSemicolon_0':
+      new MethodTrampoline(0, (Parser target) => target._expectSemicolon()),
+  'findRange_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._findRange(arg0, arg1)),
+  'getCodeBlockRanges_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
+  'getEndToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._getEndToken(arg0)),
+  'injectToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._injectToken(arg0)),
+  'isFunctionDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._isFunctionDeclaration()),
+  'isFunctionExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+  'isHexDigit_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isHexDigit(arg0)),
+  'isInitializedVariableDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._isInitializedVariableDeclaration()),
+  'isLinkText_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
+  'isOperator_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isOperator(arg0)),
+  'isSwitchMember_0':
+      new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+  'isTypedIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
+  'lockErrorListener_0':
+      new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
+  'matches_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
+  'matchesGt_0':
+      new MethodTrampoline(0, (Parser target) => target._matchesGt()),
+  'matchesIdentifier_0':
+      new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
+  'matchesKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._matchesKeyword(arg0)),
+  'matchesString_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._matchesString(arg0)),
+  'optional_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
+  'parseAdditiveExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAdditiveExpression()),
+  'parseAssertStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAssertStatement()),
+  'parseAssignableExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+  'parseAssignableSelector_2': new MethodTrampoline(
+      2,
+      (Parser target, arg0, arg1) =>
+          target._parseAssignableSelector(arg0, arg1)),
+  'parseAwaitExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAwaitExpression()),
+  'parseBitwiseAndExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseBitwiseAndExpression()),
+  'parseBitwiseXorExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseBitwiseXorExpression()),
+  'parseBreakStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+  'parseCascadeSection_0':
+      new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+  'parseClassDeclaration_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+  'parseClassMembers_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
+  'parseClassTypeAlias_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseClassTypeAlias(arg0, arg1, arg2)),
+  'parseCombinator_0':
+      new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
+  'parseCombinators_0':
+      new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+  'parseCommentAndMetadata_0': new MethodTrampoline(
+      0, (Parser target) => target._parseCommentAndMetadata()),
+  'parseCommentReference_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+  'parseCommentReferences_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseCommentReferences(arg0)),
+  'parseCompilationUnitMember_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+  'parseConstExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseConstExpression()),
+  'parseConstructor_8': new MethodTrampoline(
+      8,
+      (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target
+          ._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+  'parseConstructorFieldInitializer_0': new MethodTrampoline(
+      0, (Parser target) => target._parseConstructorFieldInitializer()),
+  'parseContinueStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseContinueStatement()),
+  'parseDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseDirective(arg0)),
+  'parseDirectives_0':
+      new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
+  'parseDocumentationComment_0': new MethodTrampoline(
+      0, (Parser target) => target._parseDocumentationComment()),
+  'parseDoStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+  'parseEmptyStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+  'parseEnumConstantDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._parseEnumConstantDeclaration()),
+  'parseEnumDeclaration_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+  'parseEqualityExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseEqualityExpression()),
+  'parseExportDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseExportDirective(arg0)),
+  'parseExpressionList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+  'parseFinalConstVarOrType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+  'parseFormalParameter_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
+  'parseForStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+  'parseFunctionBody_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseFunctionBody(arg0, arg1, arg2)),
+  'parseFunctionDeclaration_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseFunctionDeclaration(arg0, arg1, arg2)),
+  'parseFunctionDeclarationStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseFunctionDeclarationStatement()),
+  'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(
+      2,
+      (Parser target, arg0, arg1) =>
+          target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
+  'parseFunctionTypeAlias_2': new MethodTrampoline(
+      2,
+      (Parser target, arg0, arg1) =>
+          target._parseFunctionTypeAlias(arg0, arg1)),
+  'parseGetter_4': new MethodTrampoline(
+      4,
+      (Parser target, arg0, arg1, arg2, arg3) =>
+          target._parseGetter(arg0, arg1, arg2, arg3)),
+  'parseIdentifierList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
+  'parseIfStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+  'parseImportDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseImportDirective(arg0)),
+  'parseInitializedIdentifierList_4': new MethodTrampoline(
+      4,
+      (Parser target, arg0, arg1, arg2, arg3) =>
+          target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
+  'parseInstanceCreationExpression_1': new MethodTrampoline(1,
+      (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
+  'parseLibraryDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
+  'parseLibraryName_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
+  'parseListLiteral_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
+  'parseListOrMapLiteral_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+  'parseLogicalAndExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseLogicalAndExpression()),
+  'parseMapLiteral_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
+  'parseMethodDeclarationAfterParameters_7': new MethodTrampoline(
+      7,
+      (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) =>
+          target._parseMethodDeclarationAfterParameters(
+              arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
+  'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(
+      4,
+      (Parser target, arg0, arg1, arg2, arg3) => target
+          ._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
+  'parseModifiers_0':
+      new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
+  'parseMultiplicativeExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseMultiplicativeExpression()),
+  'parseNativeClause_0':
+      new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
+  'parseNewExpression_0':
+      new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
+  'parseNonLabeledStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseNonLabeledStatement()),
+  'parseOperator_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseOperator(arg0, arg1, arg2)),
+  'parseOptionalReturnType_0': new MethodTrampoline(
+      0, (Parser target) => target._parseOptionalReturnType()),
+  'parsePartDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+  'parsePostfixExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parsePostfixExpression()),
+  'parsePrimaryExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parsePrimaryExpression()),
+  'parseRedirectingConstructorInvocation_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+  'parseRelationalExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRelationalExpression()),
+  'parseRethrowExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRethrowExpression()),
+  'parseReturnStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseReturnStatement()),
+  'parseSetter_4': new MethodTrampoline(
+      4,
+      (Parser target, arg0, arg1, arg2, arg3) =>
+          target._parseSetter(arg0, arg1, arg2, arg3)),
+  'parseShiftExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseShiftExpression()),
+  'parseStatementList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
+  'parseStringInterpolation_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
+  'parseSuperConstructorInvocation_0': new MethodTrampoline(
+      0, (Parser target) => target._parseSuperConstructorInvocation()),
+  'parseSwitchStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseSwitchStatement()),
+  'parseSymbolLiteral_0':
+      new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
+  'parseThrowExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseThrowExpression()),
+  'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(
+      0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+  'parseTryStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+  'parseTypeAlias_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
+  'parseUnaryExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseUnaryExpression()),
+  'parseVariableDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._parseVariableDeclaration()),
+  'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._parseVariableDeclarationListAfterMetadata(arg0)),
+  'parseVariableDeclarationListAfterType_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+  'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+  'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
+  'parseWhileStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+  'parseYieldStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+  'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
+  'peekAt_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
+  'reportError_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._reportError(arg0)),
+  'reportErrorForCurrentToken_2': new MethodTrampoline(
+      2,
+      (Parser target, arg0, arg1) =>
+          target._reportErrorForCurrentToken(arg0, arg1)),
+  'reportErrorForNode_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._reportErrorForNode(arg0, arg1, arg2)),
+  'reportErrorForToken_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._reportErrorForToken(arg0, arg1, arg2)),
+  'skipBlock_0':
+      new MethodTrampoline(0, (Parser target) => target._skipBlock()),
+  'skipFinalConstVarOrType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
+  'skipFormalParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
+  'skipPastMatchingToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
+  'skipPrefixedIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+  'skipReturnType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipReturnType(arg0)),
+  'skipSimpleIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+  'skipStringInterpolation_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
+  'skipStringLiteral_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+  'skipTypeArgumentList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+  'skipTypeName_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeName(arg0)),
+  'skipTypeParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
+  'tokenMatches_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
+  'tokenMatchesIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
+  'tokenMatchesKeyword_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
+  'tokenMatchesString_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
+  'translateCharacter_3': new MethodTrampoline(
+      3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._translateCharacter(arg0, arg1, arg2)),
+  'unlockErrorListener_0':
+      new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
+  'validateFormalParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
+  'validateModifiersForClass_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
+  'validateModifiersForConstructor_1': new MethodTrampoline(1,
+      (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
+  'validateModifiersForEnum_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
+  'validateModifiersForField_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
+  'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._validateModifiersForFunctionDeclarationStatement(arg0)),
+  'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._validateModifiersForGetterOrSetterOrMethod(arg0)),
+  'validateModifiersForOperator_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
+  'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelDeclaration(arg0)),
+  'validateModifiersForTopLevelFunction_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelFunction(arg0)),
+  'validateModifiersForTopLevelVariable_1': new MethodTrampoline(
+      1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelVariable(arg0)),
+  'validateModifiersForTypedef_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
+};
\ No newline at end of file
diff --git a/test/regression/other/angular.unit b/test/regression/other/angular.unit
index 4a39473..add59be 100644
--- a/test/regression/other/angular.unit
+++ b/test/regression/other/angular.unit
@@ -18,4 +18,28 @@
           'a': {'b': 1}
         })).toEqual(1);
       });
-    }
\ No newline at end of file
+    }
+>>>
+@Component(
+    selector: 'io-controller',
+    template: r'<content></content>',
+    map: const {
+      'attr': '@attr',
+      'expr': '<=>expr',
+      'once': '=>!exprOnce',
+      'ondone': '&onDone',
+      'on-optional': '&onOptional'
+    })
+class IoControllerComponent implements ScopeAware {}
+<<<
+@Component(
+    selector: 'io-controller',
+    template: r'<content></content>',
+    map: const {
+      'attr': '@attr',
+      'expr': '<=>expr',
+      'once': '=>!exprOnce',
+      'ondone': '&onDone',
+      'on-optional': '&onOptional'
+    })
+class IoControllerComponent implements ScopeAware {}
\ No newline at end of file
diff --git a/test/splitting/list_arguments.stmt b/test/splitting/list_arguments.stmt
index 7206c5a..ac10cc1 100644
--- a/test/splitting/list_arguments.stmt
+++ b/test/splitting/list_arguments.stmt
@@ -273,10 +273,35 @@
   element,
   element
 ]);
->>> nest trailing named if there are non-body named
-longFunctionName(a: argument, b: argument,
+>>> allow leading non-collection to not split
+longFunctionName(a: arg, b: arg,
 c: [element, element, element, element], d: [element, element, element, element]);
 <<<
+longFunctionName(a: arg, b: arg, c: [
+  element,
+  element,
+  element,
+  element
+], d: [
+  element,
+  element,
+  element,
+  element
+]);
+>>> don't allow splitting before first arg while splitting collections
+longFunctionName(
+    a: argument, b: argument, c: [
+  element,
+  element,
+  element,
+  element
+], d: [
+  element,
+  element,
+  element,
+  element
+]);
+<<<
 longFunctionName(
     a: argument,
     b: argument,
@@ -292,6 +317,41 @@
       element,
       element
     ]);
+>>> trailing named arguments that do not split
+longFunctionName(a: [element, element, element, element],
+b: [element, element, element, element], c: argument, d: argument);
+<<<
+longFunctionName(a: [
+  element,
+  element,
+  element,
+  element
+], b: [
+  element,
+  element,
+  element,
+  element
+], c: argument, d: argument);
+>>> trailing named arguments that do split
+longFunctionName(a: [element, element, element, element],
+b: [element, element, element, element], c: argument, d: argument, e: argument);
+<<<
+longFunctionName(
+    a: [
+      element,
+      element,
+      element,
+      element
+    ],
+    b: [
+      element,
+      element,
+      element,
+      element
+    ],
+    c: argument,
+    d: argument,
+    e: argument);
 >>> leading named arguments
 longFunctionName(name1: [element, element], name2: [element, element], name3: argument, name4: argument);
 <<<
diff --git a/test/whitespace/metadata.unit b/test/whitespace/metadata.unit
index 29335fa..292e8c8 100644
--- a/test/whitespace/metadata.unit
+++ b/test/whitespace/metadata.unit
@@ -225,4 +225,10 @@
 named({@foo bar}) {}
 <<<
 positional([@foo bar]) {}
-named({@foo bar}) {}
\ No newline at end of file
+named({@foo bar}) {}
+>>> split between metadata and parameter indents
+function(@VeryLongMetadataAnnotation longParameter) {}
+<<<
+function(
+    @VeryLongMetadataAnnotation
+        longParameter) {}
\ No newline at end of file