Formatting support for spread and if control flow elements.

(I'll do "for" in a separate commit.)
diff --git a/lib/src/argument_list_visitor.dart b/lib/src/argument_list_visitor.dart
index c71b9f0..c17264c 100644
--- a/lib/src/argument_list_visitor.dart
+++ b/lib/src/argument_list_visitor.dart
@@ -453,7 +453,7 @@
       rule.disableSplitOnInnerRules();
 
       // Tell it to use the rule we've already created.
-      visitor.beforeBlock(_blocks[argument], this);
+      visitor.beforeBlock(_blocks[argument], blockRule, previousSplit);
     } else if (_allArguments.length > 1) {
       // Edge case: Only bump the nesting if there are multiple arguments. This
       // lets us avoid spurious indentation in cases like:
diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart
index afd0626..4a3950c 100644
--- a/lib/src/dart_formatter.dart
+++ b/lib/src/dart_formatter.dart
@@ -114,6 +114,9 @@
     // Parse it.
     var parser = new Parser(stringSource, errorListener);
     parser.enableOptionalNewAndConst = true;
+    parser.enableSetLiterals = true;
+    parser.enableSpreadCollections = true;
+    parser.enableControlFlowCollections = true;
 
     AstNode node;
     if (source.isCompilationUnit) {
diff --git a/lib/src/nesting_builder.dart b/lib/src/nesting_builder.dart
index 2efebb3..544ab57 100644
--- a/lib/src/nesting_builder.dart
+++ b/lib/src/nesting_builder.dart
@@ -64,22 +64,11 @@
   NestingLevel get currentNesting =>
       _pendingNesting != null ? _pendingNesting : _nesting;
 
-  /// The top "nesting level" that represents no expression nesting for the
-  /// current block.
-  NestingLevel get blockNesting {
-    // Walk the nesting levels until we bottom out.
-    var result = _nesting;
-    while (result.parent != null) {
-      result = result.parent;
-    }
-    return result;
-  }
-
   /// Creates a new indentation level [spaces] deeper than the current one.
   ///
   /// If omitted, [spaces] defaults to [Indent.block].
   void indent([int spaces]) {
-    if (spaces == null) spaces = Indent.block;
+    spaces ??= Indent.block;
 
     // Indentation should only change outside of nesting.
     assert(_pendingNesting == null);
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 6aa452a..85c02e1 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -153,7 +153,12 @@
   /// Before a block argument is visited, [ArgumentSublist] binds itself to the
   /// beginning token of each block it controls. When we later visit that
   /// literal, we use the token to find that association.
-  final Map<Token, ArgumentSublist> _blockArgumentLists = {};
+  ///
+  /// This mapping is also used for spread collection literals that appear
+  /// inside control flow elements to ensure that when a "then" collection
+  /// splits, the corresponding "else" one does too.
+  final Map<Token, Rule> _blockRules = {};
+  final Map<Token, Chunk> _blockPreviousChunks = {};
 
   /// Initialize a newly created visitor to write source code representing
   /// the visited nodes to the given [writer].
@@ -1387,6 +1392,121 @@
     _visitCombinator(node.keyword, node.hiddenNames);
   }
 
+  void visitIfElement(IfElement node) {
+    // Wrap the whole thing in a single rule. If a split happens inside the
+    // condition or the then clause, we want the then and else clauses to split.
+    builder.startRule();
+
+    token(node.ifKeyword);
+    space();
+    token(node.leftParenthesis);
+    visit(node.condition);
+    token(node.rightParenthesis);
+
+    // If the body of the then or else branch is a spread of a collection
+    // literal, then we want to format those collections more like blocks than
+    // like standalone objects. In particular, if both the then and else branch
+    // are spread collection literals, we want to ensure that they both split
+    // if either splits. So this:
+    //
+    //     [
+    //       if (condition) ...[
+    //         thenClause
+    //       ] else ...[
+    //         elseClause
+    //       ]
+    //     ]
+    //
+    // And not something like this:
+    //
+    //     [
+    //       if (condition) ...[
+    //         thenClause
+    //       ] else ...[elseClause]
+    //     ]
+    //
+    // To do that, if we see that either clause is a spread collection, we
+    // create a single rule and force both collections to use it.
+    var thenSpreadBracket = _findSpreadCollectionBracket(node.thenElement);
+    var elseSpreadBracket = _findSpreadCollectionBracket(node.elseElement);
+
+    if (thenSpreadBracket != null || elseSpreadBracket != null) {
+      var spreadRule = Rule();
+      if (thenSpreadBracket != null) {
+        beforeBlock(thenSpreadBracket, spreadRule, null);
+      }
+
+      if (elseSpreadBracket != null) {
+        beforeBlock(elseSpreadBracket, spreadRule, null);
+      }
+    }
+
+    builder.nestExpression(indent: 2, now: true);
+
+    // Treat a spread of a collection literal like a block in an if statement
+    // and don't split after the "else".
+    if (thenSpreadBracket != null) {
+      space();
+    } else {
+      split();
+
+      // If the then clause is a non-spread collection or lambda, make sure the
+      // body is indented.
+      builder.startBlockArgumentNesting();
+    }
+
+    visit(node.thenElement);
+
+    if (thenSpreadBracket == null) builder.endBlockArgumentNesting();
+    builder.unnest();
+
+    if (node.elseElement != null) {
+      if (thenSpreadBracket != null) {
+        space();
+      } else {
+        split();
+      }
+
+      token(node.elseKeyword);
+
+      builder.nestExpression(indent: 2, now: true);
+
+      if (elseSpreadBracket != null) {
+        space();
+      } else {
+        split();
+
+        // If the else clause is a non-spread collection or lambda, make sure
+        // the body is indented.
+        builder.startBlockArgumentNesting();
+      }
+
+      visit(node.elseElement);
+
+      if (elseSpreadBracket == null) builder.endBlockArgumentNesting();
+      builder.unnest();
+    }
+
+    builder.endRule();
+  }
+
+  /// If [node] is a spread of a collection literal, then this returns the
+  /// token for the opening bracket of the collection, as in:
+  ///
+  ///     [ ...[a, list] ]
+  ///     //   ^
+  ///
+  /// Otherwise, returns `null`.
+  Token _findSpreadCollectionBracket(AstNode node) {
+    if (node is SpreadElement) {
+      var expression = node.expression;
+      if (expression is ListLiteral) return expression.leftBracket;
+      if (expression is SetOrMapLiteral) return expression.leftBracket;
+    }
+
+    return null;
+  }
+
   visitIfStatement(IfStatement node) {
     builder.nestExpression();
     token(node.ifKeyword);
@@ -1611,10 +1731,12 @@
   }
 
   visitMapLiteralEntry(MapLiteralEntry node) {
+    builder.nestExpression();
     visit(node.key);
     token(node.separator);
     soloSplit();
     visit(node.value);
+    builder.unnest();
   }
 
   visitMethodDeclaration(MethodDeclaration node) {
@@ -1882,6 +2004,11 @@
     _writeStringLiteral(node.literal);
   }
 
+  visitSpreadElement(SpreadElement node) {
+    token(node.spreadOperator);
+    visit(node.expression);
+  }
+
   visitStringInterpolation(StringInterpolation node) {
     for (var element in node.elements) {
       visit(element);
@@ -2511,15 +2638,12 @@
         }
       }
 
-      builder.nestExpression();
       visit(element);
 
       // The comma after the element.
       if (element.endToken.next.type == TokenType.COMMA) {
         token(element.endToken.next);
       }
-
-      builder.unnest();
     }
 
     builder.endRule();
@@ -2741,14 +2865,17 @@
   void _startLiteralBody(Token leftBracket) {
     token(leftBracket);
 
-    // See if this literal is associated with an argument list that wants to
-    // handle splitting and indenting it. If not, we'll use a default rule.
-    var rule;
-    var argumentChunk;
-    if (_blockArgumentLists.containsKey(leftBracket)) {
-      var argumentList = _blockArgumentLists[leftBracket];
-      rule = argumentList.blockRule;
-      argumentChunk = argumentList.previousSplit;
+    // See if this literal is associated with an argument list or if element
+    // that wants to handle splitting and indenting it. If not, we'll use a
+    // default rule.
+    Rule rule;
+    if (_blockRules.containsKey(leftBracket)) {
+      rule = _blockRules[leftBracket];
+    }
+
+    Chunk argumentChunk;
+    if (_blockPreviousChunks.containsKey(leftBracket)) {
+      argumentChunk = _blockPreviousChunks[leftBracket];
     }
 
     // Create a rule for whether or not to split the block contents.
@@ -2844,12 +2971,15 @@
   }
 
   /// Marks the block that starts with [token] as being controlled by
-  /// [argumentList].
+  /// [rule] and following [previousChunk].
   ///
-  /// When the block is visited, [argumentList] will determine the
-  /// indentation and splitting rule for the block.
-  void beforeBlock(Token token, ArgumentSublist argumentList) {
-    _blockArgumentLists[token] = argumentList;
+  /// When the block is visited, these will determine the indentation and
+  /// splitting rule for the block. These are used for handling block-like
+  /// expressions inside argument lists and spread collections inside if
+  /// elements.
+  void beforeBlock(Token token, Rule rule, [Chunk previousChunk]) {
+    _blockRules[token] = rule;
+    if (previousChunk != null) _blockPreviousChunks[token] = previousChunk;
   }
 
   /// Writes the beginning of a brace-delimited body and handles indenting and
diff --git a/test/splitting/list_collection_if.stmt b/test/splitting/list_collection_if.stmt
new file mode 100644
index 0000000..7f272a7
--- /dev/null
+++ b/test/splitting/list_collection_if.stmt
@@ -0,0 +1,217 @@
+40 columns                              |
+>>> split in condition
+var list = [1, if (veryLongConditionExpression || anotherPart) 2];
+<<<
+var list = [
+  1,
+  if (veryLongConditionExpression ||
+      anotherPart)
+    2
+];
+>>> without else on one line
+var list = [1, if (c) 2, 3];
+<<<
+var list = [1, if (c) 2, 3];
+>>> with else on one line
+var list = [1, if (c) 2 else 2, 3];
+<<<
+var list = [1, if (c) 2 else 2, 3];
+>>> split collection before if
+var list = [if (c) somewhatLongThingHere];
+<<<
+var list = [
+  if (c) somewhatLongThingHere
+];
+>>> one line in multi-line
+var list = [veryLongThingThatForcesASplit, if (c) 2, 3];
+<<<
+var list = [
+  veryLongThingThatForcesASplit,
+  if (c) 2,
+  3
+];
+>>> one line in multi-line with else
+var list = [veryLongThingThatForcesASplit, if (c) 2 else 2, 3];
+<<<
+var list = [
+  veryLongThingThatForcesASplit,
+  if (c) 2 else 2,
+  3
+];
+>>> long then branch forces split
+var list = [1, if (condition) veryLongThingThatForcesASplit, 3];
+<<<
+var list = [
+  1,
+  if (condition)
+    veryLongThingThatForcesASplit,
+  3
+];
+>>> long then branch forces both to split
+var list = [1, if (condition) veryLongThingThatForcesASplit else 2, 3];
+<<<
+var list = [
+  1,
+  if (condition)
+    veryLongThingThatForcesASplit
+  else
+    2,
+  3
+];
+>>> long else branch forces both to split
+var list = [1, if (condition) 2 else veryLongThingThatForcesASplit, 3];
+<<<
+var list = [
+  1,
+  if (condition)
+    2
+  else
+    veryLongThingThatForcesASplit,
+  3
+];
+>>> split inside then
+var list = [1, if (condition) veryLongThingThatForcesASplit + anotherLongThing, 3];
+<<<
+var list = [
+  1,
+  if (condition)
+    veryLongThingThatForcesASplit +
+        anotherLongThing,
+  3
+];
+>>> split inside else
+var list = [1, if (condition) ok else veryLongThingThatForcesASplit + anotherLongThing, 3];
+<<<
+var list = [
+  1,
+  if (condition)
+    ok
+  else
+    veryLongThingThatForcesASplit +
+        anotherLongThing,
+  3
+];
+>>> trailing comma
+var list = [if (c) 2,];
+<<<
+var list = [
+  if (c) 2,
+];
+>>> spread list inside if stays on one line if it fits
+var list = [if (c) ...[1, 2]];
+<<<
+var list = [
+  if (c) ...[1, 2]
+];
+>>> spread list inside if formats like block if it splits
+var list = [if (c) ...[1, 2,]];
+<<<
+var list = [
+  if (c) ...[
+    1,
+    2,
+  ]
+];
+>>> both spreads split if then must
+var list = [if (c) ...[1, 2,] else ...[1, 2]];
+<<<
+var list = [
+  if (c) ...[
+    1,
+    2,
+  ] else ...[
+    1,
+    2
+  ]
+];
+>>> both spreads split if else must
+var list = [if (c) ...[1, 2] else ...[1, 2,]];
+<<<
+var list = [
+  if (c) ...[
+    1,
+    2
+  ] else ...[
+    1,
+    2,
+  ]
+];
+>>> a split collection that isn't spread wraps and indents
+var list = [if (c) [1,2,]];
+<<<
+var list = [
+  if (c)
+    [
+      1,
+      2,
+    ]
+];
+>>> a split collection that isn't spread wraps and indents
+var list = [if (c) [1,2,] else thing];
+<<<
+var list = [
+  if (c)
+    [
+      1,
+      2,
+    ]
+  else
+    thing
+];
+>>> a split collection that isn't spread wraps and indents
+var list = [if (c) thing else [1,2,]];
+<<<
+var list = [
+  if (c)
+    thing
+  else
+    [
+      1,
+      2,
+    ]
+];
+>>> lambda inside then
+var list = [if (c) () { body; }];
+<<<
+var list = [
+  if (c)
+    () {
+      body;
+    }
+];
+>>> lambda inside else
+var list = [if (c) thing else () { body; }];
+<<<
+var list = [
+  if (c)
+    thing
+  else
+    () {
+      body;
+    }
+];
+>>> nested if doesn't split if it fits
+var list = [if (c) if (d) thing];
+<<<
+var list = [if (c) if (d) thing];
+>>> split collection before nested if
+var list = [if (c) if (d) fairlyLongThingHere];
+<<<
+var list = [
+  if (c) if (d) fairlyLongThingHere
+];
+>>> just split outer if
+var list = [if (condition) if (another) longThingHereThatIsLong];
+<<<
+var list = [
+  if (condition)
+    if (another) longThingHereThatIsLong
+];
+>>> split inside condition
+var list = [if (veryLongCondition + thatNeedsToSplit) thing];
+<<<
+var list = [
+  if (veryLongCondition +
+      thatNeedsToSplit)
+    thing
+];
\ No newline at end of file
diff --git a/test/splitting/lists.stmt b/test/splitting/lists.stmt
index 86698f6..4b45a1e 100644
--- a/test/splitting/lists.stmt
+++ b/test/splitting/lists.stmt
@@ -167,4 +167,28 @@
   element,
 
   element
+];
+>>> a spread list literal splits an outer list even if it fits
+var list = [1, ...[2, 3], 4];
+<<<
+var list = [
+  1,
+  ...[2, 3],
+  4
+];
+>>> spread empty list does not force outer split
+var list = [1, ...[], 4];
+<<<
+var list = [1, ...[], 4];
+>>> split inside spread expression
+var list = [1, ...some + very + long + spread + expression, 3];
+<<<
+var list = [
+  1,
+  ...some +
+      very +
+      long +
+      spread +
+      expression,
+  3
 ];
\ No newline at end of file
diff --git a/test/splitting/map_collection_if.stmt b/test/splitting/map_collection_if.stmt
new file mode 100644
index 0000000..ef4758a
--- /dev/null
+++ b/test/splitting/map_collection_if.stmt
@@ -0,0 +1,203 @@
+40 columns                              |
+>>> split in condition
+var map = {1: 1, if (veryLongConditionExpression || anotherPart) 2: 2};
+<<<
+var map = {
+  1: 1,
+  if (veryLongConditionExpression ||
+      anotherPart)
+    2: 2
+};
+>>> without else on one line
+var map = {1: 1, if (c) 2: 2, 3: 3};
+<<<
+var map = {1: 1, if (c) 2: 2, 3: 3};
+>>> with else on one line
+var map = {1: 1, if (c) 2: 2 else 2: 2};
+<<<
+var map = {1: 1, if (c) 2: 2 else 2: 2};
+>>> split collection before if
+var map = {if (c) somewhatLongThingHere: 1};
+<<<
+var map = {
+  if (c) somewhatLongThingHere: 1
+};
+>>> one line in multi-line
+var map = {veryLongThingThatForcesASplit, if (c) 2: 2, 3: 3};
+<<<
+var map = {
+  veryLongThingThatForcesASplit,
+  if (c) 2: 2,
+  3: 3
+};
+>>> one line in multi-line with else
+var map = {veryLongThingThatForcesASplit: 1, if (c) 2: 2 else 2: 2, 3: 3};
+<<<
+var map = {
+  veryLongThingThatForcesASplit: 1,
+  if (c) 2: 2 else 2: 2,
+  3: 3
+};
+>>> long then branch forces split
+var map = {1: 1, if (condition) veryLongThingThatForcesASplit: 2, 3: 3};
+<<<
+var map = {
+  1: 1,
+  if (condition)
+    veryLongThingThatForcesASplit: 2,
+  3: 3
+};
+>>> long then branch forces both to split
+var map = {1: 1, if (condition) veryLongThingThatForcesASplit: 2 else 2: 2, 3: 3};
+<<<
+var map = {
+  1: 1,
+  if (condition)
+    veryLongThingThatForcesASplit: 2
+  else
+    2: 2,
+  3: 3
+};
+>>> long else branch forces both to split
+var map = {1: 1, if (condition) 2: 2 else veryLongThingThatForcesASplit: 2, 3: 3};
+<<<
+var map = {
+  1: 1,
+  if (condition)
+    2: 2
+  else
+    veryLongThingThatForcesASplit: 2,
+  3: 3
+};
+>>> trailing comma
+var map = {if (c) 2: 2,};
+<<<
+var map = {
+  if (c) 2: 2,
+};
+>>> spread list inside if stays on one line if it fits
+var map = {if (c) ...{1: 1, 2: 2}};
+<<<
+var map = {
+  if (c) ...{1: 1, 2: 2}
+};
+>>> spread list inside if formats like block if it splits
+var map = {if (c) ...{1: 1, 2: 2,}};
+<<<
+var map = {
+  if (c) ...{
+    1: 1,
+    2: 2,
+  }
+};
+>>> both spreads split if then must
+var map = {if (c) ...{1: 1, 2: 2,} else ...{1: 1, 2: 2}};
+<<<
+var map = {
+  if (c) ...{
+    1: 1,
+    2: 2,
+  } else ...{
+    1: 1,
+    2: 2
+  }
+};
+>>> both spreads split if else must
+var map = {if (c) ...{1: 1, 2: 2} else ...{1: 1, 2: 2,}};
+<<<
+var map = {
+  if (c) ...{
+    1: 1,
+    2: 2
+  } else ...{
+    1: 1,
+    2: 2,
+  }
+};
+>>> a split collection that isn't spread wraps and indents
+var map = {if (c) {1: 1,2: 2,}};
+<<<
+var map = {
+  if (c)
+    {
+      1: 1,
+      2: 2,
+    }
+};
+>>> a split collection that isn't spread wraps and indents
+var map = {if (c) {1: 1,2: 2,} else thing: 3};
+<<<
+var map = {
+  if (c)
+    {
+      1: 1,
+      2: 2,
+    }
+  else
+    thing: 3
+};
+>>> a split collection that isn't spread wraps and indents
+var map = {if (c) thing: 0 else {1: 1,2: 2,}};
+<<<
+var map = {
+  if (c)
+    thing: 0
+  else
+    {
+      1: 1,
+      2: 2,
+    }
+};
+>>> lambda inside then
+var map = {if (c) k: () { body; }};
+<<<
+var map = {
+  if (c)
+    k: () {
+      body;
+    }
+};
+>>> lambda inside else
+var map = {if (c) thing: 1 else k: () { body; }};
+<<<
+var map = {
+  if (c)
+    thing: 1
+  else
+    k: () {
+      body;
+    }
+};
+>>> nested if doesn't split if it fits
+var map = {if (c) if (d) thing: 1};
+<<<
+var map = {if (c) if (d) thing: 1};
+>>> split collection before nested if
+var map = {if (c) if (d) fairlyLongThingHere: 1};
+<<<
+var map = {
+  if (c) if (d) fairlyLongThingHere: 1
+};
+>>> just split outer if
+var map = {if (condition) if (another) longThingThatIsLong: 1};
+<<<
+var map = {
+  if (condition)
+    if (another) longThingThatIsLong: 1
+};
+>>> split inside condition
+var map = {if (veryLongCondition + thatNeedsToSplit) thing: 1};
+<<<
+var map = {
+  if (veryLongCondition +
+      thatNeedsToSplit)
+    thing: 1
+};
+>>> split entry inside if
+var map = { if (condition) veryLongKeyExpression: andAVeryLongValueExpression};
+<<<
+var map = {
+  if (condition)
+    veryLongKeyExpression:
+        andAVeryLongValueExpression
+};
\ No newline at end of file
diff --git a/test/splitting/set_collection_if.stmt b/test/splitting/set_collection_if.stmt
new file mode 100644
index 0000000..86375ad
--- /dev/null
+++ b/test/splitting/set_collection_if.stmt
@@ -0,0 +1,195 @@
+40 columns                              |
+>>> split in condition
+var set = {1, if (veryLongConditionExpression || anotherPart) 2};
+<<<
+var set = {
+  1,
+  if (veryLongConditionExpression ||
+      anotherPart)
+    2
+};
+>>> without else on one line
+var set = {1, if (c) 2, 3};
+<<<
+var set = {1, if (c) 2, 3};
+>>> with else on one line
+var set = {1, if (c) 2 else 2, 3};
+<<<
+var set = {1, if (c) 2 else 2, 3};
+>>> split collection before if
+var set = {if (c) somewhatLongThingHere};
+<<<
+var set = {
+  if (c) somewhatLongThingHere
+};
+>>> one line in multi-line
+var set = {veryLongThingThatForcesASplit, if (c) 2, 3};
+<<<
+var set = {
+  veryLongThingThatForcesASplit,
+  if (c) 2,
+  3
+};
+>>> one line in multi-line with else
+var set = {veryLongThingThatForcesASplit, if (c) 2 else 2, 3};
+<<<
+var set = {
+  veryLongThingThatForcesASplit,
+  if (c) 2 else 2,
+  3
+};
+>>> long then branch forces split
+var set = {1, if (condition) veryLongThingThatForcesASplit, 3};
+<<<
+var set = {
+  1,
+  if (condition)
+    veryLongThingThatForcesASplit,
+  3
+};
+>>> long then branch forces both to split
+var set = {1, if (condition) veryLongThingThatForcesASplit else 2, 3};
+<<<
+var set = {
+  1,
+  if (condition)
+    veryLongThingThatForcesASplit
+  else
+    2,
+  3
+};
+>>> long else branch forces both to split
+var set = {1, if (condition) 2 else veryLongThingThatForcesASplit, 3};
+<<<
+var set = {
+  1,
+  if (condition)
+    2
+  else
+    veryLongThingThatForcesASplit,
+  3
+};
+>>> trailing comma
+var set = {if (c) 2,};
+<<<
+var set = {
+  if (c) 2,
+};
+>>> spread list inside if stays on one line if it fits
+var set = {if (c) ...{1, 2}};
+<<<
+var set = {
+  if (c) ...{1, 2}
+};
+>>> spread list inside if formats like block if it splits
+var set = {if (c) ...{1, 2,}};
+<<<
+var set = {
+  if (c) ...{
+    1,
+    2,
+  }
+};
+>>> both spreads split if then must
+var set = {if (c) ...{1, 2,} else ...{1, 2}};
+<<<
+var set = {
+  if (c) ...{
+    1,
+    2,
+  } else ...{
+    1,
+    2
+  }
+};
+>>> both spreads split if else must
+var set = {if (c) ...{1, 2} else ...{1, 2,}};
+<<<
+var set = {
+  if (c) ...{
+    1,
+    2
+  } else ...{
+    1,
+    2,
+  }
+};
+>>> a split collection that isn't spread wraps and indents
+var set = {if (c) {1,2,}};
+<<<
+var set = {
+  if (c)
+    {
+      1,
+      2,
+    }
+};
+>>> a split collection that isn't spread wraps and indents
+var set = {if (c) {1,2,} else thing};
+<<<
+var set = {
+  if (c)
+    {
+      1,
+      2,
+    }
+  else
+    thing
+};
+>>> a split collection that isn't spread wraps and indents
+var set = {if (c) thing else {1,2,}};
+<<<
+var set = {
+  if (c)
+    thing
+  else
+    {
+      1,
+      2,
+    }
+};
+>>> lambda inside then
+var set = {if (c) () { body; }};
+<<<
+var set = {
+  if (c)
+    () {
+      body;
+    }
+};
+>>> lambda inside else
+var set = {if (c) thing else () { body; }};
+<<<
+var set = {
+  if (c)
+    thing
+  else
+    () {
+      body;
+    }
+};
+>>> nested if doesn't split if it fits
+var set = {if (c) if (d) thing};
+<<<
+var set = {if (c) if (d) thing};
+>>> split collection before nested if
+var set = {if (c) if (d) fairlyLongThingHere};
+<<<
+var set = {
+  if (c) if (d) fairlyLongThingHere
+};
+>>> just split outer if
+var set = {if (condition) if (another) longThingHereThatIsLong};
+<<<
+var set = {
+  if (condition)
+    if (another) longThingHereThatIsLong
+};
+>>> split inside condition
+var set = {if (veryLongCondition + thatNeedsToSplit) thing};
+<<<
+var set = {
+  if (veryLongCondition +
+      thatNeedsToSplit)
+    thing
+};
\ No newline at end of file
diff --git a/test/whitespace/collections.stmt b/test/whitespace/collections.stmt
new file mode 100644
index 0000000..bcf7119
--- /dev/null
+++ b/test/whitespace/collections.stmt
@@ -0,0 +1,59 @@
+40 columns                              |
+>>> empty map literal (dartbug.com/16382)
+var m = { };
+<<<
+var m = {};
+>>>
+var m = {};
+<<<
+var m = {};
+>>> generic map literal
+<   int,int  >{   };
+<<<
+<int, int>{};
+>>> generic set literal
+<   int  >{   };
+<<<
+<int>{};
+>>> list spread
+var list = [  ...  a,...b,  ...
+c];
+<<<
+var list = [...a, ...b, ...c];
+>>> map spread
+var map = {  ...  a,...b,1:2,  ...
+c};
+<<<
+var map = {...a, ...b, 1: 2, ...c};
+>>> set spread
+var set = {  ...  a,...b, 1,   ...
+c};
+<<<
+var set = {...a, ...b, 1, ...c};
+>>> list null aware spread
+var list = [  ...?  a,...?b,  ...?
+c];
+<<<
+var list = [...?a, ...?b, ...?c];
+>>> map null aware spread
+var map = {  ...?  a,...?b,1:2,  ...?
+c};
+<<<
+var map = {...?a, ...?b, 1: 2, ...?c};
+>>> set null aware spread
+var set = {  ...?  a,...?b, 1,   ...?
+c};
+<<<
+var set = {...?a, ...?b, 1, ...?c};
+>>> if
+var list = [   if   ( c    )   1    ,];
+<<<
+var list = [
+  if (c) 1,
+];
+>>> if else
+var list = [   if   ( c    )   1    else    2   ,];
+<<<
+var list = [
+  if (c) 1 else 2,
+];
\ No newline at end of file
diff --git a/test/whitespace/expressions.stmt b/test/whitespace/expressions.stmt
index c1ed6b2..10980d5 100644
--- a/test/whitespace/expressions.stmt
+++ b/test/whitespace/expressions.stmt
@@ -32,22 +32,6 @@
     y;
 <<<
 x && y;
->>> empty map literal (dartbug.com/16382)
-var m = { };
-<<<
-var m = {};
->>>
-var m = {};
-<<<
-var m = {};
->>> generic map literal
-<   int,int  >{   };
-<<<
-<int, int>{};
->>> generic set literal
-<   int  >{   };
-<<<
-<int>{};
 >>> unqualified symbol
 var x = #foo;
 <<<