Skip to content

Commit 4b77cd8

Browse files
authored
Support primary constructors in short style. (#1823)
* Support primary constructors in short style. * Update name part. * Enum tests. * Remove lines. * Formatting for enums and adding helpers. * Refactor visitors.
1 parent f510a0a commit 4b77cd8

8 files changed

Lines changed: 183 additions & 74 deletions

File tree

lib/src/short/source_visitor.dart

Lines changed: 83 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,59 @@ final class SourceVisitor extends ThrowingAstVisitor {
379379
_visitBody(node.leftBracket, node.statements, node.rightBracket);
380380
}
381381

382+
@override
383+
void visitBlockClassBody(BlockClassBody node) {
384+
_visitBody(node.leftBracket, node.members, node.rightBracket);
385+
}
386+
387+
@override
388+
void visitBlockEnumBody(BlockEnumBody node) {
389+
_beginBody(node.leftBracket, space: true);
390+
391+
visitCommaSeparatedNodes(node.constants, between: splitOrTwoNewlines);
392+
393+
// If there is a trailing comma, always force the constants to split.
394+
var trailingComma = node.constants.last.commaAfter;
395+
if (trailingComma != null) {
396+
builder.forceRules();
397+
}
398+
399+
// The ";" after the constants, which may occur after a trailing comma.
400+
var afterConstants = node.constants.last.endToken.next!;
401+
Token? semicolon;
402+
if (afterConstants.type == TokenType.SEMICOLON) {
403+
semicolon = node.constants.last.endToken.next!;
404+
} else if (trailingComma != null &&
405+
trailingComma.next!.type == TokenType.SEMICOLON) {
406+
semicolon = afterConstants.next!;
407+
}
408+
409+
if (semicolon != null) {
410+
// If there is both a trailing comma and a semicolon, move the semicolon
411+
// to the next line. This doesn't look great but it's less bad than being
412+
// next to the comma.
413+
// TODO(rnystrom): If the formatter starts making non-whitespace changes
414+
// like adding/removing trailing commas, then it should fix this too.
415+
if (trailingComma != null) newline();
416+
417+
token(semicolon);
418+
419+
// Put a blank line between the constants and members.
420+
if (node.members.isNotEmpty) twoNewlines();
421+
}
422+
423+
_visitBodyContents(node.members);
424+
425+
_endBody(
426+
node.rightBracket,
427+
forceSplit:
428+
semicolon != null ||
429+
trailingComma != null ||
430+
node.members.isNotEmpty ||
431+
node.constants.containsLineComments(),
432+
);
433+
}
434+
382435
@override
383436
void visitBlockFunctionBody(BlockFunctionBody node) {
384437
// Space after the parameter list.
@@ -599,17 +652,14 @@ final class SourceVisitor extends ThrowingAstVisitor {
599652
modifier(node.mixinKeyword);
600653
token(node.classKeyword);
601654
space();
602-
token(node.namePart.typeName);
603-
visit(node.namePart.typeParameters);
655+
visit(node.namePart);
604656
visit(node.extendsClause);
605657
_visitClauses(node.withClause, node.implementsClause);
606658
visit(node.nativeClause, before: space);
607-
space();
608-
659+
if (node.body is! EmptyClassBody) space();
609660
builder.unnest();
610-
// TODO(scheglov): support for EmptyBody
611-
var body = node.body as BlockClassBody;
612-
_visitBody(body.leftBracket, body.members, body.rightBracket);
661+
662+
visit(node.body);
613663
}
614664

615665
@override
@@ -977,6 +1027,16 @@ final class SourceVisitor extends ThrowingAstVisitor {
9771027
token(node.literal);
9781028
}
9791029

1030+
@override
1031+
void visitEmptyClassBody(EmptyClassBody node) {
1032+
token(node.semicolon);
1033+
}
1034+
1035+
@override
1036+
void visitEmptyEnumBody(EmptyEnumBody node) {
1037+
token(node.semicolon);
1038+
}
1039+
9801040
@override
9811041
void visitEmptyFunctionBody(EmptyFunctionBody node) {
9821042
token(node.semicolon);
@@ -1015,64 +1075,13 @@ final class SourceVisitor extends ThrowingAstVisitor {
10151075
builder.nestExpression();
10161076
token(node.enumKeyword);
10171077
space();
1018-
token(node.namePart.typeName);
1019-
visit(node.namePart.typeParameters);
1078+
visit(node.namePart);
10201079
_visitClauses(node.withClause, node.implementsClause);
1021-
space();
10221080

1081+
if (node.body is! EmptyEnumBody) space();
10231082
builder.unnest();
10241083

1025-
// TODO(scheglov): support for EmptyEnumBody
1026-
var body = node.body as BlockEnumBody;
1027-
_beginBody(body.leftBracket, space: true);
1028-
1029-
visitCommaSeparatedNodes(body.constants, between: splitOrTwoNewlines);
1030-
1031-
// If there is a trailing comma, always force the constants to split.
1032-
var trailingComma = body.constants.last.commaAfter;
1033-
if (trailingComma != null) {
1034-
builder.forceRules();
1035-
}
1036-
1037-
// The ";" after the constants, which may occur after a trailing comma.
1038-
var afterConstants = body.constants.last.endToken.next!;
1039-
Token? semicolon;
1040-
if (afterConstants.type == TokenType.SEMICOLON) {
1041-
semicolon = body.constants.last.endToken.next!;
1042-
} else if (trailingComma != null &&
1043-
trailingComma.next!.type == TokenType.SEMICOLON) {
1044-
semicolon = afterConstants.next!;
1045-
}
1046-
1047-
if (semicolon != null) {
1048-
// If there is both a trailing comma and a semicolon, move the semicolon
1049-
// to the next line. This doesn't look great but it's less bad than being
1050-
// next to the comma.
1051-
// TODO(rnystrom): If the formatter starts making non-whitespace changes
1052-
// like adding/removing trailing commas, then it should fix this too.
1053-
if (trailingComma != null) newline();
1054-
1055-
token(semicolon);
1056-
1057-
// Put a blank line between the constants and members.
1058-
if (body.members.isNotEmpty) twoNewlines();
1059-
}
1060-
1061-
_visitBodyContents(body.members);
1062-
1063-
_endBody(
1064-
body.rightBracket,
1065-
forceSplit:
1066-
semicolon != null ||
1067-
trailingComma != null ||
1068-
body.members.isNotEmpty ||
1069-
// If there is a line comment after an enum constant, it won't
1070-
// automatically force the enum body to split since the rule for
1071-
// the constants is the hard rule used by the entire block and its
1072-
// hardening state doesn't actually change. Instead, look
1073-
// explicitly for a line comment here.
1074-
body.constants.containsLineComments(),
1075-
);
1084+
visit(node.body);
10761085
}
10771086

10781087
@override
@@ -1166,12 +1175,10 @@ final class SourceVisitor extends ThrowingAstVisitor {
11661175
space();
11671176
visit(onClause.extendedType);
11681177
}
1169-
space();
1178+
if (node.body is! EmptyClassBody) space();
11701179
builder.unnest();
11711180

1172-
// TODO(scheglov): support for EmptyBody
1173-
var body = node.body as BlockClassBody;
1174-
_visitBody(body.leftBracket, body.members, body.rightBracket);
1181+
visit(node.body);
11751182
}
11761183

11771184
@override
@@ -1189,11 +1196,10 @@ final class SourceVisitor extends ThrowingAstVisitor {
11891196
visit(node.implementsClause);
11901197
builder.endRule();
11911198

1192-
space();
1199+
if (node.body is! EmptyClassBody) space();
11931200
builder.unnest();
1194-
// TODO(scheglov): support for EmptyBody
1195-
var body = node.body as BlockClassBody;
1196-
_visitBody(body.leftBracket, body.members, body.rightBracket);
1201+
1202+
visit(node.body);
11971203
}
11981204

11991205
@override
@@ -2139,13 +2145,16 @@ final class SourceVisitor extends ThrowingAstVisitor {
21392145
visit(node.implementsClause);
21402146
builder.endRule();
21412147

2142-
space();
2143-
2148+
if (node.body is! EmptyClassBody) space();
21442149
builder.unnest();
21452150

2146-
// TODO(scheglov): support for EmptyBody
2147-
var body = node.body as BlockClassBody;
2148-
_visitBody(body.leftBracket, body.members, body.rightBracket);
2151+
visit(node.body);
2152+
}
2153+
2154+
@override
2155+
void visitNameWithTypeParameters(NameWithTypeParameters node) {
2156+
token(node.typeName);
2157+
visit(node.typeParameters);
21492158
}
21502159

21512160
@override

test/short/splitting/classes.unit

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,20 @@ class TooLongClassName extends Another {}
134134
<<<
135135
class TooLongClassName
136136
extends Another {}
137+
>>> (experiment primary-constructors) Long primary constructor parameter list splits
138+
class LongClassName(final int veryLongParameterName, String anotherLongParameter) {}
139+
<<<
140+
class LongClassName(
141+
final int veryLongParameterName,
142+
String anotherLongParameter) {}
143+
>>> (experiment primary-constructors) Long const primary constructor splits
144+
class const LongClassName(final int veryLongParameterName, String anotherLongParameter);
145+
<<<
146+
class const LongClassName(
147+
final int veryLongParameterName,
148+
String anotherLongParameter);
149+
>>> (experiment primary-constructors) Primary constructor clauses split
150+
class C(int x, int y) extends LongBaseClass with Mixin {}
151+
<<<
152+
class C(int x, int y)
153+
extends LongBaseClass with Mixin {}

test/short/splitting/type_parameters.unit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,9 @@ class Foo extends GenericBaseClass<TypeParameter> {}
5252
<<<
5353
class Foo extends GenericBaseClass<
5454
TypeParameter> {}
55+
>>> (experiment primary-constructors) Split type parameters with primary constructor and empty body
56+
class LongClassName<FirstTypeParameterIsLong, Second>(int x);
57+
<<<
58+
class LongClassName<
59+
FirstTypeParameterIsLong,
60+
Second>(int x);

test/short/whitespace/classes.unit

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,27 @@ abstract mixin class C12 = Object
244244
with Mixin;
245245
abstract base mixin class C13 = Object
246246
with Mixin;
247+
>>> (experiment primary-constructors) Primary constructor with empty body
248+
class C(int x);
249+
<<<
250+
class C(int x);
251+
>>> (experiment primary-constructors) Primary constructor with block body
252+
class C(int x) {}
253+
<<<
254+
class C(int x) {}
255+
>>> (experiment primary-constructors) Abstract class with primary constructor and empty body
256+
abstract class C(int x);
257+
<<<
258+
abstract class C(int x);
259+
>>> (experiment primary-constructors) Primary constructor with multiple parameters and modifiers
260+
class C(var int x, final String y);
261+
<<<
262+
class C(var int x, final String y);
263+
>>> (experiment primary-constructors) Primary constructor with comment before semicolon
264+
class C(int x) /* comment */ ;
265+
<<<
266+
class C(int x) /* comment */;
267+
>>> (experiment primary-constructors) Const primary constructor
268+
class const C(final int x);
269+
<<<
270+
class const C(final int x);

test/short/whitespace/enums.unit

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,32 @@ enum E {
312312

313313
bar() {}
314314
}
315+
>>> (experiment primary-constructors) Enum with primary constructor and single constant
316+
enum E(final int x) { a(1) }
317+
<<<
318+
enum E(final int x) { a(1) }
319+
>>> (experiment primary-constructors) Enum with primary constructor and multiple constants
320+
enum E(final int x) { a(1), b(2) }
321+
<<<
322+
enum E(final int x) { a(1), b(2) }
323+
>>> (experiment primary-constructors) Enum with primary constructor and members
324+
enum E(final int x) { a(1), b(2); void foo() {} }
325+
<<<
326+
enum E(final int x) {
327+
a(1),
328+
b(2);
329+
330+
void foo() {}
331+
}
332+
>>> (experiment primary-constructors) Const enum with primary constructor
333+
enum const E(final int x) { a(1) }
334+
<<<
335+
enum const E(final int x) { a(1) }
336+
>>> (experiment primary-constructors) Enum with semicolon body
337+
enum E;
338+
<<<
339+
enum E;
340+
>>> (experiment primary-constructors) Enum with primary constructor and semicolon body
341+
enum E(int x);
342+
<<<
343+
enum E(int x);

test/short/whitespace/extension_types.unit

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,11 @@ extension type const A<T>.name(int a) {
179179
int me(int x) => x;
180180
int operator +(int x) => x;
181181
}
182+
>>> (experiment primary-constructors) Extension type with empty body
183+
extension type E(int x);
184+
<<<
185+
extension type E(int x);
186+
>>> (experiment primary-constructors) Extension type with comment before semicolon
187+
extension type E(int x) /* comment */ ;
188+
<<<
189+
extension type E(int x) /* comment */;

test/short/whitespace/extensions.unit

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,11 @@ extension A on B {
131131
extension A on B {
132132
bar() {}
133133
}
134+
>>> (experiment primary-constructors) Extension with empty body
135+
extension A on B;
136+
<<<
137+
extension A on B;
138+
>>> (experiment primary-constructors) Extension with comment before semicolon
139+
extension A on B /* comment */ ;
140+
<<<
141+
extension A on B /* comment */;

test/short/whitespace/mixins.unit

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,11 @@ mixin M<A, B> on C implements D {}
2929
base mixin M {}
3030
<<<
3131
base mixin M {}
32+
>>> (experiment primary-constructors) Mixin with empty body
33+
mixin M;
34+
<<<
35+
mixin M;
36+
>>> (experiment primary-constructors) Mixin with comment before semicolon
37+
mixin M /* comment */ ;
38+
<<<
39+
mixin M /* comment */;

0 commit comments

Comments
 (0)