Index: lib/src/source_visitor.dart |
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart |
index a80a960697d51f91462c4496d291856c1a871bd0..cb19267087c469c76bb073ed291b51313813dee4 100644 |
--- a/lib/src/source_visitor.dart |
+++ b/lib/src/source_visitor.dart |
@@ -879,6 +879,14 @@ class SourceVisitor implements AstVisitor { |
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 @@ class SourceVisitor implements AstVisitor { |
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 @@ class SourceVisitor implements AstVisitor { |
_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); |
jakemac
2016/07/29 21:57:05
or possibly assert(node.parameters.last.endToken.n
Bob Nystrom
2016/07/29 22:18:26
I could assert that too, but it felt a bit gratuit
|
+ |
+ // 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]. |
/// |