Skip to content

Commit 295e4c5

Browse files
authored
Format record type annotations. (#1346)
* Format record type annotations. * Use local instead. * Rebase. * Fix comment and use token() * Added createRecordTypeField * Extracted parameter splitting to a helper. * Add test for splitting between type and name. * Revert class.unit * Updated comment. * Rebase merge changes.
1 parent f88d826 commit 295e4c5

File tree

5 files changed

+350
-21
lines changed

5 files changed

+350
-21
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,19 +1430,59 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
14301430

14311431
@override
14321432
Piece visitRecordTypeAnnotation(RecordTypeAnnotation node) {
1433-
throw UnimplementedError();
1433+
var namedFields = node.namedFields;
1434+
var positionalFields = node.positionalFields;
1435+
1436+
// Single positional record types always have a trailing comma.
1437+
var listStyle = positionalFields.length == 1 && namedFields == null
1438+
? const ListStyle(commas: Commas.alwaysTrailing)
1439+
: const ListStyle(commas: Commas.trailing);
1440+
var builder = DelimitedListBuilder(this, listStyle);
1441+
1442+
// If all parameters are optional, put the `{` right after `(`.
1443+
if (positionalFields.isEmpty && namedFields != null) {
1444+
builder.leftBracket(
1445+
node.leftParenthesis,
1446+
delimiter: namedFields.leftBracket,
1447+
);
1448+
} else {
1449+
builder.leftBracket(node.leftParenthesis);
1450+
}
1451+
1452+
for (var positionalField in positionalFields) {
1453+
builder.visit(positionalField);
1454+
}
1455+
1456+
Token? rightDelimiter;
1457+
if (namedFields != null) {
1458+
// If we have both positional fields and named fields, then we need to add
1459+
// the left bracket delimiter before the first named field.
1460+
if (positionalFields.isNotEmpty) {
1461+
builder.leftDelimiter(namedFields.leftBracket);
1462+
}
1463+
for (var namedField in namedFields.fields) {
1464+
builder.visit(namedField);
1465+
}
1466+
rightDelimiter = namedFields.rightBracket;
1467+
}
1468+
1469+
builder.rightBracket(node.rightParenthesis, delimiter: rightDelimiter);
1470+
return buildPiece((b) {
1471+
b.add(builder.build());
1472+
b.token(node.question);
1473+
});
14341474
}
14351475

14361476
@override
14371477
Piece visitRecordTypeAnnotationNamedField(
14381478
RecordTypeAnnotationNamedField node) {
1439-
throw UnimplementedError();
1479+
return createRecordTypeField(node);
14401480
}
14411481

14421482
@override
14431483
Piece visitRecordTypeAnnotationPositionalField(
14441484
RecordTypeAnnotationPositionalField node) {
1445-
throw UnimplementedError();
1485+
return createRecordTypeField(node);
14461486
}
14471487

14481488
@override

lib/src/front_end/piece_factory.dart

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -356,24 +356,14 @@ mixin PieceFactory {
356356
var builder = AdjacentBuilder(this);
357357
startFormalParameter(node, builder);
358358
builder.modifier(mutableKeyword);
359-
builder.visit(type);
360-
361-
Piece? typePiece;
362-
if (type != null && name != null) {
363-
typePiece = builder.build();
364-
}
365-
366-
builder.token(fieldKeyword);
367-
builder.token(period);
368-
builder.token(name);
369-
370-
// If we have both a type and name, allow splitting between them.
371-
if (typePiece != null) {
372-
var namePiece = builder.build();
373-
return VariablePiece(typePiece, [namePiece], hasType: true);
374-
}
375-
376-
return builder.build();
359+
return finishTypeAndName(
360+
type,
361+
name,
362+
builder,
363+
mutableKeyword: mutableKeyword,
364+
fieldKeyword: fieldKeyword,
365+
period: period,
366+
);
377367
}
378368

379369
/// Creates a function, method, getter, or setter declaration.
@@ -673,6 +663,17 @@ mixin PieceFactory {
673663
return builder.build();
674664
}
675665

666+
/// Creates an [AdjacentPiece] for a given record type field.
667+
Piece createRecordTypeField(RecordTypeAnnotationField node) {
668+
// TODO(tall): Format metadata.
669+
if (node.metadata.isNotEmpty) throw UnimplementedError();
670+
return finishTypeAndName(
671+
node.type,
672+
node.name,
673+
AdjacentBuilder(this),
674+
);
675+
}
676+
676677
/// Creates a class, enum, extension, mixin, or mixin application class
677678
/// declaration.
678679
///
@@ -784,6 +785,34 @@ mixin PieceFactory {
784785
style: const ListStyle(commas: Commas.nonTrailing, splitCost: 3));
785786
}
786787

