Don't force a split before "." on parenthesized targets.

The method chain splitting logic allows "." to immediately follow some
targets, like collection literals:

    [
      element,
    ].forEach((_) => ...);

This extends that to allow parenthesized expressions:

    ([
      element,
    ]).forEach((_) => ...);

Fix #704.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4234f1b..4a2122e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 * Format the `late` modifier.
 * Format the `required` modifier.
 * Better formatting of empty spread collections (#831).
+* Don't force split before `.` when the target is parenthesized (#704).
 
 # 1.2.10
 
diff --git a/lib/src/call_chain_visitor.dart b/lib/src/call_chain_visitor.dart
index 9b6b50b..ce40e72 100644
--- a/lib/src/call_chain_visitor.dart
+++ b/lib/src/call_chain_visitor.dart
@@ -284,10 +284,14 @@
   bool _forcesSplit(Expression expression) {
     // TODO(rnystrom): Other cases we may want to consider handling and
     // recursing into:
-    // * ParenthesizedExpression.
     // * The right operand in an infix operator call.
     // * The body of a `=>` function.
 
+    // Unwrap parentheses.
+    while (expression is ParenthesizedExpression) {
+      expression = (expression as ParenthesizedExpression).expression;
+    }
+
     // Don't split right after a collection literal.
     if (expression is ListLiteral) return false;
     if (expression is SetOrMapLiteral) return false;
diff --git a/test/regression/0700/0704.unit b/test/regression/0700/0704.unit
new file mode 100644
index 0000000..0fd1b8b
--- /dev/null
+++ b/test/regression/0700/0704.unit
@@ -0,0 +1,37 @@
+>>>
+void main() {
+  test("synchronized files are up-to-date", () {
+    ({
+      'lib/src/visitor/async_evaluate.dart': 'lib/src/visitor/evaluate.dart',
+      'lib/src/async_environment.dart': 'lib/src/environment.dart'
+    })
+        .forEach((sourcePath, targetPath) {
+      var source = new File(sourcePath).readAsStringSync();
+      var target = new File(targetPath).readAsStringSync();
+
+      var hash = sha1.convert(utf8.encode(source));
+      if (!target.contains("Checksum: $hash")) {
+        fail("$targetPath is out-of-date.\n"
+            "Run pub run grinder to update it.");
+      }
+    });
+  });
+}
+<<<
+void main() {
+  test("synchronized files are up-to-date", () {
+    ({
+      'lib/src/visitor/async_evaluate.dart': 'lib/src/visitor/evaluate.dart',
+      'lib/src/async_environment.dart': 'lib/src/environment.dart'
+    }).forEach((sourcePath, targetPath) {
+      var source = new File(sourcePath).readAsStringSync();
+      var target = new File(targetPath).readAsStringSync();
+
+      var hash = sha1.convert(utf8.encode(source));
+      if (!target.contains("Checksum: $hash")) {
+        fail("$targetPath is out-of-date.\n"
+            "Run pub run grinder to update it.");
+      }
+    });
+  });
+}
\ No newline at end of file
diff --git a/test/splitting/invocations.stmt b/test/splitting/invocations.stmt
index e034d46..702726e 100644
--- a/test/splitting/invocations.stmt
+++ b/test/splitting/invocations.stmt
@@ -280,6 +280,12 @@
 (function)(() {
   ;
 }).someLongMethod();
+>>> do not split on "." when target is parenthesized unsplittable
+((([element,]))).someLongMethod();
+<<<
+((([
+  element,
+]))).someLongMethod();
 >>> trailing comma argument list does not force trailing method chain to split
 function(argument,).method().method();
 <<<