Reformat expressions inside interpolated expressions. (#708)

* Reformat expressions inside interpolated expressions.

If it ever ends up splitting inside an interpolation, it looks pretty
gnarly, but that's mostly a signal to the user to split up the string.
It does nicely clean up the whitespace.

More importantly, this means fixes apply inside interpolation too.

Fix #226. Fix #707.

* Add test for fixing "new" inside string interpolation.

Also bump version before publishing.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cdfd1d0..b2aa4ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.1.1
+
+* Format expressions in string interpolations (#226).
+* Apply fixes inside string interpolations (#707).
+
 # 1.1.0
 
 * Add support for "style fixes", opt-in non-whitespace changes.
diff --git a/bin/format.dart b/bin/format.dart
index 999a9d8..23d0979 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -15,7 +15,7 @@
 import 'package:dart_style/src/style_fix.dart';
 
 // Note: The following line of code is modified by tool/grind.dart.
-const version = "1.1.0";
+const version = "1.1.1";
 
 void main(List<String> args) {
   var parser = new ArgParser(allowTrailingOptions: true);
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index b4a5ca6..a05f5c8 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -1560,12 +1560,14 @@
 
   visitInterpolationExpression(InterpolationExpression node) {
     token(node.leftBracket);
+    builder.startSpan();
     visit(node.expression);
+    builder.endSpan();
     token(node.rightBracket);
   }
 
   visitInterpolationString(InterpolationString node) {
-    token(node.contents);
+    _writeStringLiteral(node.contents);
   }
 
   visitIsExpression(IsExpression node) {
@@ -1834,24 +1836,13 @@
   }
 
   visitSimpleStringLiteral(SimpleStringLiteral node) {
-    // Since we output the string literal manually, ensure any preceding
-    // comments are written first.
-    writePrecedingCommentsAndNewlines(node.literal);
-
-    _writeStringLiteral(node.literal.lexeme, node.offset);
+    _writeStringLiteral(node.literal);
   }
 
   visitStringInterpolation(StringInterpolation node) {
-    // Since we output the interpolated text manually, ensure we include any
-    // preceding stuff first.
-    writePrecedingCommentsAndNewlines(node.beginToken);
-
-    // Right now, the formatter does not try to do any reformatting of the
-    // contents of interpolated strings. Instead, it treats the entire thing as
-    // a single (possibly multi-line) chunk of text.
-    _writeStringLiteral(
-        _source.text.substring(node.beginToken.offset, node.endToken.end),
-        node.offset);
+    for (var element in node.elements) {
+      visit(element);
+    }
   }
 
   visitSuperConstructorInvocation(SuperConstructorInvocation node) {
@@ -2762,9 +2753,14 @@
   ///
   /// Splits multiline strings into separate chunks so that the line splitter
   /// can handle them correctly.
-  void _writeStringLiteral(String string, int offset) {
+  void _writeStringLiteral(Token string) {
+    // Since we output the string literal manually, ensure any preceding
+    // comments are written first.
+    writePrecedingCommentsAndNewlines(string);
+
     // Split each line of a multiline string into separate chunks.
-    var lines = string.split(_formatter.lineEnding);
+    var lines = string.lexeme.split(_formatter.lineEnding);
+    var offset = string.offset;
 
     _writeText(lines.first, offset);
     offset += lines.first.length;
diff --git a/pubspec.lock b/pubspec.lock
index 94a5e50..54d2a9a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -366,4 +366,4 @@
     source: hosted
     version: "2.1.14"
 sdks:
-  dart: ">=2.0.0-dev.62.0 <=2.0.0-edge.83cb0c425dde6023625b9f9816136338f5df1b16"
+  dart: ">=2.0.0-dev.62.0 <=2.0.0-edge.edf26852e6f6f096bb935ab8a545ce0646cf05a6"
diff --git a/pubspec.yaml b/pubspec.yaml
index 6c2994c..0810110 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: dart_style
 # Note: See tool/grind.dart for how to bump the version.
-version: 1.1.0
+version: 1.1.1
 author: Dart Team <misc@dartlang.org>
 description: Opinionated, automatic Dart source code formatter.
 homepage: https://github.com/dart-lang/dart_style
diff --git a/test/fixes/optional_new.stmt b/test/fixes/optional_new.stmt
index 6905b3f..20e7199 100644
--- a/test/fixes/optional_new.stmt
+++ b/test/fixes/optional_new.stmt
@@ -64,4 +64,8 @@
 >>> handle already-removed keyword
 A<int>(A<int>.named());
 <<<
-A<int>(A<int>.named());
\ No newline at end of file
+A<int>(A<int>.named());
+>>> inside string interpolation
+"before ${new Foo()} after";
+<<<
+"before ${Foo()} after";
\ No newline at end of file
diff --git a/test/regression/0000/0057.stmt b/test/regression/0000/0057.stmt
index 282a5c7..be00d6b 100644
--- a/test/regression/0000/0057.stmt
+++ b/test/regression/0000/0057.stmt
@@ -6,7 +6,7 @@
 <<<
 embeddedPlural2(n) => Intl.message(
     "${Intl.plural(n,
-zero: 'none', one: 'one', other: 'some')} plus some text.",
+        zero: 'none', one: 'one', other: 'some')} plus some text.",
     name: 'embeddedPlural2',
     desc: 'An embedded plural',
     args: [n]);
\ No newline at end of file
diff --git a/test/regression/0100/0189.stmt b/test/regression/0100/0189.stmt
index b4235df..4acf089 100644
--- a/test/regression/0100/0189.stmt
+++ b/test/regression/0100/0189.stmt
@@ -11,7 +11,8 @@
   nestedSelect(currency, amount) => Intl.select(
       currency,
       {
-        "CDN": """${Intl.plural(amount, one: '$amount Canadian dollar',
+        "CDN": """${Intl.plural(amount,
+            one: '$amount Canadian dollar',
             other: '$amount Canadian dollars')}""",
         "other": "Whatever",
       },
diff --git a/test/regression/0200/0226.stmt b/test/regression/0200/0226.stmt
new file mode 100644
index 0000000..d38cead
--- /dev/null
+++ b/test/regression/0200/0226.stmt
@@ -0,0 +1,4 @@
+>>>
+test('Bad Request${(detail!='' ? ': ' : '')}$detail');
+<<<
+test('Bad Request${(detail != '' ? ': ' : '')}$detail');
\ No newline at end of file
diff --git a/test/regression/0400/0467.unit b/test/regression/0400/0467.unit
index 57df4c3..680067c 100644
--- a/test/regression/0400/0467.unit
+++ b/test/regression/0400/0467.unit
@@ -21,6 +21,8 @@
         'nodesAndEntryPoints',
         new PolymerDom(this).children.map((child) =>
             '${child.outerHtml} ------> '
-            '${(new PolymerDom(child).getDestinationInsertionPoints()[0] as Element).outerHtml}'));
+            '${(new PolymerDom(child).getDestinationInsertionPoints()[0]
+                    as Element)
+                .outerHtml}'));
   }
 }
\ No newline at end of file
diff --git a/test/regression/0500/0500.unit b/test/regression/0500/0500.unit
index 4e1a62f..82988db 100644
--- a/test/regression/0500/0500.unit
+++ b/test/regression/0500/0500.unit
@@ -26,9 +26,10 @@
         for (int i = 0; i < names.length; ++i) {
           env.initialize(names[i],
               getter: (TopLevelBinding binding, ExprCont ek, ExprCont k) {
-                binding.getter = (TopLevelBinding _, ExprCont ek0,
-                        ExprCont k0) =>
-                    ek0("Reading static variable '${binding.name}' during its initialization");
+                binding.getter =
+                    (TopLevelBinding _, ExprCont ek0, ExprCont k0) => ek0(
+                        "Reading static variable '${binding
+                            .name}' during its initialization");
                 initializers[i](env, ek, (v) {
                   binding.getter =
                       (TopLevelBinding _, ExprCont ek1, ExprCont k1) => k1(v);
diff --git a/test/regression/0700/0707.stmt b/test/regression/0700/0707.stmt
new file mode 100644
index 0000000..c9f464a
--- /dev/null
+++ b/test/regression/0700/0707.stmt
@@ -0,0 +1,4 @@
+>>> (indent 4) (fix optional-new)
+    sink.write('FILE ACCESSED ${new DateTime.now()}\n');
+<<<
+    sink.write('FILE ACCESSED ${DateTime.now()}\n');
\ No newline at end of file
diff --git a/test/regression/other/analysis_server.unit b/test/regression/other/analysis_server.unit
index 0c03cfa..8dc5f80 100644
--- a/test/regression/other/analysis_server.unit
+++ b/test/regression/other/analysis_server.unit
@@ -258,4 +258,24 @@
         .variables
         .variables[0]
         .name
-        .staticElement;
\ No newline at end of file
+        .staticElement;
+>>>
+main() {
+  addTestFile('''
+    library libA;
+    part "${convertPathForImport('/testA.dart')}";
+    import "dart:math";
+    /// The [^]
+    main(aaa, bbb) {}
+    ''');
+}
+<<<
+main() {
+  addTestFile('''
+    library libA;
+    part "${convertPathForImport('/testA.dart')}";
+    import "dart:math";
+    /// The [^]
+    main(aaa, bbb) {}
+    ''');
+}
\ No newline at end of file
diff --git a/test/splitting/strings.stmt b/test/splitting/strings.stmt
index fc262fe..f6d5529 100644
--- a/test/splitting/strings.stmt
+++ b/test/splitting/strings.stmt
@@ -138,3 +138,17 @@
   "many",
   "elements"
 ]);
+>>> interpolation in multi-line does not force unneeded splits
+someMethod("foo", """
+  some text that is pretty long
+  some more text that is pretty long
+  ${    inpolate + a + thing   }
+  more text
+""");
+<<<
+someMethod("foo", """
+  some text that is pretty long
+  some more text that is pretty long
+  ${inpolate + a + thing}
+  more text
+""");
\ No newline at end of file
diff --git a/test/utils.dart b/test/utils.dart
index 6b7a474..74fa489 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -18,7 +18,8 @@
 const unformattedSource = 'void  main()  =>  print("hello") ;';
 const formattedSource = 'void main() => print("hello");\n';
 
-final _indentPattern = new RegExp(r"^\(indent (\d+)\)\s*");
+final _indentPattern = new RegExp(r"\(indent (\d+)\)");
+final _fixPattern = new RegExp(r"\(fix ([a-x-]+)\)");
 
 /// Runs the command line formatter, passing it [args].
 Future<TestProcess> runFormatter([List<String> args]) {
@@ -36,10 +37,7 @@
   args.insert(0, formatterPath);
 
   // Use the same package root, if there is one.
-  if (Platform.packageRoot != null && Platform.packageRoot.isNotEmpty) {
-    args.insert(0, "--package-root=${Platform.packageRoot}");
-  } else if (Platform.packageConfig != null &&
-      Platform.packageConfig.isNotEmpty) {
+  if (Platform.packageConfig != null && Platform.packageConfig.isNotEmpty) {
     args.insert(0, "--packages=${Platform.packageConfig}");
   }
 
@@ -84,7 +82,10 @@
   _testFile(p.dirname(path), p.join(testDir, path), fixes);
 }
 
-void _testFile(String name, String path, Iterable<StyleFix> fixes) {
+void _testFile(String name, String path, Iterable<StyleFix> baseFixes) {
+  var fixes = <StyleFix>[];
+  if (baseFixes != null) fixes.addAll(baseFixes);
+
   group("$name ${p.basename(path)}", () {
     // Explicitly create a File, in case the entry is a Link.
     var lines = new File(path).readAsLinesSync();
@@ -98,16 +99,23 @@
 
     var i = 0;
     while (i < lines.length) {
-      var description = lines[i++].replaceAll(">>>", "").trim();
+      var description = lines[i++].replaceAll(">>>", "");
 
       // Let the test specify a leading indentation. This is handy for
       // regression tests which often come from a chunk of nested code.
       var leadingIndent = 0;
-      var indentMatch = _indentPattern.firstMatch(description);
-      if (indentMatch != null) {
-        leadingIndent = int.parse(indentMatch[1]);
-        description = description.substring(indentMatch.end);
-      }
+      description = description.replaceAllMapped(_indentPattern, (match) {
+        leadingIndent = int.parse(match[1]);
+        return "";
+      });
+
+      // Let the test specify fixes to apply.
+      description = description.replaceAllMapped(_fixPattern, (match) {
+        fixes.add(StyleFix.all.firstWhere((fix) => fix.name == match[1]));
+        return "";
+      });
+
+      description = description.trim();
 
       if (description == "") {
         description = "line ${i + 1}";
diff --git a/test/whitespace/expressions.stmt b/test/whitespace/expressions.stmt
index d67bd89..4cf871a 100644
--- a/test/whitespace/expressions.stmt
+++ b/test/whitespace/expressions.stmt
@@ -142,4 +142,8 @@
 >>> generic method call
 method   <int,String   ,    bool>  ();
 <<<
-method<int, String, bool>();
\ No newline at end of file
+method<int, String, bool>();
+>>> inside interpolation
+" ${   interp+olate } and ${fn  (  1 ) } end";
+<<<
+" ${interp + olate} and ${fn(1)} end";
\ No newline at end of file