788+
/// Creates a [VariablePiece] that allows splitting between a type and a name,
789+
/// if they both exist.
790+
///
791+
/// Otherwise, finishes building the existing [AdjacentPiece] with the
792+
/// [builder].
793+
Piece finishTypeAndName(
794+
TypeAnnotation? type, Token? name, AdjacentBuilder builder,
795+
{Token? mutableKeyword, Token? fieldKeyword, Token? period}) {
796+
builder.visit(type);
797+
798+
Piece? typePiece;
799+
if (type != null && name != null) {
800+
typePiece = builder.build();
801+
}
802+
803+
builder.token(fieldKeyword);
804+
builder.token(period);
805+
builder.token(name);
806+
807+
// If we have both a type and name, allow splitting between them.
808+
if (typePiece != null) {
809+
var namePiece = builder.build();
810+
return VariablePiece(typePiece, [namePiece], hasType: true);
811+
}
812+
813+
return builder.build();
814+
}
815+
787816
/// Writes the parts of a formal parameter shared by all formal parameter
788817
/// types: metadata, `covariant`, etc.
789818
void startFormalParameter(

test/type/function.stmt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,56 @@ longMethod({
218218
required int
219219
reallyLongParameterNameWow,
220220
}) {}
221+
>>> Record type with multiple fields in parameter has no trailing comma.
222+
function((TypeName, TypeName) parameter) { ; }
223+
<<<
224+
function(
225+
(TypeName, TypeName) parameter,
226+
) {
227+
;
228+
}
229+
>>> Split single long positional record type field.
230+
function((VeryLongTypeName___________________,) param) {;}
231+
<<<
232+
function(
233+
(
234+
VeryLongTypeName___________________,
235+
) param,
236+
) {
237+
;
238+
}
239+
>>> Split inside parameter list with record type.
240+
function((TypeName, TypeName, TypeName, TypeName, TypeName) record) {;}
241+
<<<
242+
function(
243+
(
244+
TypeName,
245+
TypeName,
246+
TypeName,
247+
TypeName,
248+
TypeName,
249+
) record,
250+
) {
251+
;
252+
}
253+
>>> Single positional has a trailing comma inside parameter list with record type.
254+
function((TypeName,) record) {;}
255+
<<<
256+
function((TypeName,) record) {
257+
;
258+
}
259+
>>> Named parameter has no trailing comma inside parameter list with record type.
260+
function(({TypeName param,}) record) {;}
261+
<<<
262+
function(({TypeName param}) record) {
263+
;
264+
}
265+
>>> Multiple positional fields have no trailing comma in parameter list with record type.
266+
function((TypeName,TypeName,) record,) {;}
267+
<<<
268+
function((TypeName, TypeName) record) {
269+
;
270+
}
221271
>>> Unsplit generic method instantiation.
222272
void main() => id < int > ;
223273
<<<

test/type/record.stmt

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
40 columns |
2+
>>> Empty record.
3+
( ) x;
4+
<<<
5+
() x;
6+
>>> Empty nullable record type.
7+
( ) ? x;
8+
<<<
9+
()? x;
10+
>>> Nullable record type.
11+
( int , bool ) ? x;
12+
<<<
13+
(int, bool)? x;
14+
>>> Single positional field.
15+
( int , ) x;
16+
<<<
17+
(int,) x;
18+
>>> Single named field.
19+
( { int n } ) x;
20+
<<<
21+
({int n}) x;
22+
>>> Named positional fields.
23+
( int value , String label) x;
24+
<<<
25+
(int value, String label) x;
26+
>>> Unnamed positional fields.
27+
( int , String ) x;
28+
<<<
29+
(int, String) x;
30+
>>> Named fields.
31+
( { int value , String label } ) x;
32+
<<<
33+
({int value, String label}) x;
34+
>>> Split between the type and the name.
35+
( VeryVeryLongType_____ veryLongName___________________ , ) x;
36+
<<<
37+
(
38+
VeryVeryLongType_____
39+
veryLongName___________________,
40+
) x;
41+
>>> Split named positional fields.
42+
( int longValue , String veryVeryLongLabel , ) x;
43+
<<<
44+
(
45+
int longValue,
46+
String veryVeryLongLabel,
47+
) x;
48+
>>> Unsplit unnamed positional fields have no trailing comma.
49+
( int , String , ) x;
50+
<<<
51+
(int, String) x;
52+
>>> Split only named fields.
53+
( { int longValue , String veryLongLabel , } ) x;
54+
<<<
55+
({
56+
int longValue,
57+
String veryLongLabel,
58+
}) x;
59+
>>> Empty record types don't split.
60+
someLongFunctionName__________________(() x) {}
61+
<<<
62+
someLongFunctionName__________________(
63+
() x,
64+
) {}
65+
>>> Unsplit short single positional field.
66+
(TypeName,
67+
) x;
68+
<<<
69+
(TypeName,) x;
70+
>>> Unsplit single positional field.
71+
(VeryLongTypeName________________,) x;
72+
<<<
73+
(VeryLongTypeName________________,) x;
74+
>>> Split positional types.
75+
(TypeName,TypeName,TypeName,TypeName) x;
76+
<<<
77+
(
78+
TypeName,
79+
TypeName,
80+
TypeName,
81+
TypeName,
82+
) x;
83+
>>> Split named types.
84+
({TypeName a,TypeName b,TypeName c,TypeName d}) x;
85+
<<<
86+
({
87+
TypeName a,
88+
TypeName b,
89+
TypeName c,
90+
TypeName d,
91+
}) x;
92+
>>> Split named if positional splits.
93+
(TypeName,TypeName,TypeName,TypeName,{TypeName a,TypeName b}) x;
94+
<<<
95+
(
96+
TypeName,
97+
TypeName,
98+
TypeName,
99+
TypeName, {
100+
TypeName a,
101+
TypeName b,
102+
}) x;
103+
>>> Split positional if named splits.
104+
(TypeName,TypeName,{TypeName a,TypeName b,TypeName c,TypeName d}) x;
105+
<<<
106+
(
107+
TypeName,
108+
TypeName, {
109+
TypeName a,
110+
TypeName b,
111+
TypeName c,
112+
TypeName d,
113+
}) x;
114+
>>> Single named field has no trailing comma.
115+
({int n,}) x;
116+
<<<
117+
({int n}) x;
118+
>>> Multiple positional fields have no trailing comma.
119+
(int m, int n,) x;
120+
<<<
121+
(int m, int n) x;
122+
>>> Split outer record if inner record splits.
123+
((TypeName,TypeName,TypeName,TypeName),TypeName) x;
124+
<<<
125+
(
126+
(
127+
TypeName,
128+
TypeName,
129+
TypeName,
130+
TypeName,
131+
),
132+
TypeName,
133+
) x;
134+
>>> Split outer type argument list if inner record splits.
135+
Map<String, (TypeName,TypeName,TypeName,TypeName)> map;
136+
<<<
137+
Map<
138+
String,
139+
(
140+
TypeName,
141+
TypeName,
142+
TypeName,
143+
TypeName,
144+
)
145+
> map;

0 commit comments

Comments
 (0)