Handle function arguments inside function calls.
Fixes #366.
Also, I'm calling it. This is the 0.2.0 release.
R=kevmoo@google.com, pquitslund@google.com
Review URL: https://chromiumcodereview.appspot.com//1258203006 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 602970f..731fa32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.2.0
+
+* Treat functions nested inside function calls like block arguments (#366).
+
# 0.2.0-rc.4
* Smarter indentation for function arguments (#369).
diff --git a/lib/src/argument_list_visitor.dart b/lib/src/argument_list_visitor.dart
index 4acb3b4..c45b737 100644
--- a/lib/src/argument_list_visitor.dart
+++ b/lib/src/argument_list_visitor.dart
@@ -97,12 +97,8 @@
new ArgumentSublist(node.arguments, argumentsAfter));
}
- ArgumentListVisitor._(
- this._visitor,
- this._node,
- this._arguments,
- this._functions,
- this._argumentsAfterFunctions);
+ ArgumentListVisitor._(this._visitor, this._node, this._arguments,
+ this._functions, this._argumentsAfterFunctions);
/// Builds chunks for the call chain.
void visit() {
@@ -162,11 +158,36 @@
expression = (expression as NamedExpression).expression;
}
+ // Allow functions wrapped in dotted method calls like "a.b.c(() { ... })".
+ if (expression is MethodInvocation) {
+ if (!_isValidWrappingTarget(expression.target)) return false;
+ if (expression.argumentList.arguments.length != 1) return false;
+
+ return _isBlockFunction(expression.argumentList.arguments.single);
+ }
+
// Curly body functions are.
if (expression is! FunctionExpression) return false;
var function = expression as FunctionExpression;
return function.body is BlockFunctionBody;
}
+
+ /// Returns `true` if [expression] is a valid method invocation target for
+ /// an invocation that wraps a function literal argument.
+ static bool _isValidWrappingTarget(Expression expression) {
+ // Allow bare function calls.
+ if (expression == null) return true;
+
+ // Allow property accesses.
+ while (expression is PropertyAccess) {
+ expression = (expression as PropertyAccess).target;
+ }
+
+ if (expression is PrefixedIdentifier) return true;
+ if (expression is SimpleIdentifier) return true;
+
+ return false;
+ }
}
/// A range of arguments from a complete argument list.
@@ -333,8 +354,8 @@
}
// Split before the first named argument.
- namedRule.beforeArguments(visitor.builder.split(
- space: !_isFirstArgument(_named.first)));
+ namedRule.beforeArguments(
+ visitor.builder.split(space: !_isFirstArgument(_named.first)));
for (var argument in _named) {
_visitArgument(visitor, namedRule, argument);
@@ -346,7 +367,8 @@
visitor.builder.endRule();
}
- void _visitArgument(SourceVisitor visitor, ArgumentRule rule, Expression argument) {
+ void _visitArgument(
+ SourceVisitor visitor, ArgumentRule rule, Expression argument) {
// If we're about to write a collection argument, handle it specially.
if (_collections.contains(argument)) {
if (rule != null) rule.beforeCollection();
@@ -394,4 +416,4 @@
return expression is ListLiteral || expression is MapLiteral;
}
-}
\ No newline at end of file
+}
diff --git a/lib/src/line_splitting/solve_state.dart b/lib/src/line_splitting/solve_state.dart
index 04a9b11..b419756 100644
--- a/lib/src/line_splitting/solve_state.dart
+++ b/lib/src/line_splitting/solve_state.dart
@@ -218,7 +218,7 @@
// Lines that contain both bound and unbound rules must have the same
// bound values.
if (_boundRulesInUnboundLines.length !=
- other._boundRulesInUnboundLines.length) {
+ other._boundRulesInUnboundLines.length) {
return false;
}
@@ -415,7 +415,6 @@
var hasUnbound = false;
for (var i = 0; i < _splitter.chunks.length - 1; i++) {
-
if (splits.shouldSplitAt(i)) {
if (hasUnbound) _boundRulesInUnboundLines.addAll(boundInLine);
@@ -439,25 +438,23 @@
String toString() {
var buffer = new StringBuffer();
- buffer.writeAll(
- _splitter.rules.map((rule) {
- var valueLength = "${rule.fullySplitValue}".length;
+ buffer.writeAll(_splitter.rules.map((rule) {
+ var valueLength = "${rule.fullySplitValue}".length;
- var value = "?";
- if (_ruleValues.contains(rule)) {
- value = "${_ruleValues.getValue(rule)}";
- }
+ var value = "?";
+ if (_ruleValues.contains(rule)) {
+ value = "${_ruleValues.getValue(rule)}";
+ }
- value = value.padLeft(valueLength);
- if (_liveRules.contains(rule)) {
- value = debug.bold(value);
- } else {
- value = debug.gray(value);
- }
+ value = value.padLeft(valueLength);
+ if (_liveRules.contains(rule)) {
+ value = debug.bold(value);
+ } else {
+ value = debug.gray(value);
+ }
- return value;
- }),
- " ");
+ return value;
+ }), " ");
buffer.write(" \$${splits.cost}");
diff --git a/lib/src/rule/argument.dart b/lib/src/rule/argument.dart
index 8e08fba..813bfb1 100644
--- a/lib/src/rule/argument.dart
+++ b/lib/src/rule/argument.dart
@@ -113,9 +113,8 @@
/// split before the argument if the argument itself contains a split.
SinglePositionalRule(Rule collectionRule, {bool splitsOnInnerRules})
: super(collectionRule),
- splitsOnInnerRules = splitsOnInnerRules
- != null
- ? splitsOnInnerRules : false;
+ splitsOnInnerRules =
+ splitsOnInnerRules != null ? splitsOnInnerRules : false;
bool isSplit(int value, Chunk chunk) => value == 1;
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index a59ff5e..524db2e 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -647,11 +647,9 @@
visit(node.name);
space();
- _writeBody(node.leftBracket, node.rightBracket,
- space: true,
- body: () {
- visitCommaSeparatedNodes(node.constants, between: split);
- });
+ _writeBody(node.leftBracket, node.rightBracket, space: true, body: () {
+ visitCommaSeparatedNodes(node.constants, between: split);
+ });
}
visitExportDirective(ExportDirective node) {
diff --git a/pubspec.yaml b/pubspec.yaml
index 086999d..7d5bf57 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart_style
-version: 0.2.0-rc.4
+version: 0.2.0
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/formatter_test.dart b/test/formatter_test.dart
index 859cf6b..d227133 100644
--- a/test/formatter_test.dart
+++ b/test/formatter_test.dart
@@ -39,12 +39,14 @@
test("FormatterException describes parse errors", () {
try {
- new DartFormatter().format("""
+ new DartFormatter().format(
+ """
var a = some error;
var b = another one;
- """, uri: "my_file.dart");
+ """,
+ uri: "my_file.dart");
fail("Should throw.");
} on FormatterException catch (err) {
@@ -90,7 +92,8 @@
test('preserves initial indent', () {
var formatter = new DartFormatter(indent: 3);
expect(
- formatter.formatStatement('if (foo) {bar;}'), equals(' if (foo) {\n'
+ formatter.formatStatement('if (foo) {bar;}'),
+ equals(' if (foo) {\n'
' bar;\n'
' }'));
});
@@ -119,7 +122,8 @@
expect(
new DartFormatter(lineEnding: "\r\n").formatStatement(' """first\r\n'
'second\r\n'
- 'third""" ;'), equals('"""first\r\n'
+ 'third""" ;'),
+ equals('"""first\r\n'
'second\r\n'
'third""";'));
});
diff --git a/test/regression/0100/0108.unit b/test/regression/0100/0108.unit
index 2e0bfeb..64522f2 100644
--- a/test/regression/0100/0108.unit
+++ b/test/regression/0100/0108.unit
@@ -207,19 +207,11 @@
DDC$RT.type((Iterable<Future<dynamic>> _) {}), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
DDC$RT.type((Future<List<StyleElement>> _) {}), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:7"));
<<<
-async.Future<List<dom.StyleElement>> call(String tag, List<String> cssUrls,
- {Type type}) =>
+async.Future<List<dom.StyleElement>> call(String tag, List<String> cssUrls, {Type type}) =>
(DDC$RT.cast(
- async.Future.wait((DDC$RT.cast(
- cssUrls.map((url) => _styleElement(
- tag,
- (DDC$RT.cast(url, String,
- key:
- "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")),
- type)),
- DDC$RT.type((Iterable<Future<dynamic>> _) {}),
- key:
- "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
+ async.Future.wait(
+ (DDC$RT.cast(cssUrls.map((url) => _styleElement(tag, (DDC$RT.cast(url, String, key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:65")), type)),
+ DDC$RT.type((Iterable<Future<dynamic>> _) {
+ }), key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:25"))),
DDC$RT.type((Future<List<StyleElement>> _) {}),
- key:
- "Cast failed: package:angular/core_dom/component_css_loader.dart:17:7"));
\ No newline at end of file
+ key: "Cast failed: package:angular/core_dom/component_css_loader.dart:17:7"));
\ No newline at end of file
diff --git a/test/regression/0300/0366.stmt b/test/regression/0300/0366.stmt
new file mode 100644
index 0000000..e14856e
--- /dev/null
+++ b/test/regression/0300/0366.stmt
@@ -0,0 +1,10 @@
+>>> (indent 4)
+ zoop(
+ "zoop description here",
+ spang(() {
+ ;
+ }));
+<<<
+ zoop("zoop description here", spang(() {
+ ;
+ }));
\ No newline at end of file
diff --git a/test/splitting/function_arguments.stmt b/test/splitting/function_arguments.stmt
index 3da9e71..89c0956 100644
--- a/test/splitting/function_arguments.stmt
+++ b/test/splitting/function_arguments.stmt
@@ -137,34 +137,30 @@
}, b: () {
;
});
->>> don't nest because of nested 1-arg fn
+>>> do not nest because of nested 1-arg fn
outer(inner(() {body;}));
<<<
outer(inner(() {
body;
}));
->>> do nest because of nested many-arg fn
+>>> do not nest because of nested many-arg fn
outer(argument, inner(() {body;}));
<<<
-outer(
- argument,
- inner(() {
- body;
- }));
->>> don't nest because of nested 1-arg method call
-obj.outer(obj.inner(() {body;}));
-<<<
-obj.outer(obj.inner(() {
+outer(argument, inner(() {
body;
}));
->>> do nest because of nested many-arg method call
-obj.outer(argument, obj.inner(() {body;}));
+>>> do not nest because of nested 1-arg method call
+obj.outer(a.b.c.fn(() {body;}));
<<<
-obj.outer(
- argument,
- obj.inner(() {
- body;
- }));
+obj.outer(a.b.c.fn(() {
+ body;
+}));
+>>> do not nest because of nested many-arg method call
+obj.outer(argument, a.b.c.fn(() {body;}));
+<<<
+obj.outer(argument, a.b.c.fn(() {
+ body;
+}));
>>> do not force named args to split on positional function
function(argument, () {;},
named: argument, another: argument);
diff --git a/test/splitting/mixed.stmt b/test/splitting/mixed.stmt
index 2c57dae..0ef02fd 100644
--- a/test/splitting/mixed.stmt
+++ b/test/splitting/mixed.stmt
@@ -71,11 +71,9 @@
>>> unnested function inside nested expression
function(argument, function(() {;}));
<<<
-function(
- argument,
- function(() {
- ;
- }));
+function(argument, function(() {
+ ;
+}));
>>> nested function inside nested expression
function(argument, function(() {;}, argument, () {;}));
<<<