Support generic method parameters.
Previous support only covered arguments. :(
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b595ef3..8dde679 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.13
+
+* Support generic method *parameters* as well as arguments.
+
# 0.2.12
* Add support for assert() in constructor initializers.
diff --git a/bin/format.dart b/bin/format.dart
index 1dcad32..3e89f78 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -14,7 +14,7 @@
import 'package:dart_style/src/source_code.dart';
// Note: The following line of code is modified by tool/grind.dart.
-const version = "0.2.12";
+const version = "0.2.13";
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 2d2c257..1b450b6 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -631,7 +631,7 @@
// the parameter list gets more deeply indented.
if (node.redirectedConstructor != null) builder.nestExpression();
- _visitBody(node.parameters, node.body, () {
+ _visitBody(null, node.parameters, node.body, () {
// Check for redirects or initializer lists.
if (node.redirectedConstructor != null) {
_visitConstructorRedirects(node);
@@ -895,7 +895,8 @@
_visitLoopBody(node.body);
}
- visitFormalParameterList(FormalParameterList node) {
+ visitFormalParameterList(FormalParameterList node,
+ {bool nestExpression: true}) {
// Corner case: empty parameter lists.
if (node.parameters.isEmpty) {
token(node.leftParenthesis);
@@ -922,7 +923,7 @@
.where((param) => param is DefaultFormalParameter)
.toList();
- builder.nestExpression();
+ if (nestExpression) builder.nestExpression();
token(node.leftParenthesis);
_metadataRules.add(new MetadataRule());
@@ -997,7 +998,7 @@
_metadataRules.removeLast();
token(node.rightParenthesis);
- builder.unnest();
+ if (nestExpression) builder.unnest();
}
visitForStatement(ForStatement node) {
@@ -1067,7 +1068,20 @@
}
visitFunctionExpression(FunctionExpression node) {
- _visitBody(node.parameters, node.body);
+ // TODO(rnystrom): This is working but not tested. As of 2016/11/29, the
+ // latest version of analyzer on pub does not parse generic lambdas. When
+ // a version of it that does is published, upgrade dart_style to use it and
+ // then test it:
+ //
+ // >>> generic function expression
+ // main() {
+ // var generic = < T,S >(){};
+ // }
+ // <<<
+ // main() {
+ // var generic = <T, S>() {};
+ // }
+ _visitBody(node.typeParameters, node.parameters, node.body);
}
visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
@@ -1340,7 +1354,6 @@
// The indentation is fine, but splitting in the middle of each argument
// list looks kind of strange. If this ends up happening in real world
// code, consider putting a constraint between them.
-
visit(node.typeArguments);
visitArgumentList(node.argumentList, nestExpression: false);
@@ -1867,7 +1880,14 @@
visit(node.name);
builder.endSpan();
- _visitBody(function.parameters, function.body, () {
+ TypeParameterList typeParameters;
+ if (node is FunctionDeclaration) {
+ typeParameters = node.functionExpression.typeParameters;
+ } else {
+ typeParameters = (node as MethodDeclaration).typeParameters;
+ }
+
+ _visitBody(typeParameters, function.parameters, function.body, () {
// If the body is a block, we need to exit nesting before we hit the body
// indentation, but we do want to wrap it around the parameters.
if (function.body is! ExpressionFunctionBody) builder.unnest();
@@ -1882,7 +1902,8 @@
/// space before it if it's not empty.
///
/// If [beforeBody] is provided, it is invoked before the body is visited.
- void _visitBody(FormalParameterList parameters, FunctionBody body,
+ void _visitBody(TypeParameterList typeParameters,
+ FormalParameterList parameters, FunctionBody body,
[beforeBody()]) {
// If the body is "=>", add an extra level of indentation around the
// parameters and a rule that spans the parameters and the "=>". This
@@ -1914,7 +1935,16 @@
builder.startLazyRule(new Rule(Cost.arrow));
}
- if (parameters != null) visit(parameters);
+ // Start the nesting for the parameters here, so they wrap around the
+ // type parameters too, if any.
+ builder.nestExpression();
+
+ visit(typeParameters);
+ if (parameters != null) {
+ visitFormalParameterList(parameters, nestExpression: false);
+ }
+
+ builder.unnest();
if (beforeBody != null) beforeBody();
visit(body);
diff --git a/pubspec.yaml b/pubspec.yaml
index 40a0e05..de73b6d 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: 0.2.12
+version: 0.2.13
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/splitting/generic_method_parameters.unit b/test/splitting/generic_method_parameters.unit
new file mode 100644
index 0000000..bbe1f43
--- /dev/null
+++ b/test/splitting/generic_method_parameters.unit
@@ -0,0 +1,67 @@
+40 columns |
+>>> all fit on one line
+foo<A,B,C,D>() {}
+<<<
+foo<A, B, C, D>() {}
+>>> prefer to split between params even when they all fit on next line
+longFunctionName<First, Second, Third>() {}
+<<<
+longFunctionName<First, Second,
+ Third>() {}
+>>> split before first if needed
+longFunctionName<FirstTypeParameterIsLong, S>() {}
+<<<
+longFunctionName<
+ FirstTypeParameterIsLong, S>() {}
+>>> split in middle if fit in two lines
+longFunctionName<First, Second, Third, Fourth, Fifth, Sixth>() {}
+<<<
+longFunctionName<First, Second, Third,
+ Fourth, Fifth, Sixth>() {}
+>>> split one per line if they don't fit in two lines
+veryLongFunctionName<First, Second, Third, Fourth, Fifth, Sixth, Seventh>() {}
+<<<
+veryLongFunctionName<
+ First,
+ Second,
+ Third,
+ Fourth,
+ Fifth,
+ Sixth,
+ Seventh>() {}
+>>> split both type and value arguments
+lengthyMethodName<First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth>(first, second, third, fourth, fifth, sixth, seventh, eighth) {}
+<<<
+lengthyMethodName<
+ First,
+ Second,
+ Third,
+ Fourth,
+ Fifth,
+ Sixth,
+ Seventh,
+ Eighth>(
+ first,
+ second,
+ third,
+ fourth,
+ fifth,
+ sixth,
+ seventh,
+ eighth) {}
+>>> type parameters and => body
+longFunctionName<LongTypeParameterT, LongTypeParameterS>() => body;
+<<<
+longFunctionName<
+ LongTypeParameterT,
+ LongTypeParameterS>() =>
+ body;
+>>> type parameters, value parameters, and => body
+longFunctionName<LongTypeParameterT, LongTypeParameterS>(longParameter1, longParameter2) => body;
+<<<
+longFunctionName<
+ LongTypeParameterT,
+ LongTypeParameterS>(
+ longParameter1,
+ longParameter2) =>
+ body;
\ No newline at end of file
diff --git a/test/splitting/type_parameters.unit b/test/splitting/type_parameters.unit
index 088f045..920cb38 100644
--- a/test/splitting/type_parameters.unit
+++ b/test/splitting/type_parameters.unit
@@ -34,4 +34,16 @@
TypeArgument> extends Base {}
<<<
class SomeClass<TypeArgument>
- extends Base {}
\ No newline at end of file
+ extends Base {}
+>>> generic function declaration
+foo<A,B,C,D>() {}
+<<<
+foo<A, B, C, D>() {}
+>>> generic method on class
+class Foo {
+ foo<A,B,C,D>() {}
+}
+<<<
+class Foo {
+ foo<A, B, C, D>() {}
+}
\ No newline at end of file
diff --git a/test/utils.dart b/test/utils.dart
index 022fa28..2ebf406 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -21,8 +21,10 @@
// Locate the "test" directory. Use mirrors so that this works with the test
// package, which loads this suite into an isolate.
- var testDir = p.dirname(
- currentMirrorSystem().findLibrary(#dart_style.test.utils).uri.toFilePath());
+ var testDir = p.dirname(currentMirrorSystem()
+ .findLibrary(#dart_style.test.utils)
+ .uri
+ .toFilePath());
var formatterPath = p.normalize(p.join(testDir, "../bin/format.dart"));