Skip to content

Commit 53dc7e1

Browse files
authored
Add formatting for extension type declarations. (#1276)
Add formatting for `extension type` declarations.
1 parent e5144d1 commit 53dc7e1

File tree

5 files changed

+253
-3
lines changed

5 files changed

+253
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Add `tall-style` experiment flag to enable the in-progress unstable new
44
formatting style (#1253).
5+
* Format extension types.
56

67
## 2.3.3
78

bin/format.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ void main(List<String> args) async {
132132
show: show,
133133
output: output,
134134
summary: summary,
135-
setExitIfChanged: setExitIfChanged);
135+
setExitIfChanged: setExitIfChanged,
136+
experimentFlags: argResults['enable-experiment']);
136137

137138
if (argResults.rest.isEmpty) {
138139
await formatStdin(options, selection, argResults['stdin-name'] as String);

lib/src/source_visitor.dart

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,32 @@ class SourceVisitor extends ThrowingAstVisitor {
12821282
_visitBody(node.leftBracket, node.members, node.rightBracket);
12831283
}
12841284

1285+
@override
1286+
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
1287+
visitMetadata(node.metadata);
1288+
1289+
builder.nestExpression();
1290+
token(node.extensionKeyword);
1291+
space();
1292+
token(node.typeKeyword);
1293+
token(node.constKeyword, before: space);
1294+
space();
1295+
token(node.name);
1296+
1297+
builder.nestExpression();
1298+
visit(node.typeParameters);
1299+
visit(node.representation);
1300+
builder.unnest();
1301+
1302+
builder.startRule(CombinatorRule());
1303+
visit(node.implementsClause);
1304+
builder.endRule();
1305+
1306+
space();
1307+
builder.unnest();
1308+
_visitBody(node.leftBracket, node.members, node.rightBracket);
1309+
}
1310+
12851311
@override
12861312
void visitFieldDeclaration(FieldDeclaration node) {
12871313
visitMetadata(node.metadata);
@@ -2600,6 +2626,46 @@ class SourceVisitor extends ThrowingAstVisitor {
26002626
visit(node.operand);
26012627
}
26022628

2629+
@override
2630+
void visitRepresentationConstructorName(RepresentationConstructorName node) {
2631+
token(node.period);
2632+
token(node.name);
2633+
}
2634+
2635+
@override
2636+
void visitRepresentationDeclaration(RepresentationDeclaration node) {
2637+
visit(node.constructorName);
2638+
2639+
token(node.leftParenthesis);
2640+
2641+
final rule = PositionalRule(null, argumentCount: 1);
2642+
2643+
builder.startRule(rule);
2644+
rule.beforeArgument(zeroSplit());
2645+
2646+
// Make sure record and function type parameter lists are indented.
2647+
builder.startBlockArgumentNesting();
2648+
builder.startSpan();
2649+
2650+
visitParameterMetadata(node.fieldMetadata, () {
2651+
builder.startLazyRule(Rule(Cost.parameterType));
2652+
builder.nestExpression();
2653+
2654+
visit(node.fieldType);
2655+
_separatorBetweenTypeAndVariable(node.fieldType);
2656+
token(node.fieldName);
2657+
2658+
builder.unnest();
2659+
builder.endRule();
2660+
});
2661+
2662+
builder.endBlockArgumentNesting();
2663+
builder.endSpan();
2664+
builder.endRule();
2665+
2666+
token(node.rightParenthesis);
2667+
}
2668+
26032669
@override
26042670
void visitRethrowExpression(RethrowExpression node) {
26052671
token(node.rethrowKeyword);

test/utils.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,9 @@ void _testFile(
150150
pageWidth: testFile.pageWidth,
151151
indent: formatTest.leadingIndent,
152152
fixes: [...?baseFixes, ...formatTest.fixes],
153-
experimentFlags:
154-
useTallStyle ? const [tallStyleExperimentFlag] : null);
153+
experimentFlags: useTallStyle
154+
? const ['inline-class', tallStyleExperimentFlag]
155+
: const ['inline-class']);
155156

156157
var actual = formatter.formatSource(formatTest.input);
157158

test/whitespace/extension_types.unit

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
40 columns |
2+
>>> indentation
3+
extension type const A(int a) {
4+
inc(int x) => ++x;
5+
foo(int x) {
6+
if (x == 0) {
7+
return true;
8+
}}}
9+
<<<
10+
extension type const A(int a) {
11+
inc(int x) => ++x;
12+
foo(int x) {
13+
if (x == 0) {
14+
return true;
15+
}
16+
}
17+
}
18+
>>> all tokens
19+
@Anno<int, int>() extension type const A<S, T>.name(@required Map<int, int> a) implements I<S>, J<T> {}
20+
<<<
21+
@Anno<int, int>()
22+
extension type const A<S, T>.name(
23+
@required Map<int, int> a)
24+
implements I<S>, J<T> {}
25+
>>> parameter is only double-indented if there is an implements
26+
extension type const A<S, T>.name(
27+
@required Map<int, int> a) {}
28+
<<<
29+
extension type const A<S, T>.name(
30+
@required Map<int, int> a) {}
31+
>>> implements on same line if they fit
32+
extension type E(T i)
33+
implements I, J {}
34+
<<<
35+
extension type E(T i) implements I, J {}
36+
>>> trailing space inside body
37+
extension type A(int a) {
38+
}
39+
<<<
40+
extension type A(int a) {}
41+
>>> leading space before "extension type"
42+
extension type A(int a) {
43+
}
44+
<<<
45+
extension type A(int a) {}
46+
>>>
47+
extension type A(int a) { int meaningOfLife() => 42; }
48+
<<<
49+
extension type A(int a) {
50+
int meaningOfLife() => 42;
51+
}
52+
>>>
53+
extension type A ( int a ) {
54+
}
55+
<<<
56+
extension type A(int a) {}
57+
>>> comments everywhere, all retained
58+
/*a*/ extension /*b*/ type /*c*/ A
59+
/*d*/ ( /*e*/ @ /*f*/ override /*g*/ int /*h*/ a /*i*/ ) /*j*/
60+
implements /*k*/ I1 /*l*/ , /*m*/ I2 /*n*/ { /*o*/ } /*p*/
61+
<<<
62+
/*a*/ extension /*b*/ type /*c*/ A /*d*/ (
63+
/*e*/ @ /*f*/ override /*g*/
64+
int /*h*/ a /*i*/) /*j*/
65+
implements /*k*/
66+
I1 /*l*/,
67+
/*m*/ I2 /*n*/ {/*o*/} /*p*/
68+
>>> eol comments everywhere, all retained.
69+
// 0
70+
@patch // a
71+
extension // b
72+
type // c
73+
const // d
74+
A // e
75+
< // f
76+
T // g
77+
> // h
78+
. // i
79+
name // j
80+
( // k
81+
@ // l
82+
required // m
83+
int // n
84+
a // o
85+
) // p
86+
implements // q
87+
I // r
88+
{ // s
89+
} // t
90+
<<<
91+
// 0
92+
@patch // a
93+
extension // b
94+
type // c
95+
const // d
96+
A // e
97+
< // f
98+
T // g
99+
> // h
100+
. // i
101+
name // j
102+
(
103+
// k
104+
@ // l
105+
required // m
106+
int // n
107+
a // o
108+
) // p
109+
implements // q
110+
I // r
111+
{
112+
// s
113+
} // t
114+
>>> eats newlines
115+
extension
116+
117+
type
118+
119+
const
120+
121+
A
122+
123+
<
124+
125+
T
126+
127+
>
128+
129+
.
130+
131+
name
132+
133+
(
134+
135+
int
136+
137+
a
138+
139+
)
140+
141+
{
142+
143+
}
144+
<<<
145+
extension type const A<T>.name(int a) {}
146+
>>> Supports all members except instance fields
147+
extension type const A<T>.name(int a) {
148+
static const int c = 1;
149+
static final int f = 1;
150+
static late final int l;
151+
static var v;
152+
static int get g => c;
153+
static set g(int i) {}
154+
static int m<X>(X x) => c;
155+
const A(int a) : this.a = a;
156+
const A.r(int a) : this(a);
157+
const factory A.rf(int a) = A;
158+
factory A.f(int a) => A(a);
159+
int get pr => 0;
160+
set pr(int x) {}
161+
int me(int x) => x;
162+
int operator+(int x) => x;
163+
}
164+
<<<
165+
extension type const A<T>.name(int a) {
166+
static const int c = 1;
167+
static final int f = 1;
168+
static late final int l;
169+
static var v;
170+
static int get g => c;
171+
static set g(int i) {}
172+
static int m<X>(X x) => c;
173+
const A(int a) : this.a = a;
174+
const A.r(int a) : this(a);
175+
const factory A.rf(int a) = A;
176+
factory A.f(int a) => A(a);
177+
int get pr => 0;
178+
set pr(int x) {}
179+
int me(int x) => x;
180+
int operator +(int x) => x;
181+
}

0 commit comments

Comments
 (0)