Skip to content

Commit a9115cd

Browse files
authored
Simplify and unify how delimited bodies are handled. (#1113)
For mostly historical reasons, SourceVisitor used to create a flat list of Chunks for some brace-delimited bodies like block statements and switch bodies while using nested BlockChunks for function expression bodies. This now uniformly uses nested blocks for all of them. The visible behavior is mostly unchanged except in a rare corner case with type bodies containing only inline block comments where the old formatter would arbitrarily split in one case but not the other. Now its behavior is more consistent.
1 parent 1056428 commit a9115cd

File tree

6 files changed

+116
-194
lines changed

6 files changed

+116
-194
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# 2.2.4-dev
22

3+
* Unify how brace-delimited syntax is formatted. This is mostly an internal
4+
refactoring, but slightly changes how a type body containing only an inline
5+
block comment is formatted.
6+
37
* Refactor Chunk to store split before text instead of after. This mostly does
48
not affect the visible behavior of the formatter, but a few edge cases are
59
handled slightly differently. These are all bug fixes where the previous

lib/src/ast_extensions.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ extension AstNodeExtensions on AstNode {
2222
/// Whether there is a comma token immediately following this.
2323
bool get hasCommaAfter => commaAfter != null;
2424

25+
/// Whether this node is a statement or member with a braced body that isn't
26+
/// empty.
27+
///
28+
/// Used to determine if a blank line should be inserted after the node.
29+
bool get hasNonEmptyBody {
30+
AstNode? body;
31+
var node = this;
32+
if (node is MethodDeclaration) {
33+
body = node.body;
34+
} else if (node is FunctionDeclarationStatement) {
35+
body = node.functionDeclaration.functionExpression.body;
36+
}
37+
38+
return body is BlockFunctionBody && body.block.statements.isNotEmpty;
39+
}
40+
2541
bool get isControlFlowElement => this is IfElement || this is ForElement;
2642

2743
/// Whether this is immediately contained within an anonymous

lib/src/chunk_builder.dart

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -581,12 +581,14 @@ class ChunkBuilder {
581581
///
582582
/// Forces the chunk that owns the block to split if it can tell that the
583583
/// block contents will always split. It does that by looking for hard splits
584-
/// in the block. If [ignoredSplit] is given, that rule will be ignored
585-
/// when determining if a block contains a hard split. If [forceSplit] is
586-
/// `true`, the block is considered to always split.
584+
/// in the block. If [bodyRule] is given, that rule will be ignored when
585+
/// determining if a block contains a hard split. If [space] is `true`, the
586+
/// split at the end of the block will get a space when unsplit. If
587+
/// [forceSplit] is `true`, the block always splits.
587588
///
588589
/// Returns the previous writer for the surrounding block.
589-
ChunkBuilder endBlock(Rule? ignoredSplit, {required bool forceSplit}) {
590+
ChunkBuilder endBlock(
591+
{Rule? bodyRule, bool space = false, bool forceSplit = true}) {
590592
_divideChunks();
591593

592594
// If the last chunk ends with a comment that wants a newline after it,
@@ -609,31 +611,30 @@ class ChunkBuilder {
609611
// code), then force the collection to split.
610612
if (chunk != _chunks.first &&
611613
chunk.rule.isHardened &&
612-
chunk.rule != ignoredSplit) {
614+
chunk.rule != bodyRule) {
613615
forceSplit = true;
614616
break;
615617
}
616618
}
617619
}
618620

619-
if (forceSplit) {
620-
forceRules();
621-
}
621+
if (forceSplit) forceRules();
622622

623-
_parent!._endChildBlock(forceSplit: forceSplit);
624-
return _parent!;
623+
var parent = _parent!;
624+
parent._endChildBlock(space: space, forceSplit: forceSplit);
625+
return parent;
625626
}
626627

627628
/// Finishes off the last chunk in a child block of this parent.
628-
void _endChildBlock({required bool forceSplit}) {
629+
void _endChildBlock({required bool space, required bool forceSplit}) {
629630
// If there is a hard newline within the block, force the surrounding rule
630631
// for it so that we apply that constraint.
631632
if (forceSplit) forceRules();
632633

633634
// Start a new chunk for the code after the block contents. The split at
634635
// the beginning of this chunk also determines whether the preceding block
635636
// splits and, if so, how it is indented.
636-
_startChunk(_blockArgumentNesting.last, isHard: false);
637+
_startChunk(_blockArgumentNesting.last, isHard: false, space: space);
637638

638639
if (rule.isHardened) _handleHardSplit();
639640
}

0 commit comments

Comments
 (0)