Reformat expressions inside interpolated expressions. (#708)
* Reformat expressions inside interpolated expressions.
If it ever ends up splitting inside an interpolation, it looks pretty
gnarly, but that's mostly a signal to the user to split up the string.
It does nicely clean up the whitespace.
More importantly, this means fixes apply inside interpolation too.
Fix #226. Fix #707.
* Add test for fixing "new" inside string interpolation.
Also bump version before publishing.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cdfd1d0..b2aa4ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.1.1
+
+* Format expressions in string interpolations (#226).
+* Apply fixes inside string interpolations (#707).
+
# 1.1.0
* Add support for "style fixes", opt-in non-whitespace changes.
diff --git a/bin/format.dart b/bin/format.dart
index 999a9d8..23d0979 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -15,7 +15,7 @@
import 'package:dart_style/src/style_fix.dart';
// Note: The following line of code is modified by tool/grind.dart.
-const version = "1.1.0";
+const version = "1.1.1";
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 b4a5ca6..a05f5c8 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -1560,12 +1560,14 @@
visitInterpolationExpression(InterpolationExpression node) {
token(node.leftBracket);
+ builder.startSpan();
visit(node.expression);
+ builder.endSpan();
token(node.rightBracket);
}
visitInterpolationString(InterpolationString node) {
- token(node.contents);
+ _writeStringLiteral(node.contents);
}
visitIsExpression(IsExpression node) {
@@ -1834,24 +1836,13 @@
}
visitSimpleStringLiteral(SimpleStringLiteral node) {
- // Since we output the string literal manually, ensure any preceding
- // comments are written first.
- writePrecedingCommentsAndNewlines(node.literal);
-
- _writeStringLiteral(node.literal.lexeme, node.offset);
+ _writeStringLiteral(node.literal);
}
visitStringInterpolation(StringInterpolation node) {
- // Since we output the interpolated text manually, ensure we include any
- // preceding stuff first.
- writePrecedingCommentsAndNewlines(node.beginToken);
-
- // Right now, the formatter does not try to do any reformatting of the
- // contents of interpolated strings. Instead, it treats the entire thing as
- // a single (possibly multi-line) chunk of text.
- _writeStringLiteral(
- _source.text.substring(node.beginToken.offset, node.endToken.end),
- node.offset);
+ for (var element in node.elements) {
+ visit(element);
+ }
}
visitSuperConstructorInvocation(SuperConstructorInvocation node) {
@@ -2762,9 +2753,14 @@
///
/// Splits multiline strings into separate chunks so that the line splitter
/// can handle them correctly.
- void _writeStringLiteral(String string, int offset) {
+ void _writeStringLiteral(Token string) {
+ // Since we output the string literal manually, ensure any preceding
+ // comments are written first.
+ writePrecedingCommentsAndNewlines(string);
+
// Split each line of a multiline string into separate chunks.
- var lines = string.split(_formatter.lineEnding);
+ var lines = string.lexeme.split(_formatter.lineEnding);
+ var offset = string.offset;
_writeText(lines.first, offset);
offset += lines.first.length;
diff --git a/pubspec.lock b/pubspec.lock
index 94a5e50..54d2a9a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -366,4 +366,4 @@
source: hosted
version: "2.1.14"
sdks:
- dart: ">=2.0.0-dev.62.0 <=2.0.0-edge.83cb0c425dde6023625b9f9816136338f5df1b16"
+ dart: ">=2.0.0-dev.62.0 <=2.0.0-edge.edf26852e6f6f096bb935ab8a545ce0646cf05a6"
diff --git a/pubspec.yaml b/pubspec.yaml
index 6c2994c..0810110 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: 1.1.0
+version: 1.1.1
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/fixes/optional_new.stmt b/test/fixes/optional_new.stmt
index 6905b3f..20e7199 100644
--- a/test/fixes/optional_new.stmt
+++ b/test/fixes/optional_new.stmt
@@ -64,4 +64,8 @@
>>> handle already-removed keyword
A<int>(A<int>.named());
<<<
-A<int>(A<int>.named());
\ No newline at end of file
+A<int>(A<int>.named());
+>>> inside string interpolation
+"before ${new Foo()} after";
+<<<
+"before ${Foo()} after";
\ No newline at end of file
diff --git a/test/regression/0000/0057.stmt b/test/regression/0000/0057.stmt
index 282a5c7..be00d6b 100644
--- a/test/regression/0000/0057.stmt
+++ b/test/regression/0000/0057.stmt
@@ -6,7 +6,7 @@
<<<
embeddedPlural2(n) => Intl.message(
"${Intl.plural(n,
-zero: 'none', one: 'one', other: 'some')} plus some text.",
+ zero: 'none', one: 'one', other: 'some')} plus some text.",
name: 'embeddedPlural2',
desc: 'An embedded plural',
args: [n]);
\ No newline at end of file
diff --git a/test/regression/0100/0189.stmt b/test/regression/0100/0189.stmt
index b4235df..4acf089 100644
--- a/test/regression/0100/0189.stmt
+++ b/test/regression/0100/0189.stmt
@@ -11,7 +11,8 @@
nestedSelect(currency, amount) => Intl.select(
currency,
{
- "CDN": """${Intl.plural(amount, one: '$amount Canadian dollar',
+ "CDN": """${Intl.plural(amount,
+ one: '$amount Canadian dollar',
other: '$amount Canadian dollars')}""",
"other": "Whatever",
},
diff --git a/test/regression/0200/0226.stmt b/test/regression/0200/0226.stmt
new file mode 100644
index 0000000..d38cead
--- /dev/null
+++ b/test/regression/0200/0226.stmt
@@ -0,0 +1,4 @@
+>>>
+test('Bad Request${(detail!='' ? ': ' : '')}$detail');
+<<<
+test('Bad Request${(detail != '' ? ': ' : '')}$detail');
\ No newline at end of file
diff --git a/test/regression/0400/0467.unit b/test/regression/0400/0467.unit
index 57df4c3..680067c 100644
--- a/test/regression/0400/0467.unit
+++ b/test/regression/0400/0467.unit
@@ -21,6 +21,8 @@
'nodesAndEntryPoints',
new PolymerDom(this).children.map((child) =>
'${child.outerHtml} ------> '
- '${(new PolymerDom(child).getDestinationInsertionPoints()[0] as Element).outerHtml}'));
+ '${(new PolymerDom(child).getDestinationInsertionPoints()[0]
+ as Element)
+ .outerHtml}'));
}
}
\ No newline at end of file
diff --git a/test/regression/0500/0500.unit b/test/regression/0500/0500.unit
index 4e1a62f..82988db 100644
--- a/test/regression/0500/0500.unit
+++ b/test/regression/0500/0500.unit
@@ -26,9 +26,10 @@
for (int i = 0; i < names.length; ++i) {
env.initialize(names[i],
getter: (TopLevelBinding binding, ExprCont ek, ExprCont k) {
- binding.getter = (TopLevelBinding _, ExprCont ek0,
- ExprCont k0) =>
- ek0("Reading static variable '${binding.name}' during its initialization");
+ binding.getter =
+ (TopLevelBinding _, ExprCont ek0, ExprCont k0) => ek0(
+ "Reading static variable '${binding
+ .name}' during its initialization");
initializers[i](env, ek, (v) {
binding.getter =
(TopLevelBinding _, ExprCont ek1, ExprCont k1) => k1(v);
diff --git a/test/regression/0700/0707.stmt b/test/regression/0700/0707.stmt
new file mode 100644
index 0000000..c9f464a
--- /dev/null
+++ b/test/regression/0700/0707.stmt
@@ -0,0 +1,4 @@
+>>> (indent 4) (fix optional-new)
+ sink.write('FILE ACCESSED ${new DateTime.now()}\n');
+<<<
+ sink.write('FILE ACCESSED ${DateTime.now()}\n');
\ No newline at end of file
diff --git a/test/regression/other/analysis_server.unit b/test/regression/other/analysis_server.unit
index 0c03cfa..8dc5f80 100644
--- a/test/regression/other/analysis_server.unit
+++ b/test/regression/other/analysis_server.unit
@@ -258,4 +258,24 @@
.variables
.variables[0]
.name
- .staticElement;
\ No newline at end of file
+ .staticElement;
+>>>
+main() {
+ addTestFile('''
+ library libA;
+ part "${convertPathForImport('/testA.dart')}";
+ import "dart:math";
+ /// The [^]
+ main(aaa, bbb) {}
+ ''');
+}
+<<<
+main() {
+ addTestFile('''
+ library libA;
+ part "${convertPathForImport('/testA.dart')}";
+ import "dart:math";
+ /// The [^]
+ main(aaa, bbb) {}
+ ''');
+}
\ No newline at end of file
diff --git a/test/splitting/strings.stmt b/test/splitting/strings.stmt
index fc262fe..f6d5529 100644
--- a/test/splitting/strings.stmt
+++ b/test/splitting/strings.stmt
@@ -138,3 +138,17 @@
"many",
"elements"
]);
+>>> interpolation in multi-line does not force unneeded splits
+someMethod("foo", """
+ some text that is pretty long
+ some more text that is pretty long
+ ${ inpolate + a + thing }
+ more text
+""");
+<<<
+someMethod("foo", """
+ some text that is pretty long
+ some more text that is pretty long
+ ${inpolate + a + thing}
+ more text
+""");
\ No newline at end of file
diff --git a/test/utils.dart b/test/utils.dart
index 6b7a474..74fa489 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -18,7 +18,8 @@
const unformattedSource = 'void main() => print("hello") ;';
const formattedSource = 'void main() => print("hello");\n';
-final _indentPattern = new RegExp(r"^\(indent (\d+)\)\s*");
+final _indentPattern = new RegExp(r"\(indent (\d+)\)");
+final _fixPattern = new RegExp(r"\(fix ([a-x-]+)\)");
/// Runs the command line formatter, passing it [args].
Future<TestProcess> runFormatter([List<String> args]) {
@@ -36,10 +37,7 @@
args.insert(0, formatterPath);
// Use the same package root, if there is one.
- if (Platform.packageRoot != null && Platform.packageRoot.isNotEmpty) {
- args.insert(0, "--package-root=${Platform.packageRoot}");
- } else if (Platform.packageConfig != null &&
- Platform.packageConfig.isNotEmpty) {
+ if (Platform.packageConfig != null && Platform.packageConfig.isNotEmpty) {
args.insert(0, "--packages=${Platform.packageConfig}");
}
@@ -84,7 +82,10 @@
_testFile(p.dirname(path), p.join(testDir, path), fixes);
}
-void _testFile(String name, String path, Iterable<StyleFix> fixes) {
+void _testFile(String name, String path, Iterable<StyleFix> baseFixes) {
+ var fixes = <StyleFix>[];
+ if (baseFixes != null) fixes.addAll(baseFixes);
+
group("$name ${p.basename(path)}", () {
// Explicitly create a File, in case the entry is a Link.
var lines = new File(path).readAsLinesSync();
@@ -98,16 +99,23 @@
var i = 0;
while (i < lines.length) {
- var description = lines[i++].replaceAll(">>>", "").trim();
+ var description = lines[i++].replaceAll(">>>", "");
// Let the test specify a leading indentation. This is handy for
// regression tests which often come from a chunk of nested code.
var leadingIndent = 0;
- var indentMatch = _indentPattern.firstMatch(description);
- if (indentMatch != null) {
- leadingIndent = int.parse(indentMatch[1]);
- description = description.substring(indentMatch.end);
- }
+ description = description.replaceAllMapped(_indentPattern, (match) {
+ leadingIndent = int.parse(match[1]);
+ return "";
+ });
+
+ // Let the test specify fixes to apply.
+ description = description.replaceAllMapped(_fixPattern, (match) {
+ fixes.add(StyleFix.all.firstWhere((fix) => fix.name == match[1]));
+ return "";
+ });
+
+ description = description.trim();
if (description == "") {
description = "line ${i + 1}";
diff --git a/test/whitespace/expressions.stmt b/test/whitespace/expressions.stmt
index d67bd89..4cf871a 100644
--- a/test/whitespace/expressions.stmt
+++ b/test/whitespace/expressions.stmt
@@ -142,4 +142,8 @@
>>> generic method call
method <int,String , bool> ();
<<<
-method<int, String, bool>();
\ No newline at end of file
+method<int, String, bool>();
+>>> inside interpolation
+" ${ interp+olate } and ${fn ( 1 ) } end";
+<<<
+" ${interp + olate} and ${fn(1)} end";
\ No newline at end of file