From 89955e5d83caf1afdce5f8a34ab7c8f0ff9db60f Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Fri, 22 Mar 2019 16:09:13 -0700 Subject: [PATCH 1/2] Add support for "for" elements in collection literals. --- CHANGELOG.md | 5 + bin/format.dart | 2 +- lib/src/source_visitor.dart | 77 +++++++++--- pubspec.lock | 43 +------ pubspec.yaml | 4 +- test/splitting/list_collection_for.stmt | 147 ++++++++++++++++++++++ test/splitting/map_collection_for.stmt | 155 ++++++++++++++++++++++++ test/splitting/set_collection_for.stmt | 147 ++++++++++++++++++++++ test/whitespace/collections.stmt | 38 +++++- 9 files changed, 558 insertions(+), 60 deletions(-) create mode 100644 test/splitting/list_collection_for.stmt create mode 100644 test/splitting/map_collection_for.stmt create mode 100644 test/splitting/set_collection_for.stmt diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a26ada3..28b5d214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.2.5 + +* Add support for spreads inside collections (#778). +* Add support for `if` and `for` elements inside collections (#779). + # 1.2.4 * Update to latest analyzer package AST API. diff --git a/bin/format.dart b/bin/format.dart index 04e4f0c2..0d779647 100644 --- a/bin/format.dart +++ b/bin/format.dart @@ -15,7 +15,7 @@ import 'package:dart_style/src/source_code.dart'; import 'package:dart_style/src/style_fix.dart'; // Note: The following line of code is modified by tool/grind.dart. -const version = "1.2.4"; +const version = "1.2.5"; void main(List args) { var parser = new ArgParser(allowTrailingOptions: true); diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart index 85c02e12..60a527f2 100644 --- a/lib/src/source_visitor.dart +++ b/lib/src/source_visitor.dart @@ -1159,6 +1159,45 @@ class SourceVisitor extends ThrowingAstVisitor { if (nestExpression) builder.unnest(); } + void visitForElement(ForElement node) { + // Treat a spread of a collection literal like a block in a for statement + // and don't split after the for parts. + var isSpreadBody = _isSpreadCollection(node.body); + + builder.nestExpression(); + token(node.awaitKeyword, after: space); + token(node.forKeyword); + space(); + token(node.leftParenthesis); + + // Start the body rule so that if the parts split, the body does too. + builder.startRule(); + + // The rule for the parts. + builder.startRule(); + visit(node.forLoopParts); + token(node.rightParenthesis); + builder.endRule(); + builder.unnest(); + + builder.nestExpression(indent: 2, now: true); + + if (isSpreadBody) { + space(); + } else { + split(); + + // If the body is a non-spread collection or lambda, indent it. + builder.startBlockArgumentNesting(); + } + + visit(node.body); + + if (!isSpreadBody) builder.endBlockArgumentNesting(); + builder.unnest(); + builder.endRule(); + } + visitForStatement2(ForStatement2 node) { builder.nestExpression(); token(node.awaitKeyword, after: space); @@ -1490,23 +1529,6 @@ class SourceVisitor extends ThrowingAstVisitor { builder.endRule(); } - /// If [node] is a spread of a collection literal, then this returns the - /// token for the opening bracket of the collection, as in: - /// - /// [ ...[a, list] ] - /// // ^ - /// - /// Otherwise, returns `null`. - Token _findSpreadCollectionBracket(AstNode node) { - if (node is SpreadElement) { - var expression = node.expression; - if (expression is ListLiteral) return expression.leftBracket; - if (expression is SetOrMapLiteral) return expression.leftBracket; - } - - return null; - } - visitIfStatement(IfStatement node) { builder.nestExpression(); token(node.ifKeyword); @@ -2802,6 +2824,27 @@ class SourceVisitor extends ThrowingAstVisitor { builder.endRule(); } + /// Whether [node] is a spread of a collection literal. + bool _isSpreadCollection(AstNode node) => + _findSpreadCollectionBracket(node) != null; + + /// If [node] is a spread of a collection literal, then this returns the + /// token for the opening bracket of the collection, as in: + /// + /// [ ...[a, list] ] + /// // ^ + /// + /// Otherwise, returns `null`. + Token _findSpreadCollectionBracket(AstNode node) { + if (node is SpreadElement) { + var expression = node.expression; + if (expression is ListLiteral) return expression.leftBracket; + if (expression is SetOrMapLiteral) return expression.leftBracket; + } + + return null; + } + /// 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.lock b/pubspec.lock index 6172ed62..fd338b73 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,7 +7,7 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.35.3" + version: "0.35.4" args: dependency: "direct main" description: @@ -21,7 +21,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.1.0" boolean_selector: dependency: transitive description: @@ -64,20 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.6" - csslib: - dependency: transitive - description: - name: csslib - url: "https://pub.dartlang.org" - source: hosted - version: "0.14.6" front_end: dependency: transitive description: name: front_end url: "https://pub.dartlang.org" source: hosted - version: "0.1.13" + version: "0.1.14" glob: dependency: transitive description: @@ -92,13 +85,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.8.3" - html: - dependency: transitive - description: - name: html - url: "https://pub.dartlang.org" - source: hosted - version: "0.13.4+1" http: dependency: transitive description: @@ -147,14 +133,7 @@ packages: name: kernel url: "https://pub.dartlang.org" source: hosted - version: "0.3.13" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "0.11.3+2" + version: "0.3.14" matcher: dependency: transitive description: @@ -218,13 +197,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.0" - plugin: - dependency: transitive - description: - name: plugin - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.0+3" pool: dependency: transitive description: @@ -358,13 +330,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.6" - utf: - dependency: transitive - description: - name: utf - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.0+5" vm_service_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 386d163c..ba9edba8 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.2.4 +version: 1.2.5 author: Dart Team description: Opinionated, automatic Dart source code formatter. homepage: https://github.com/dart-lang/dart_style @@ -9,7 +9,7 @@ environment: sdk: '>=2.0.0 <3.0.0' dependencies: - analyzer: '>=0.35.3 <0.36.0' + analyzer: '>=0.35.3 <0.37.0' args: '>=0.12.1 <2.0.0' path: ^1.0.0 source_span: ^1.4.0 diff --git a/test/splitting/list_collection_for.stmt b/test/splitting/list_collection_for.stmt new file mode 100644 index 00000000..b569861c --- /dev/null +++ b/test/splitting/list_collection_for.stmt @@ -0,0 +1,147 @@ +40 columns | +>>> split collection before for +var list = [for (a in b) somewhatLongThingHere]; +<<< +var list = [ + for (a in b) somewhatLongThingHere +]; +>>> one line in multi-line +var list = [veryLongThingThatForcesASplit, for (a in b) 2, 3]; +<<< +var list = [ + veryLongThingThatForcesASplit, + for (a in b) 2, + 3 +]; +>>> long body forces split +var list = [1, for (a in b) veryLongThingThatForcesASplit, 3]; +<<< +var list = [ + 1, + for (a in b) + veryLongThingThatForcesASplit, + 3 +]; +>>> split inside body +var list = [1, for (a in b) veryLongThingThatForcesASplit + anotherLongThing, 3]; +<<< +var list = [ + 1, + for (a in b) + veryLongThingThatForcesASplit + + anotherLongThing, + 3 +]; +>>> trailing comma +var list = [for (a in b) 2,]; +<<< +var list = [ + for (a in b) 2, +]; +>>> spread list inside for stays on one line if it fits +var list = [for (a in b) ...[1, 2]]; +<<< +var list = [ + for (a in b) ...[1, 2] +]; +>>> spread list inside for formats like block if it splits +var list = [for (a in b) ...[1, 2,]]; +<<< +var list = [ + for (a in b) ...[ + 1, + 2, + ] +]; +>>> a split collection that isn't spread wraps and indents +var list = [for (a in b) [1,2,]]; +<<< +var list = [ + for (a in b) + [ + 1, + 2, + ] +]; +>>> lambda inside body +var list = [for (a in b) () { body; }]; +<<< +var list = [ + for (a in b) + () { + body; + } +]; +>>> nested for doesn't split if it fits +var l = [for (a in b) for (c in d) t]; +<<< +var l = [for (a in b) for (c in d) t]; +>>> split collection before nested for +var list = [for (a in b) for (c in d) longThing]; +<<< +var list = [ + for (a in b) for (c in d) longThing +]; +>>> just split outer for +var list = [for (a in b) for (c in d) longThingThatIsLong]; +<<< +var list = [ + for (a in b) + for (c in d) longThingThatIsLong +]; +>>> split in for-in type +var list = [for (LongGenericTypeName a in b) body]; +<<< +var list = [ + for (LongGenericTypeName a in b) + body +]; +>>> split in for-in expression +var list = [for (a in sequenceExpression + thatDoesNotFit) body]; +<<< +var list = [ + for (a in sequenceExpression + + thatDoesNotFit) + body +]; +>>> split in for var type +var list = [for (LongGenericTypeName a = 0; a < 1; a++) body]; +<<< +var list = [ + for (LongGenericTypeName a = 0; + a < 1; + a++) + body +]; +>>> split in for initializer +var list = [for (a = initializerExpression + thatDoesNotFit; a < 1; a++) body]; +<<< +var list = [ + for (a = initializerExpression + + thatDoesNotFit; + a < 1; + a++) + body +]; +>>> split in for condition +var list = [for (a = b; conditionExpression + thatDoesNotFit; a++) body]; +<<< +var list = [ + for (a = b; + conditionExpression + + thatDoesNotFit; + a++) + body +]; +>>> split in for increment +var list = [for (a = b; a < 1; incrementExpression + thatDoesNotFit) body]; +<<< +var list = [ + for (a = b; + a < 1; + incrementExpression + + thatDoesNotFit) + body +]; \ No newline at end of file diff --git a/test/splitting/map_collection_for.stmt b/test/splitting/map_collection_for.stmt new file mode 100644 index 00000000..eed802e6 --- /dev/null +++ b/test/splitting/map_collection_for.stmt @@ -0,0 +1,155 @@ +40 columns | +>>> split collection before for +var map = {for (a in b) somewhatLongThingHere: 1}; +<<< +var map = { + for (a in b) somewhatLongThingHere: 1 +}; +>>> one line in multi-line +var map = {veryLongThingThatForcesASplit: 1, for (a in b) 2: 2, 3: 3}; +<<< +var map = { + veryLongThingThatForcesASplit: 1, + for (a in b) 2: 2, + 3: 3 +}; +>>> long body forces split +var map = {1: 1, for (a in b) veryLongThingThatForcesASplit, 3: 3}; +<<< +var map = { + 1: 1, + for (a in b) + veryLongThingThatForcesASplit, + 3: 3 +}; +>>> split inside body +var map = {1: 1, for (a in b) veryLongThingThatForcesASplit + anotherLongThing, 3: 3}; +<<< +var map = { + 1: 1, + for (a in b) + veryLongThingThatForcesASplit + + anotherLongThing, + 3: 3 +}; +>>> trailing comma +var map = {for (a in b) 2: 2,}; +<<< +var map = { + for (a in b) 2: 2, +}; +>>> spread map inside for stays on one line if it fits +var map = {for (a in b) ...{1: 1, 2: 2}}; +<<< +var map = { + for (a in b) ...{1: 1, 2: 2} +}; +>>> spread map inside for formats like block if it splits +var map = {for (a in b) ...{1: 1, 2: 2,}}; +<<< +var map = { + for (a in b) ...{ + 1: 1, + 2: 2, + } +}; +>>> a split collection that isn't spread wraps and indents +var map = {for (a in b) {1: 1,2: 2,}}; +<<< +var map = { + for (a in b) + { + 1: 1, + 2: 2, + } +}; +>>> lambda inside body +var map = {for (a in b) k: () { body; }}; +<<< +var map = { + for (a in b) + k: () { + body; + } +}; +>>> nested for doesn't split if it fits +({for (a in b) for (c in d) 1: 1}); +<<< +({for (a in b) for (c in d) 1: 1}); +>>> split collection before nested for +var map = {for (a in b) for (c in d) 1: longThing}; +<<< +var map = { + for (a in b) for (c in d) 1: longThing +}; +>>> just split outer for +var map = {for (a in b) for (c in d) 1: longThingThatIsLong}; +<<< +var map = { + for (a in b) + for (c in d) 1: longThingThatIsLong +}; +>>> split in for-in type +var map = {for (LongGenericTypeName a in b) 1: body}; +<<< +var map = { + for (LongGenericTypeName a in b) + 1: body +}; +>>> split in for-in expression +var map = {for (a in sequenceExpression + thatDoesNotFit) 1: body}; +<<< +var map = { + for (a in sequenceExpression + + thatDoesNotFit) + 1: body +}; +>>> split in for var type +var map = {for (LongGenericTypeName a = 0; a < 1; a++) 1: body}; +<<< +var map = { + for (LongGenericTypeName a = 0; + a < 1; + a++) + 1: body +}; +>>> split in for initializer +var map = {for (a = initializerExpression + thatDoesNotFit; a < 1; a++) 1: body}; +<<< +var map = { + for (a = initializerExpression + + thatDoesNotFit; + a < 1; + a++) + 1: body +}; +>>> split in for condition +var map = {for (a = b; conditionExpression + thatDoesNotFit; a++) 1: body}; +<<< +var map = { + for (a = b; + conditionExpression + + thatDoesNotFit; + a++) + 1: body +}; +>>> split in for increment +var map = {for (a = b; a < 1; incrementExpression + thatDoesNotFit) 1: body}; +<<< +var map = { + for (a = b; + a < 1; + incrementExpression + + thatDoesNotFit) + 1: body +}; +>>> if key/value splits, body splits +var map = {for (a in b) someLongKeyExpression: andAVeryLongValue}; +<<< +var map = { + for (a in b) + someLongKeyExpression: + andAVeryLongValue +}; \ No newline at end of file diff --git a/test/splitting/set_collection_for.stmt b/test/splitting/set_collection_for.stmt new file mode 100644 index 00000000..4d2a3995 --- /dev/null +++ b/test/splitting/set_collection_for.stmt @@ -0,0 +1,147 @@ +40 columns | +>>> split collection before for +var set = {for (a in b) somewhatLongThingHere}; +<<< +var set = { + for (a in b) somewhatLongThingHere +}; +>>> one line in multi-line +var set = {veryLongThingThatForcesASplit, for (a in b) 2, 3}; +<<< +var set = { + veryLongThingThatForcesASplit, + for (a in b) 2, + 3 +}; +>>> long body forces split +var set = {1, for (a in b) veryLongThingThatForcesASplit, 3}; +<<< +var set = { + 1, + for (a in b) + veryLongThingThatForcesASplit, + 3 +}; +>>> split inside body +var set = {1, for (a in b) veryLongThingThatForcesASplit + anotherLongThing, 3}; +<<< +var set = { + 1, + for (a in b) + veryLongThingThatForcesASplit + + anotherLongThing, + 3 +}; +>>> trailing comma +var set = {for (a in b) 2,}; +<<< +var set = { + for (a in b) 2, +}; +>>> spread list inside for stays on one line if it fits +var set = {for (a in b) ...{1, 2}}; +<<< +var set = { + for (a in b) ...{1, 2} +}; +>>> spread list inside for formats like block if it splits +var set = {for (a in b) ...{1, 2,}}; +<<< +var set = { + for (a in b) ...{ + 1, + 2, + } +}; +>>> a split collection that isn't spread wraps and indents +var set = {for (a in b) {1,2,}}; +<<< +var set = { + for (a in b) + { + 1, + 2, + } +}; +>>> lambda inside body +var set = {for (a in b) () { body; }}; +<<< +var set = { + for (a in b) + () { + body; + } +}; +>>> nested for doesn't split if it fits +var s = {for (a in b) for (c in d) t}; +<<< +var s = {for (a in b) for (c in d) t}; +>>> split collection before nested for +var set = {for (a in b) for (c in d) longThing}; +<<< +var set = { + for (a in b) for (c in d) longThing +}; +>>> just split outer for +var set = {for (a in b) for (c in d) longThingThatIsLong}; +<<< +var set = { + for (a in b) + for (c in d) longThingThatIsLong +}; +>>> split in for-in type +var set = {for (LongGenericTypeName a in b) body}; +<<< +var set = { + for (LongGenericTypeName a in b) + body +}; +>>> split in for-in expression +var set = {for (a in sequenceExpression + thatDoesNotFit) body}; +<<< +var set = { + for (a in sequenceExpression + + thatDoesNotFit) + body +}; +>>> split in for var type +var set = {for (LongGenericTypeName a = 0; a < 1; a++) body}; +<<< +var set = { + for (LongGenericTypeName a = 0; + a < 1; + a++) + body +}; +>>> split in for initializer +var set = {for (a = initializerExpression + thatDoesNotFit; a < 1; a++) body}; +<<< +var set = { + for (a = initializerExpression + + thatDoesNotFit; + a < 1; + a++) + body +}; +>>> split in for condition +var set = {for (a = b; conditionExpression + thatDoesNotFit; a++) body}; +<<< +var set = { + for (a = b; + conditionExpression + + thatDoesNotFit; + a++) + body +}; +>>> split in for increment +var set = {for (a = b; a < 1; incrementExpression + thatDoesNotFit) body}; +<<< +var set = { + for (a = b; + a < 1; + incrementExpression + + thatDoesNotFit) + body +}; \ No newline at end of file diff --git a/test/whitespace/collections.stmt b/test/whitespace/collections.stmt index bcf71199..d514da39 100644 --- a/test/whitespace/collections.stmt +++ b/test/whitespace/collections.stmt @@ -56,4 +56,40 @@ var list = [ if ( c ) 1 else 2 ,]; <<< var list = [ if (c) 1 else 2, -]; \ No newline at end of file +]; +>>> c-style for +var l = [for ( var i = 0 ; i < 1 ; i++ ) i]; +<<< +var l = [for (var i = 0; i < 1; i++) i]; +>>> empty clauses +var l = [for( ; ; ) 1]; +<<< +var l = [for (;;) 1]; +>>> empty initializer clause +var l = [for ( ; f; bar) 1]; +<<< +var l = [for (; f; bar) 1]; +>>> for-in +var l = [for (var i in i ) i]; +<<< +var l = [for (var i in i) i]; +>>> for-in with type +var l = [for (Foo f in foos) f]; +<<< +var l = [for (Foo f in foos) f]; +>>> for-in with final and type +var l = [for (final Foo f in foos) f]; +<<< +var l = [for (final Foo f in foos) f]; +>>> for-in with just final +var l = [for (final f in foos) f]; +<<< +var l = [for (final f in foos) f]; +>>> await for +f() async { + var l = [await for(x in y) x]; +} +<<< +f() async { + var l = [await for (x in y) x]; +} \ No newline at end of file From 048ced1b40a743bab5d82dd472b2007e4cc1e799 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 25 Mar 2019 13:35:46 -0700 Subject: [PATCH 2/2] Unbump the version. I'm going to wait until this works with analyzer 0.36.0 before publishing. --- bin/format.dart | 2 +- pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/format.dart b/bin/format.dart index 0d779647..04e4f0c2 100644 --- a/bin/format.dart +++ b/bin/format.dart @@ -15,7 +15,7 @@ import 'package:dart_style/src/source_code.dart'; import 'package:dart_style/src/style_fix.dart'; // Note: The following line of code is modified by tool/grind.dart. -const version = "1.2.5"; +const version = "1.2.4"; void main(List args) { var parser = new ArgParser(allowTrailingOptions: true); diff --git a/pubspec.yaml b/pubspec.yaml index ba9edba8..7269f61b 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.2.5 +version: 1.2.5-dev author: Dart Team description: Opinionated, automatic Dart source code formatter. homepage: https://github.com/dart-lang/dart_style @@ -9,7 +9,7 @@ environment: sdk: '>=2.0.0 <3.0.0' dependencies: - analyzer: '>=0.35.3 <0.37.0' + analyzer: '>=0.35.3 <0.36.0' args: '>=0.12.1 <2.0.0' path: ^1.0.0 source_span: ^1.4.0