Merge branch 'chalin-max-sdk-to-3-0714' of https://github.com/chalin/dart_style into chalin-chalin-max-sdk-to-3-0714

# Conflicts:
#	CHANGELOG.md
#	analysis_options.yaml
#	pubspec.lock
#	pubspec.yaml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6a1e24..63bbed8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # 1.1.3
 
+* Preserve whitespace in multi-line strings inside string interpolations (#711).
+  **Note!** This bug means that dart_style 1.1.2 may make semantics changes to
+  your strings. You should avoid that version and use 1.1.3.
+
 * Set max SDK version to <3.0.0, and adjusted other dependencies.
 
 # 1.1.2
diff --git a/README.md b/README.md
index 3746768..a0d4400 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,10 @@
 
 [dart style guide]: https://www.dartlang.org/guides/language/effective-dart/style
 
-The formatter handles indentation, inline whitespace, and
-(by far the most difficult) intelligent line wrapping.
-It has no problems with nested collections, function
-expressions, long argument lists, or otherwise tricky code.
+The formatter handles indentation, inline whitespace, and (by far the most
+difficult) intelligent line wrapping. It has no problems with nested
+collections, function expressions, long argument lists, or otherwise tricky
+code.
 
 The formatter turns code like this:
 
@@ -36,8 +36,8 @@
 
 The formatter can also apply non-whitespace changes to make your code
 consistently idiomatic. You must opt into these by passing either `--fix` which
-applies all style fixes, or any of the `--fix-`-prefixed flags to apply
-specific fixes.
+applies all style fixes, or any of the `--fix-`-prefixed flags to apply specific
+fixes.
 
 For example, running with `--fix-named-default-separator` changes this:
 
@@ -57,19 +57,23 @@
 
 ## Getting dartfmt
 
-Dartfmt is included in the Dart SDK, so you might want to add the SDK's bin
-directory to your system path.
+Dartfmt is included in the Dart SDK, so most users get it directly from there.
+That has the latest version of dartfmt that was available when the SDK was
+released.
 
-If you want to make sure you are running the latest version of dartfmt,
-you can [globally activate][] the package from the dart_style package
-on pub.dartlang.org, and let pub put its executable on your path:
+If you want to make sure you are running the latest version of dartfmt, you can
+[globally activate][] the package from the dart_style package on
+pub.dartlang.org:
 
     $ pub global activate dart_style
     $ dartfmt ...
 
+For this to work, you need to put pub's bin directory on your PATH before the
+Dart SDL directory. Otherwise, the SDK's dartfmt will shadow this one.
+
 [globally activate]: https://www.dartlang.org/tools/pub/cmd/pub-global.html
 
-If you don't want `dartfmt` on your path, you can run it explicitly:
+If you don't want pub to put `dartfmt` on your PATH, you can run it explicitly:
 
     $ pub global activate dart_style --no-executables
     $ pub global run dart_style:format ...
@@ -77,28 +81,27 @@
 ## Using dartfmt
 
 IDEs and editors that support Dart usually provide easy ways to run the
-formatter. For example, in WebStorm you can right-click a .dart file
-and then choose **Reformat with Dart Style**.
+formatter. For example, in WebStorm you can right-click a .dart file and then
+choose **Reformat with Dart Style**.
 
 Here's a simple example of using dartfmt on the command line:
 
     $ dartfmt test.dart
 
-This command formats the `test.dart` file and writes the result to
-standard output.
+This command formats the `test.dart` file and writes the result to standard
+output.
 
-Dartfmt takes a list of paths, which can point to directories or files.
-If the path is a directory, it processes every `.dart` file in that directory
-or any of its subdirectories.
-If no file or directory is specified, dartfmt reads from standard input.
+Dartfmt takes a list of paths, which can point to directories or files. If the
+path is a directory, it processes every `.dart` file in that directory or any of
+its subdirectories. If no file or directory is specified, dartfmt reads from
+standard input.
 
 By default, it formats each file and just prints the resulting code to stdout.
-If you pass `-w`, it overwrites your existing files with the
-formatted results.
+If you pass `-w`, it overwrites your existing files with the formatted results.
 
-You may pass a `-l` option to control the width of the page that it
-wraps lines to fit within, but you're strongly encouraged to keep the default
-line length of 80 columns.
+You may pass a `-l` option to control the width of the page that it wraps lines
+to fit within, but you're strongly encouraged to keep the default line length of
+80 columns.
 
 ### Validating files
 
@@ -115,6 +118,7 @@
 
 The package also exposes a single dart_style library containing a programmatic
 API for formatting code. Simple usage looks like this:
+
 ```dart
 import 'package:dart_style/dart_style.dart';
 
@@ -134,6 +138,7 @@
   }
 }
 ```
+
 ## Other resources
 
 * Before sending an email, see if you are asking a
diff --git a/analysis_options.yaml b/analysis_options.yaml
index f0079f0..e4b1b8f 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,5 +1,3 @@
-analyzer:
-
 linter:
   rules:
     - unawaited_futures
diff --git a/bin/format.dart b/bin/format.dart
index e0de12d..45756a3 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.2";
+const version = "1.1.3";
 
 void main(List<String> args) {
   var parser = new ArgParser(allowTrailingOptions: true);
diff --git a/lib/src/chunk_builder.dart b/lib/src/chunk_builder.dart
index 4bdd166..a192f21 100644
--- a/lib/src/chunk_builder.dart
+++ b/lib/src/chunk_builder.dart
@@ -171,9 +171,20 @@
   /// If [nest] is `false`, ignores any current expression nesting. Otherwise,
   /// uses the current nesting level. If unsplit, it expands to a space if
   /// [space] is `true`.
-  Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) =>
-      _writeSplit(_rules.last,
-          flushLeft: flushLeft, isDouble: isDouble, nest: nest, space: space);
+  Chunk split({bool flushLeft, bool isDouble, bool nest, bool space}) {
+    space ??= false;
+
+    // If we are not allowed to split at all, don't. Returning null for the
+    // chunk is safe since the rule that uses the chunk will itself get
+    // discarded because no chunk references it.
+    if (_preventSplitNesting > 0) {
+      if (space) _pendingWhitespace = Whitespace.space;
+      return null;
+    }
+
+    return _writeSplit(_rules.last,
+        flushLeft: flushLeft, isDouble: isDouble, nest: nest, space: space);
+  }
 
   /// Outputs the series of [comments] and associated whitespace that appear
   /// before [token] (which is not written by this).
@@ -512,10 +523,6 @@
   ///
   /// Nested blocks are handled using their own independent [LineWriter].
   ChunkBuilder startBlock(Chunk argumentChunk) {
-    // If we are not allowed to split at all, don't create a new block. Instead,
-    // the block contents will end up in the current chunk.
-    if (_preventSplitNesting > 0) return this;
-
     var chunk = _chunks.last;
     chunk.makeBlock(argumentChunk);
 
@@ -538,11 +545,6 @@
   ///
   /// Returns the previous writer for the surrounding block.
   ChunkBuilder endBlock(Rule ignoredSplit, {bool forceSplit}) {
-    // If we are not allowed to split at all, we didn't create a new block and
-    // thus didn't create a new ChunkBuilder, so there is no builder to pop.
-    // We are still in the current one.
-    if (_preventSplitNesting > 0) return this;
-
     _divideChunks();
 
     // If we don't already know if the block is going to split, see if it
@@ -767,21 +769,6 @@
   /// to be at column zero. Otherwise, it uses the normal indentation and
   /// nesting behavior.
   void _writeHardSplit({bool isDouble, bool flushLeft, bool nest: false}) {
-    // If we are not allowed to split at all, simply write a space. Instead of:
-    //
-    //     foo("${() {
-    //       a;
-    //       b;
-    //     }}");
-    //
-    // produces:
-    //
-    //     foo("${() { a; b; }}");
-    if (_preventSplitNesting > 0) {
-      _writeText(" ");
-      return;
-    }
-
     // A hard split overrides any other whitespace.
     _pendingWhitespace = null;
     _writeSplit(new Rule.hard(),
@@ -796,14 +783,6 @@
     nest ??= true;
     space ??= false;
 
-    // If we are not allowed to split at all, don't. Returning null for the
-    // chunk is safe since the rule that uses the chunk will itself get
-    // discarded because no chunk references it.
-    if (_preventSplitNesting > 0) {
-      if (space) write(" ");
-      return null;
-    }
-
     if (_chunks.isEmpty) {
       if (flushLeft != null) _firstFlushLeft = flushLeft;
 
diff --git a/pubspec.lock b/pubspec.lock
index 01f0f31..9c24470 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -49,7 +49,7 @@
       name: cli_util
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.1.3"
+    version: "0.1.3+2"
   collection:
     dependency: transitive
     description:
@@ -77,7 +77,7 @@
       name: csslib
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.14.4"
+    version: "0.14.4+1"
   front_end:
     dependency: transitive
     description:
@@ -168,7 +168,7 @@
       name: matcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.3"
+    version: "0.12.3+1"
   meta:
     dependency: transitive
     description:
@@ -245,7 +245,7 @@
       name: shelf
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.7.3+2"
+    version: "0.7.3+3"
   shelf_packages_handler:
     dependency: transitive
     description:
@@ -322,7 +322,7 @@
       name: test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0"
+    version: "1.3.0"
   test_descriptor:
     dependency: "direct dev"
     description:
@@ -350,7 +350,7 @@
       name: utf
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.0+4"
+    version: "0.9.0+5"
   vm_service_client:
     dependency: transitive
     description:
@@ -380,4 +380,4 @@
     source: hosted
     version: "2.1.14"
 sdks:
-  dart: ">=2.0.0-dev.62.0 <=2.0.0-dev.68.0"
+  dart: ">=2.0.0-dev.62.0 <=2.0.0-edge.69a15d20069891ed13e7c65a6810527b4a9f9e40"
diff --git a/pubspec.yaml b/pubspec.yaml
index e67dc0d..476b11c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,9 +1,8 @@
 name: dart_style
 # Note: See tool/grind.dart for how to bump the version.
 version: 1.1.3
-
-description: Opinionated, automatic Dart source code formatter.
 author: Dart Team <misc@dartlang.org>
+description: Opinionated, automatic Dart source code formatter.
 homepage: https://github.com/dart-lang/dart_style
 
 environment:
diff --git a/test/regression/0700/0711.stmt b/test/regression/0700/0711.stmt
new file mode 100644
index 0000000..8fd2179
--- /dev/null
+++ b/test/regression/0700/0711.stmt
@@ -0,0 +1,12 @@
+>>>
+var str = '''${"""
+a
+b
+c
+"""}''';
+<<<
+var str = '''${"""
+a
+b
+c
+"""}''';
\ No newline at end of file
diff --git a/test/splitting/strings.stmt b/test/splitting/strings.stmt
index d3bdaab..5321b70 100644
--- a/test/splitting/strings.stmt
+++ b/test/splitting/strings.stmt
@@ -161,8 +161,84 @@
 someMethod(
     "some text that is ${pretty + 'long ${interpolate + a + thing} more'} text",
     "another arg");
->>> hard splits are not split in interpolation
+>>> hard splits are split in interpolation
 someMethod("before ${(){statement();statement();statement();}} after");
 <<<
-someMethod(
-    "before ${() { statement(); statement(); statement(); }} after");
\ No newline at end of file
+someMethod("before ${() {
+  statement();
+  statement();
+  statement();
+}} after");
+>>> collections split in interpolation if needed
+method(
+"b ${[1, 2, 3, 4, 5, 6]} a",
+"before ${[first, second, third, fourth, {fifth: sixth}]} after");
+<<<
+method(
+    "b ${[1, 2, 3, 4, 5, 6]} a",
+    "before ${[
+      first,
+      second,
+      third,
+      fourth,
+      {fifth: sixth}
+    ]} after");
+>>> nested multiline strings are not merged in interpolation
+"before ${"""a
+b"""} ${aft
++
+er}";
+<<<
+"before ${"""a
+b"""} ${aft + er}";
+>>> multiply-nested interpolation
+'''a
+${b +
+"""c
+${d
++ '''e
+f'''
++
+g}
+h"""
++ i}
+j ${k
++
+l}''';
+<<<
+'''a
+${b + """c
+${d + '''e
+f''' + g}
+h""" + i}
+j ${k + l}''';
+>>> nested interpolation inside function
+"before ${ () { a(); """b
+c"""; d(); }} after";
+<<<
+"before ${() {
+  a();
+  """b
+c""";
+  d();
+}} after";
+>>> comment inside interpolation
+"before ${// comment
+a
++
+b
++  // another
+c} after";
+<<<
+"before ${ // comment
+    a + b + // another
+        c} after";
+>>>
+function(
+  "long string long string ${interpolated + interpolated} long string",
+  longLongLongLongObject.longLongLongLongMethod());
+<<<
+function(
+    "long string long string ${interpolated + interpolated} long string",
+    longLongLongLongObject
+        .longLongLongLongMethod());
\ No newline at end of file
diff --git a/test/utils.dart b/test/utils.dart
index 74fa489..0d5f889 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -62,6 +62,8 @@
 
   var entries = new Directory(p.join(testDir, name))
       .listSync(recursive: true, followLinks: false);
+  entries.sort((a, b) => a.path.compareTo(b.path));
+
   for (var entry in entries) {
     if (!entry.path.endsWith(".stmt") && !entry.path.endsWith(".unit")) {
       continue;
diff --git a/test/whitespace/strings.stmt b/test/whitespace/strings.stmt
new file mode 100644
index 0000000..b44f8bf
--- /dev/null
+++ b/test/whitespace/strings.stmt
@@ -0,0 +1,9 @@
+40 columns                              |
+>>>
+"a"     "b" "c";
+<<<
+"a" "b" "c";
+>>> empty multi-line
+"""""" '''''';
+<<<
+"""""" '''''';
\ No newline at end of file