Format parameter lists with trailing commas like argument lists.
Fix #447.
R=jakemac@google.com
Review URL: https://codereview.chromium.org//2196863002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fe3947..fcccee8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.8
+
+* Format parameter lists with trailing commas like argument lists (#447).
+
# 0.2.7
* Make it strong mode clean.
diff --git a/bin/format.dart b/bin/format.dart
index 410d0c8..a25680e 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.7";
+const version = "0.2.8";
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 a80a960..cb19267 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -879,6 +879,14 @@
return;
}
+ // If the parameter list has a trailing comma, format it like a collection
+ // literal where each parameter goes on its own line, they are indented +2,
+ // and the ")" ends up on its own line.
+ if (node.parameters.last.endToken.next.type == TokenType.COMMA) {
+ _visitTrailingCommaParameterList(node);
+ return;
+ }
+
var requiredParams = node.parameters
.where((param) => param is! DefaultFormalParameter)
.toList();
@@ -1854,11 +1862,7 @@
builder.startLazyRule(new Rule(Cost.arrow));
}
- if (parameters != null) {
- builder.nestExpression();
- visit(parameters);
- builder.unnest();
- }
+ if (parameters != null) visit(parameters);
if (beforeBody != null) beforeBody();
visit(body);
@@ -2020,6 +2024,77 @@
_endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force);
}
+ /// Writes [parameters], which is assumed to have a trailing comma after the
+ /// last parameter.
+ ///
+ /// Parameter lists with trailing commas are formatted differently from
+ /// regular parameter lists. They are treated more like collection literals.
+ ///
+ /// We don't reuse [_visitCollectionLiteral] here because there are enough
+ /// weird differences around optional parameters that it's easiest just to
+ /// give them their own method.
+ void _visitTrailingCommaParameterList(FormalParameterList parameters) {
+ // Can't have a trailing comma if there are no parameters.
+ assert(parameters.parameters.isNotEmpty);
+
+ // Always split the parameters.
+ builder.startRule(new Rule.hard());
+
+ token(parameters.leftParenthesis);
+
+ // Find the parameter immediately preceding the optional parameters (if
+ // there are any).
+ FormalParameter lastRequired;
+ for (var i = 0; i < parameters.parameters.length; i++) {
+ if (parameters.parameters[i] is DefaultFormalParameter) {
+ if (i > 0) lastRequired = parameters.parameters[i - 1];
+ break;
+ }
+ }
+
+ // If all parameters are optional, put the "[" or "{" right after "(".
+ if (parameters.parameters.first is DefaultFormalParameter) {
+ token(parameters.leftDelimiter);
+ }
+
+ // Process the parameters as a separate set of chunks.
+ builder = builder.startBlock(null);
+
+ for (var parameter in parameters.parameters) {
+ builder.nestExpression();
+ visit(parameter);
+
+ // The comma after the parameter.
+ if (parameter.endToken.next.type == TokenType.COMMA) {
+ token(parameter.endToken.next);
+ }
+
+ // If the optional parameters start after this one, put the delimiter
+ // at the end of its line.
+ if (parameter == lastRequired) {
+ space();
+ token(parameters.leftDelimiter);
+ lastRequired = null;
+ }
+
+ builder.unnest();
+ newline();
+ }
+
+ // Put comments before the closing ")", "]", or "}" inside the block.
+ var firstDelimiter =
+ parameters.rightDelimiter ?? parameters.rightParenthesis;
+ writePrecedingCommentsAndNewlines(firstDelimiter);
+ builder = builder.endBlock(null, forceSplit: true);
+ builder.endRule();
+
+ // Now write the delimiter itself.
+ _writeText(firstDelimiter.lexeme, firstDelimiter.offset);
+ if (firstDelimiter != parameters.rightParenthesis) {
+ token(parameters.rightParenthesis);
+ }
+ }
+
/// Gets the cost to split at an assignment (or `:` in the case of a named
/// default value) with the given [rightHandSide].
///
diff --git a/pubspec.yaml b/pubspec.yaml
index 8ae5477..9464315 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.7
+version: 0.2.8
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/comments/functions.unit b/test/comments/functions.unit
index b2d0bc9..00c235b 100644
--- a/test/comments/functions.unit
+++ b/test/comments/functions.unit
@@ -126,4 +126,21 @@
<<<
main() {
// comment
+}
+>>> comment before "]" with trailing comma
+function([parameter,/* c */]) {;}
+<<<
+function([
+ parameter,
+ /* c */
+]) {
+ ;
+}
+>>> comment before ")" with trailing comma
+function([parameter,]/* c */) {;}
+<<<
+function([
+ parameter,
+] /* c */) {
+ ;
}
\ No newline at end of file
diff --git a/test/whitespace/functions.unit b/test/whitespace/functions.unit
index 2e8f487..206ec2b 100644
--- a/test/whitespace/functions.unit
+++ b/test/whitespace/functions.unit
@@ -77,10 +77,88 @@
var lambda = () sync* {};
}
>>> trailing comma in single parameter list
-function(argument , ) {}
+function(parameter , ) {;}
<<<
-function(argument,) {}
+function(
+ parameter,
+) {
+ ;
+}
>>> trailing comma in parameter list
-function(argument,argument , ) {}
+function(parameter,parameter , ) {;}
<<<
-function(argument, argument,) {}
\ No newline at end of file
+function(
+ parameter,
+ parameter,
+) {
+ ;
+}
+>>> trailing comma in all optional parameter list
+function([parameter,parameter , ]) {;}
+<<<
+function([
+ parameter,
+ parameter,
+]) {
+ ;
+}
+>>> trailing comma in all named parameter list
+function({parameter,parameter , }) {;}
+<<<
+function({
+ parameter,
+ parameter,
+}) {
+ ;
+}
+>>> trailing comma in mixed optional parameter list
+function(parameter,[parameter,parameter , ]) {;}
+<<<
+function(
+ parameter, [
+ parameter,
+ parameter,
+]) {
+ ;
+}
+>>> trailing comma in mixed named parameter list
+function(parameter,{parameter,parameter , }) {;}
+<<<
+function(
+ parameter, {
+ parameter,
+ parameter,
+}) {
+ ;
+}
+>>> trailing comma with => function containing split
+function(parameter,parameter,) => veryLongBodyThatWraps(argument, argument, argument, argument);
+<<<
+function(
+ parameter,
+ parameter,
+) =>
+ veryLongBodyThatWraps(argument,
+ argument, argument, argument);
+>>> trailing comma with wrap at =>
+function(parameter,parameter,) /* comment */ => "a very very long string";
+<<<
+function(
+ parameter,
+ parameter,
+) /* comment */ =>
+ "a very very long string";
+>>> trailing comma function nested in expression
+main() {
+ someVeryLongFunction(argument, argument, (parameter, parameter,) {;});
+}
+<<< (this looks weird, but it should rare and at least we test it)
+main() {
+ someVeryLongFunction(
+ argument, argument, (
+ parameter,
+ parameter,
+ ) {
+ ;
+ });
+}
\ No newline at end of file