Skip to content

Commit 0e79680

Browse files
authored
Format while, do-while, break, and continue statements. (#1285)
Format while, do-while, break, and continue statements. Reuses the existing formatting logic for if statements to format while statements. (A goal of the rewrite is to reuse as much formatting code as possible across different syntactic elements so that the architecture of the formatter itself ensures consistent formatting.) Do-while loops are kind of weird, so they get their own handling.
1 parent 582a717 commit 0e79680

File tree

8 files changed

+224
-4
lines changed

8 files changed

+224
-4
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import 'package:analyzer/dart/ast/visitor.dart';
66
import 'package:analyzer/source/line_info.dart';
77

88
import '../dart_formatter.dart';
9+
import '../piece/do_while.dart';
10+
import '../piece/if.dart';
911
import '../piece/piece.dart';
1012
import '../piece/variable.dart';
1113
import '../source_code.dart';
@@ -158,7 +160,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
158160

159161
@override
160162
void visitBreakStatement(BreakStatement node) {
161-
throw UnimplementedError();
163+
createBreak(node.breakKeyword, node.label, node.semicolon);
162164
}
163165

164166
@override
@@ -250,7 +252,7 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
250252

251253
@override
252254
void visitContinueStatement(ContinueStatement node) {
253-
throw UnimplementedError();
255+
createBreak(node.continueKeyword, node.label, node.semicolon);
254256
}
255257

256258
@override
@@ -270,7 +272,21 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
270272

271273
@override
272274
void visitDoStatement(DoStatement node) {
273-
throw UnimplementedError();
275+
token(node.doKeyword);
276+
writer.space();
277+
visit(node.body);
278+
writer.space();
279+
token(node.whileKeyword);
280+
var body = writer.pop();
281+
writer.split();
282+
283+
token(node.leftParenthesis);
284+
visit(node.condition);
285+
token(node.rightParenthesis);
286+
token(node.semicolon);
287+
var condition = writer.pop();
288+
289+
writer.push(DoWhilePiece(body, condition));
274290
}
275291

276292
@override
@@ -947,7 +963,20 @@ class AstNodeVisitor extends ThrowingAstVisitor<void>
947963

948964
@override
949965
void visitWhileStatement(WhileStatement node) {
950-
throw UnimplementedError();
966+
token(node.whileKeyword);
967+
writer.space();
968+
token(node.leftParenthesis);
969+
visit(node.condition);
970+
token(node.rightParenthesis);
971+
var condition = writer.pop();
972+
writer.split();
973+
974+
visit(node.body);
975+
var body = writer.pop();
976+
977+
var piece = IfPiece();
978+
piece.add(condition, body, isBlock: node.body is Block);
979+
writer.push(piece);
951980
}
952981

953982
@override

lib/src/front_end/piece_factory.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ mixin PieceFactory implements CommentWriter {
8585
alwaysSplit: forceSplit || block.statements.isNotEmpty));
8686
}
8787

