Skip to content

Commit 398e8c8

Browse files
Combine SequencePiece and BlockPiece. (#1369)
Combine SequencePiece and BlockPiece. SequencePiece is used whenever there is a series of AST nodes separated by mandatory newlines: block bodies, members in class bodies, the top level of a compilation unit, etc. Since the top level of a compilation unit doesn't have any enclosing curly braces or indentation, I originally designed it as two separate pieces: SequencePiece has the series of elements and it can optionally by wrapped in a BlockPiece that has the brackets and indentation. It's a little weird, though, because BlockPiece is the one that decides whether or not the contents split even though SequencePiece contains the actual contents. And it doesn't match ListPiece which uses a single unified Piece for both the delimiters and contents. So I changed it to be more like ListPiece which has both the brackets and the contents, and the brackets are optional in places where they aren't needed. I think this makes the design a little more consistent and simpler. (Also, I have some in-progress optimization work that *might* be easier with SequencePiece organized this way.) At some point, we might even be able to reuse code between SequencePiece and ListPiece (and likewise Sequencebuilder and DelimitedListBuilder), but I'm not worrying about that right now. Co-authored-by: Nate Bosch <[email protected]>
1 parent a70fc14 commit 398e8c8

File tree

5 files changed

+115
-124
lines changed

5 files changed

+115
-124
lines changed

lib/src/front_end/ast_node_visitor.dart

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import '../dart_formatter.dart';
1212
import '../piece/adjacent.dart';
1313
import '../piece/adjacent_strings.dart';
1414
import '../piece/assign.dart';
15-
import '../piece/block.dart';
1615
import '../piece/constructor.dart';
1716
import '../piece/for.dart';
1817
import '../piece/if.dart';
@@ -545,9 +544,9 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
545544

546545
// If there are members, format it like a block where each constant and
547546
// member is on its own line.
548-
var leftBracketPiece = tokenPiece(node.leftBracket);
549-
550547
var sequence = SequenceBuilder(this);
548+
sequence.leftBracket(node.leftBracket);
549+
551550
for (var constant in node.constants) {
552551
sequence.addCommentsBefore(constant.firstNonCommentToken);
553552
sequence.add(createEnumConstant(constant,
@@ -567,13 +566,9 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
567566
if (node.hasNonEmptyBody) sequence.addBlank();
568567
}
569568

570-
// Place any comments before the "}" inside the block.
571-
sequence.addCommentsBefore(node.rightBracket);
569+
sequence.rightBracket(node.rightBracket);
572570

573-
var rightBracketPiece = tokenPiece(node.rightBracket);
574-
575-
builder.add(
576-
BlockPiece(leftBracketPiece, sequence.build(), rightBracketPiece));
571+
builder.add(sequence.build());
577572
return builder.build();
578573
}
579574
}
@@ -1661,57 +1656,54 @@ class AstNodeVisitor extends ThrowingAstVisitor<Piece> with PieceFactory {
16611656

16621657
@override
16631658
Piece visitSwitchStatement(SwitchStatement node) {
1664-
var leftBracket = buildPiece((b) {
1659+
return buildPiece((b) {
16651660
b.add(startControlFlow(node.switchKeyword, node.leftParenthesis,
16661661
node.expression, node.rightParenthesis));
16671662
b.space();
1668-
b.token(node.leftBracket);
1669-
});
16701663

1671-
var sequence = SequenceBuilder(this);
1672-
for (var member in node.members) {
1673-
for (var label in member.labels) {
1674-
sequence.visit(label);
1675-
}
1664+
var sequence = SequenceBuilder(this);
1665+
sequence.leftBracket(node.leftBracket);
16761666

1677-
sequence.addCommentsBefore(member.keyword);
1667+
for (var member in node.members) {
1668+
for (var label in member.labels) {
1669+
sequence.visit(label);
1670+
}
16781671

1679-
var casePiece = buildPiece((b) {
1680-
b.token(member.keyword);
1672+
sequence.addCommentsBefore(member.keyword);
16811673

1682-
if (member is SwitchCase) {
1683-
b.space();
1684-
b.visit(member.expression);
1685-
} else if (member is SwitchPatternCase) {
1686-
if (member.guardedPattern.whenClause != null) {
1687-
throw UnimplementedError();
1688-
}
1674+
var casePiece = buildPiece((b) {
1675+
b.token(member.keyword);
16891676

1690-
b.space();
1691-
b.visit(member.guardedPattern.pattern);
1692-
} else {
1693-
assert(member is SwitchDefault);
1694-
// Nothing to do.
1695-
}
1677+
if (member is SwitchCase) {
1678+
b.space();
1679+
b.visit(member.expression);
1680+
} else if (member is SwitchPatternCase) {
1681+
if (member.guardedPattern.whenClause != null) {
1682+
throw UnimplementedError();
1683+
}
16961684

1697-
b.token(member.colon);
1698-
});
1685+
b.space();
1686+
b.visit(member.guardedPattern.pattern);
1687+
} else {
1688+
assert(member is SwitchDefault);
1689+
// Nothing to do.
1690+
}
16991691

1700-
// Don't allow any blank lines between the `case` line and the first
1701-
// statement in the case (or the next case if this case has no body).
1702-
sequence.add(casePiece, indent: Indent.none, allowBlankAfter: false);
1692+
b.token(member.colon);
1693+
});
17031694

1704-
for (var statement in member.statements) {
1705-
sequence.visit(statement, indent: Indent.block);
1706-
}
1707-
}
1695+
// Don't allow any blank lines between the `case` line and the first
1696+
// statement in the case (or the next case if this case has no body).
1697+
sequence.add(casePiece, indent: Indent.none, allowBlankAfter: false);
17081698

1709-
// Place any comments before the "}" inside the sequence.
1710-
sequence.addCommentsBefore(node.rightBracket);
1711-
var rightBracketPiece = tokenPiece(node.rightBracket);
1699+
for (var statement in member.statements) {
1700+
sequence.visit(statement, indent: Indent.block);
1701+
}
1702+
}
17121703

1713-
return BlockPiece(leftBracket, sequence.build(), rightBracketPiece,
1714-
alwaysSplit: node.members.isNotEmpty || sequence.mustSplit);
1704+
sequence.rightBracket(node.rightBracket);
1705+
b.add(sequence.build());
1706+
});
17151707
}
17161708

17171709
@override

lib/src/front_end/piece_factory.dart

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import 'package:analyzer/dart/ast/token.dart';
66

77
import '../ast_extensions.dart';
88
import '../piece/assign.dart';
9-
import '../piece/block.dart';
109
import '../piece/clause.dart';
1110
import '../piece/function.dart';
1211
import '../piece/infix.dart';
1312
import '../piece/list.dart';
1413
import '../piece/piece.dart';
1514
import '../piece/postfix.dart';
15+
import '../piece/sequence.dart';
1616
import '../piece/try.dart';
1717
import '../piece/type.dart';
1818
import '../piece/variable.dart';
@@ -64,8 +64,8 @@ mixin PieceFactory {
6464
style: const ListStyle(allowBlockElement: true));
6565
}
6666

67-
/// Creates a [BlockPiece] for a given bracket-delimited block or declaration
68-
/// body.
67+
/// Creates a [SequencePiece] for a given bracket-delimited block or
68+
/// declaration body.
6969
///
7070
/// If [forceSplit] is `true`, then the block will split even if empty. This
7171
/// is used, for example, with empty blocks in `if` statements followed by
@@ -76,9 +76,9 @@ mixin PieceFactory {
7676
Piece createBody(
7777
Token leftBracket, List<AstNode> contents, Token rightBracket,
7878
{bool forceSplit = false}) {
79-
var leftBracketPiece = tokenPiece(leftBracket);
80-
8179
var sequence = SequenceBuilder(this);
80+
sequence.leftBracket(leftBracket);
81+
8282
for (var node in contents) {
8383
sequence.visit(node);
8484

@@ -87,16 +87,11 @@ mixin PieceFactory {
8787
if (node.hasNonEmptyBody) sequence.addBlank();
8888
}
8989

90-
// Place any comments before the "}" inside the block.
91-
sequence.addCommentsBefore(rightBracket);
92-
93-
var rightBracketPiece = tokenPiece(rightBracket);
94-
95-
return BlockPiece(leftBracketPiece, sequence.build(), rightBracketPiece,
96-
alwaysSplit: forceSplit || contents.isNotEmpty || sequence.mustSplit);
90+
sequence.rightBracket(rightBracket);
91+
return sequence.build(forceSplit: forceSplit);
9792
}
9893

99-
/// Creates a [BlockPiece] for a given [Block].
94+
/// Creates a [SequencePiece] for a given [Block].
10095
///
10196
/// If [forceSplit] is `true`, then the block will split even if empty. This
10297
/// is used, for example, with empty blocks in `if` statements followed by

lib/src/front_end/sequence_builder.dart

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@ import 'piece_factory.dart';
2424
class SequenceBuilder {
2525
final PieceFactory _visitor;
2626

27+
/// The opening bracket before the elements, if any.
28+
Piece? _leftBracket;
29+
2730
/// The series of elements in the sequence.
2831
final List<SequenceElement> _elements = [];
2932

33+
/// The closing bracket after the elements, if any.
34+
Piece? _rightBracket;
35+
3036
/// Whether a blank line should be allowed after the current element.
3137
bool _allowBlank = false;
3238

@@ -35,16 +41,39 @@ class SequenceBuilder {
3541
bool _mustSplit = false;
3642
bool get mustSplit => _mustSplit;
3743

38-
SequencePiece build() => SequencePiece(_elements);
44+
SequencePiece build({bool forceSplit = false}) {
45+
var piece = SequencePiece(_elements,
46+
leftBracket: _leftBracket, rightBracket: _rightBracket);
47+
48+
if (mustSplit || forceSplit) piece.pin(State.split);
49+
50+
return piece;
51+
}
52+
53+
/// Adds the opening [bracket] to the built sequence.
54+
void leftBracket(Token bracket) {
55+
_leftBracket = _visitor.tokenPiece(bracket);
56+
}
57+
58+
/// Adds the closing [bracket] to the built sequence along with any comments
59+
/// that precede it.
60+
void rightBracket(Token bracket) {
61+
// Place any comments before the bracket inside the block.
62+
addCommentsBefore(bracket);
63+
_rightBracket = _visitor.tokenPiece(bracket);
64+
}
3965

4066
/// Adds [piece] to this sequence.
4167
///
4268
/// The caller should have already called [addCommentsBefore()] with the
4369
/// first token in [piece].
4470
void add(Piece piece, {int? indent, bool allowBlankAfter = true}) {
45-
_elements.add(SequenceElement(indent ?? Indent.none, piece));
71+
_add(indent ?? Indent.none, piece);
4672

4773
_allowBlank = allowBlankAfter;
74+
75+
// There is at least one non-comment element in the sequence, so it splits.
76+
_mustSplit = true;
4877
}
4978

5079
/// Visits [node] and adds the resulting [Piece] to this sequence, handling
@@ -98,7 +127,7 @@ class SequenceBuilder {
98127
}
99128

100129
// Write the comment as its own sequence piece.
101-
add(comment);
130+
_add(Indent.none, comment);
102131
}
103132
}
104133

@@ -108,4 +137,11 @@ class SequenceBuilder {
108137
// Write a blank before the token if there should be one.
109138
if (comments.linesBeforeNextToken > 1) addBlank();
110139
}
140+
141+
void _add(int indent, Piece piece) {
142+
// If there are brackets, add a level of block indentation.
143+
if (_leftBracket != null) indent += Indent.block;
144+
145+
_elements.add(SequenceElement(indent, piece));
146+
}
111147
}

lib/src/piece/block.dart

Lines changed: 0 additions & 58 deletions
This file was deleted.

lib/src/piece/sequence.dart

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,40 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import '../back_end/code_writer.dart';
6+
import '../constants.dart';
67
import 'piece.dart';
78

89
/// A piece for a series of statements or members inside a block or declaration
910
/// body.
1011
///
1112
/// Usually constructed using a [SequenceBuilder].
1213
class SequencePiece extends Piece {
14+
/// The opening delimiter, if any.
15+
final Piece? _leftBracket;
16+
1317
/// The series of members or statements.
1418
final List<SequenceElement> _elements;
1519

16-
SequencePiece(this._elements);
20+
SequencePiece(this._elements, {Piece? leftBracket, Piece? rightBracket})
21+
: _leftBracket = leftBracket,
22+
_rightBracket = rightBracket;
23+
24+
/// The closing delimiter, if any.
25+
final Piece? _rightBracket;
1726

18-
/// Whether this sequence has any contents.
19-
bool get isNotEmpty => _elements.isNotEmpty;
27+
@override
28+
List<State> get additionalStates => [if (_elements.isNotEmpty) State.split];
2029

2130
@override
2231
void format(CodeWriter writer, State state) {
32+
writer.setAllowNewlines(state == State.split);
33+
34+
if (_leftBracket case var leftBracket?) {
35+
writer.format(leftBracket);
36+
writer.splitIf(state == State.split,
37+
space: false, indent: _elements.firstOrNull?.indent ?? 0);
38+
}
39+
2340
for (var i = 0; i < _elements.length; i++) {
2441
var element = _elements[i];
2542
writer.format(element.piece);
@@ -34,16 +51,25 @@ class SequencePiece extends Piece {
3451
blank: element.blankAfter, indent: _elements[i + 1].indent);
3552
}
3653
}
54+
55+
if (_rightBracket case var rightBracket?) {
56+
writer.splitIf(state == State.split, space: false, indent: Indent.none);
57+
writer.format(rightBracket);
58+
}
3759
}
3860

3961
@override
4062
void forEachChild(void Function(Piece piece) callback) {
63+
if (_leftBracket case var leftBracket?) callback(leftBracket);
64+
4165
for (var element in _elements) {
4266
callback(element.piece);
4367
for (var comment in element.hangingComments) {
4468
callback(comment);
4569
}
4670
}
71+
72+
if (_rightBracket case var rightBracket?) callback(rightBracket);
4773
}
4874

4975
@override

0 commit comments

Comments
 (0)