Indent the parameter list more if the body is a wrapped "=>". Fix #144.

BUG=https://github.com/dart-lang/dart_style/issues/144
R=pquitslund@google.com

Review URL: https://chromiumcodereview.appspot.com//889943004
diff --git a/CHANGELOG.md b/CHANGELOG.md
index edb23db..8852031 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
   list is split across multiple lines (#151).
 * Allow splitting in index operator calls (#140).
 * Handle sync* and async* syntax (#151).
+* Indent the parameter list more if the body is a wrapped "=>" (#144).
 
 # 0.1.2
 
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index ad089ff..a741e7c 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -460,7 +460,6 @@
   visitConstructorDeclaration(ConstructorDeclaration node) {
     visitMemberMetadata(node.metadata);
 
-    _writer.nestExpression();
     modifier(node.externalKeyword);
     modifier(node.constKeyword);
     modifier(node.factoryKeyword);
@@ -473,18 +472,14 @@
     // line that the initialization list gets pushed to its own line too.
     if (node.initializers.length == 1) _writer.startMultisplit();
 
-    visit(node.parameters);
-
-    // Check for redirects or initializer lists.
-    if (node.redirectedConstructor != null) {
-      _visitConstructorRedirects(node);
-    } else if (node.initializers.isNotEmpty) {
-      _visitConstructorInitializers(node);
-    }
-
-    _writer.unnest();
-
-    visitBody(node.body);
+    _visitBody(node.parameters, node.body, () {
+      // Check for redirects or initializer lists.
+      if (node.redirectedConstructor != null) {
+        _visitConstructorRedirects(node);
+      } else if (node.initializers.isNotEmpty) {
+        _visitConstructorInitializers(node);
+      }
+    });
   }
 
   void _visitConstructorInitializers(ConstructorDeclaration node) {
@@ -714,6 +709,7 @@
   }
 
   visitFormalParameterList(FormalParameterList node) {
+    _writer.nestExpression();
     token(node.leftParenthesis);
 
     // Allow splitting after the "(" in non-empty parameter lists, but not for
@@ -758,6 +754,7 @@
     token(node.rightDelimiter);
 
     token(node.rightParenthesis);
+    _writer.unnest();
     _writer.endSpan();
   }
 
@@ -829,8 +826,7 @@
   }
 
   visitFunctionExpression(FunctionExpression node) {
-    visit(node.parameters);
-    visitBody(node.body);
+    _visitBody(node.parameters, node.body);
   }
 
   visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
@@ -1009,18 +1005,14 @@
   visitMethodDeclaration(MethodDeclaration node) {
     visitMemberMetadata(node.metadata);
 
-    _writer.nestExpression();
     modifier(node.externalKeyword);
     modifier(node.modifierKeyword);
     visit(node.returnType, after: space);
     modifier(node.propertyKeyword);
     modifier(node.operatorKeyword);
     visit(node.name);
-    if (!node.isGetter) visit(node.parameters);
 
-    _writer.unnest();
-
-    visitBody(node.body);
+    _visitBody(node.parameters, node.body);
   }
 
   visitMethodInvocation(MethodInvocation node) {
@@ -1485,9 +1477,29 @@
     visitNodes(metadata, between: space, after: space);
   }
 
-  /// Visit the given function [body], printing a space before it if it's not
-  /// empty.
-  void visitBody(FunctionBody body) {
+  /// Visit the given function [parameters] followed by its [body], printing a
+  /// space before it if it's not empty.
+  ///
+  /// If [afterParameters] is provided, it is invoked between the parameters
+  /// and body. (It's used for constructor initialization lists.)
+  void _visitBody(FormalParameterList parameters, FunctionBody body,
+      [afterParameters()]) {
+    if (parameters != null) {
+      // If the body is "=>", add an extra level of indentation around the
+      // parameters. This ensures that if they wrap, they wrap more deeply than
+      // the "=>" does, as in:
+      //
+      //     someFunction(parameter,
+      //             parameter, parameter) =>
+      //         "the body";
+      if (body is ExpressionFunctionBody) _writer.nestExpression();
+
+      visit(parameters);
+      if (afterParameters != null) afterParameters();
+
+      if (body is ExpressionFunctionBody) _writer.unnest();
+    }
+
     if (body is! EmptyFunctionBody) space();
     visit(body);
   }
diff --git a/test/regression/144.unit b/test/regression/144.unit
new file mode 100644
index 0000000..8880048
--- /dev/null
+++ b/test/regression/144.unit
@@ -0,0 +1,26 @@
+>>>
+class Selector {
+  factory Selector.call(String name,
+                        LibraryElement library,
+                        int arity,
+                        [List<String> namedArguments])
+      => new Selector(SelectorKind.CALL, name, library, arity, namedArguments);
+}
+<<<
+class Selector {
+  factory Selector.call(String name, LibraryElement library, int arity,
+          [List<String> namedArguments]) =>
+      new Selector(SelectorKind.CALL, name, library, arity, namedArguments);
+}
+>>> a knock-on issue caused by the initial fix for the above
+class Foo {
+  get getter => "result";
+
+    // Comment.
+}
+<<<
+class Foo {
+  get getter => "result";
+
+  // Comment.
+}
\ No newline at end of file
diff --git a/test/splitting/constructors.unit b/test/splitting/constructors.unit
index 0d7e474..99a932e 100644
--- a/test/splitting/constructors.unit
+++ b/test/splitting/constructors.unit
@@ -47,4 +47,14 @@
   Foo(int longArg1, int longArg2,
       int longArg3)
       : this._(longArg1);
-}
\ No newline at end of file
+}
+>>> indent parameters more if body is a wrapped =>
+class Foo {
+  Foo(firstArgument, secondArgument, third) => "very long body that must wrap";
+}
+<<<
+class Foo {
+  Foo(firstArgument, secondArgument,
+          third) =>
+      "very long body that must wrap";
+}
diff --git a/test/splitting/parameters.unit b/test/splitting/parameters.unit
index 986eaa1..56d43f2 100644
--- a/test/splitting/parameters.unit
+++ b/test/splitting/parameters.unit
@@ -8,3 +8,9 @@
   Foo(this.first, this.second,
       this.third, this.fourth);
 }
+>>> indent parameters more if body is a wrapped =>
+method(int firstArgument, int argumentTwo) => "very long body that must wrap";
+<<<
+method(int firstArgument,
+        int argumentTwo) =>
+    "very long body that must wrap";
\ No newline at end of file