88+
/// Creates a piece for a `break` or `continue` statement.
89+
void createBreak(Token keyword, SimpleIdentifier? label, Token semicolon) {
90+
token(keyword);
91+
if (label != null) {
92+
writer.space();
93+
visit(label);
94+
}
95+
token(semicolon);
96+
}
97+
8898
/// Creates a [ListPiece] for a collection literal.
8999
void createCollection(TypedLiteral literal, Token leftBracket,
90100
List<AstNode> elements, Token rightBracket) {

lib/src/piece/do_while.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
import '../back_end/code_writer.dart';
5+
import '../constants.dart';
6+
import 'piece.dart';
7+
8+
/// A piece for a do-while statement.
9+
class DoWhilePiece extends Piece {
10+
final Piece _body;
11+
final Piece _condition;
12+
13+
DoWhilePiece(this._body, this._condition);
14+
15+
@override
16+
List<State> get states => const [];
17+
18+
@override
19+
void format(CodeWriter writer, State state) {
20+
writer.setIndent(Indent.none);
21+
writer.format(_body);
22+
writer.space();
23+
writer.format(_condition);
24+
}
25+
26+
@override
27+
void forEachChild(void Function(Piece piece) callback) {
28+
callback(_body);
29+
callback(_condition);
30+
}
31+
32+
@override
33+
String toString() => 'DoWhile';
34+
}

lib/src/piece/if.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import '../constants.dart';
66
import 'piece.dart';
77

88
/// A piece for an if statement.
9+
///
10+
/// We also use this for while statements, which are formatted exactly like an
11+
/// if statement with no else clause.
912
class IfPiece extends Piece {
1013
final List<_IfSection> _sections = [];
1114

test/statement/do_while.stmt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
40 columns |
2+
>>> Empty block body.
3+
do {} while (true);
4+
<<<
5+
do {} while (true);
6+
>>> Don't split before long condition.
7+
do {
8+
;
9+
} while (aLongConditionExpressionThatWraps);
10+
<<<
11+
do {
12+
;
13+
} while (aLongConditionExpressionThatWraps);
14+
>>> Split inside condition.
15+
do {
16+
;
17+
} while (aLongCondition + expressionThatWraps);
18+
<<<
19+
do {
20+
;
21+
} while (aLongCondition +
22+
expressionThatWraps);
23+
>>> Unbraced body.
24+
do something(i); while (condition);
25+
<<<
26+
do something(i); while (condition);

test/statement/other.stmt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
40 columns |
2+
>>> Break.
3+
while (true) {
4+
break ;
5+
}
6+
<<<
7+
while (true) {
8+
break;
9+
}
10+
>>> Break to label.
11+
while (true) {
12+
break someLabel ;
13+
}
14+
<<<
15+
while (true) {
16+
break someLabel;
17+
}
18+
>>> Continue.
19+
while (true) {
20+
continue ;
21+
}
22+
<<<
23+
while (true) {
24+
continue;
25+
}
26+
>>> Continue to label.
27+
while (true) {
28+
continue someLabel ;
29+
}
30+
<<<
31+
while (true) {
32+
continue someLabel;
33+
}

test/statement/other_comment.stmt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
40 columns |
2+
>>> Break with comment after keyword.
3+
while (true) {
4+
break // comment
5+
;
6+
}
7+
<<<
8+
while (true) {
9+
break // comment
10+
;
11+
}
12+
>>> Break with comment after semicolon.
13+
while (true) {
14+
break; // comment
15+
}
16+
<<<
17+
while (true) {
18+
break; // comment
19+
}
20+
>>> Continue with comment after keyword.
21+
while (true) {
22+
break // comment
23+
;
24+
}
25+
<<<
26+
while (true) {
27+
break // comment
28+
;
29+
}
30+
>>> Continue with comment after semicolon.
31+
while (true) {
32+
break; // comment
33+
}
34+
<<<
35+
while (true) {
36+
break; // comment
37+
}

test/statement/while.stmt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
40 columns |
2+
>>> Empty block body.
3+
while (true) {}
4+
<<<
5+
while (true) {}
6+
>>> Semicolon body.
7+
while (true);
8+
<<<
9+
while (true) ;
10+
>>> Don't split before long condition.
11+
while (aLongConditionExpressionThatWraps) {
12+
;
13+
}
14+
<<<
15+
while (aLongConditionExpressionThatWraps) {
16+
;
17+
}
18+
>>> Split inside condition.
19+
while (aLongCondition + expressionThatWraps) {
20+
;
21+
}
22+
<<<
23+
while (aLongCondition +
24+
expressionThatWraps) {
25+
;
26+
}
27+
>>> Unbraced body.
28+
while (condition) something(i);
29+
<<<
30+
while (condition) something(i);
31+
>>> Split unbraced body.
32+
### This goes against the style guide, but the formatter still has to handle it.
33+
while (condition) someLong(argument, another);
34+
<<<
35+
while (condition)
36+
someLong(argument, another);
37+
>>> Split in condition forces split before body.
38+
while (veryLongCondition || veryLongExpression) body;
39+
<<<
40+
while (veryLongCondition ||
41+
veryLongExpression)
42+
body;
43+
>>> Split in body forces split before body.
44+
while (condition) veryLongExpression + anotherLongExpression;
45+
<<<
46+
while (condition)
47+
veryLongExpression +
48+
anotherLongExpression;

0 commit comments

Comments
 (0)