diff --git a/CHANGELOG.md b/CHANGELOG.md index 750175fc..3d3b1062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 3.0.1-wip * Handle trailing commas in for-loop updaters (#1354). +* Format `||` patterns like fallthrough cases in switch expressions (#1602). * Handle comments and metadata before variables more gracefully (#1604). * Ensure comment formatting is idempotent (#1606). * Better indentation of leading comments on property accesses in binary operator diff --git a/lib/src/piece/case.dart b/lib/src/piece/case.dart index b17a0ab7..6867b1da 100644 --- a/lib/src/piece/case.dart +++ b/lib/src/piece/case.dart @@ -8,13 +8,20 @@ import 'piece.dart'; /// Piece for a case pattern, guard, and body in a switch expression. final class CaseExpressionPiece extends Piece { + /// Split inside the body, which must be block formattable, like: + /// + /// pattern => function( + /// argument, + /// ), + static const State _blockSplitBody = State(1, cost: 0); + /// Split after the `=>` before the body. - static const State _beforeBody = State(1); + static const State _beforeBody = State(2); /// Split before the `when` guard clause and after the `=>`. - static const State _beforeWhenAndBody = State(2); + static const State _beforeWhenAndBody = State(3); - /// The pattern the value is matched against along with the leading `case`. + /// The pattern the value is matched against. final Piece _pattern; /// If there is a `when` clause, that clause. @@ -54,6 +61,7 @@ final class CaseExpressionPiece extends Piece { @override List get additionalStates => [ + if (_canBlockSplitBody) _blockSplitBody, _beforeBody, if (_guard != null) ...[_beforeWhenAndBody], ]; @@ -61,10 +69,15 @@ final class CaseExpressionPiece extends Piece { @override bool allowNewlineInChild(State state, Piece child) { return switch (state) { + // If the outermost pattern is `||`, then always let it split even while + // allowing the body on the same line as `=>`. + _ when child == _pattern && _patternIsLogicalOr => true, + + // There are almost never splits in the arrow piece. It requires a comment + // in a funny location, but if it happens, allow it. _ when child == _arrow => true, - State.unsplit when child == _body && _canBlockSplitBody => true, - _beforeBody when child == _pattern => - _guard == null || _patternIsLogicalOr, + _blockSplitBody when child == _body => true, + _beforeBody when child == _pattern => _guard == null, _beforeBody when child == _body => true, _beforeWhenAndBody => true, _ => false, @@ -94,12 +107,13 @@ final class CaseExpressionPiece extends Piece { writer.space(); writer.format(_arrow); - if (state != State.unsplit) writer.pushIndent(Indent.block); + var indentBody = state != State.unsplit && state != _blockSplitBody; + if (indentBody) writer.pushIndent(Indent.block); writer.splitIf(state == _beforeBody || state == _beforeWhenAndBody); writer.format(_body); - if (state != State.unsplit) writer.popIndent(); + if (indentBody) writer.popIndent(); } @override diff --git a/test/tall/expression/switch.stmt b/test/tall/expression/switch.stmt index a144b83a..f6b52d3f 100644 --- a/test/tall/expression/switch.stmt +++ b/test/tall/expression/switch.stmt @@ -120,8 +120,7 @@ e = switch (obj) { e = switch (obj) { oneConstant || twoConstant || - threeConstant => - body, + threeConstant => body, }; >>> Nested logic-or operands are indented. e = switch (obj) { @@ -175,4 +174,15 @@ e = switch (obj) { veryLongElement, veryLongElement, ), -}; \ No newline at end of file +}; +>>> Prefer to split `||` pattern instead of case body. +e = switch (obj) { + pattern || another => function( + argument, + ), +}; +<<< +e = switch (obj) { + pattern || + another => function(argument), +}; diff --git a/test/tall/expression/switch_guard.stmt b/test/tall/expression/switch_guard.stmt index baa1631f..e820b614 100644 --- a/test/tall/expression/switch_guard.stmt +++ b/test/tall/expression/switch_guard.stmt @@ -96,8 +96,7 @@ e = switch (obj) { <<< e = switch (obj) { veryVeryLongPattern || - reallyMustSplitHere when true => - body, + reallyMustSplitHere when true => body, }; >>> Outermost logic-or split in pattern, expression split in guard. e = switch (obj) { diff --git a/test/tall/regression/1100/1197.unit b/test/tall/regression/1100/1197.unit index 2bbf01e2..028b6ce6 100644 --- a/test/tall/regression/1100/1197.unit +++ b/test/tall/regression/1100/1197.unit @@ -25,6 +25,9 @@ main() { } } <<< +### TODO(1466): Ideally, the first case would also split at the `||` instead of +### of before `.`, but the formatter can't distinguish that case without fixing +### #1466. main() { { return TextFieldTapRegion( @@ -38,14 +41,13 @@ main() { TargetPlatform.android || TargetPlatform.fuchsia || TargetPlatform.linux || - TargetPlatform.windows => - _renderEditable.selectWordsInRange( - from: - longPressMoveUpdateDetails.globalPosition - - longPressMoveUpdateDetails.offsetFromOrigin, - to: longPressMoveUpdateDetails.globalPosition, - cause: SelectionChangedCause.longPress, - ), + TargetPlatform.windows => _renderEditable.selectWordsInRange( + from: + longPressMoveUpdateDetails.globalPosition - + longPressMoveUpdateDetails.offsetFromOrigin, + to: longPressMoveUpdateDetails.globalPosition, + cause: SelectionChangedCause.longPress, + ), }); }, ); @@ -77,6 +79,9 @@ main() { } } <<< +### TODO(1466): Ideally, the first case would also split at the `||` instead of +### of before `.`, but the formatter can't distinguish that case without fixing +### #1466. main() { { return TextFieldTapRegion( @@ -92,14 +97,13 @@ main() { TargetPlatform.android || TargetPlatform.fuchsia || TargetPlatform.linux || - TargetPlatform.windows => - _renderEditable.selectWordsInRange( - from: - longPressMoveUpdateDetails.globalPosition - - longPressMoveUpdateDetails.offsetFromOrigin, - to: longPressMoveUpdateDetails.globalPosition, - cause: SelectionChangedCause.longPress, - ), + TargetPlatform.windows => _renderEditable.selectWordsInRange( + from: + longPressMoveUpdateDetails.globalPosition - + longPressMoveUpdateDetails.offsetFromOrigin, + to: longPressMoveUpdateDetails.globalPosition, + cause: SelectionChangedCause.longPress, + ), }, ); } diff --git a/test/tall/regression/1600/1602.stmt b/test/tall/regression/1600/1602.stmt new file mode 100644 index 00000000..d996b69d --- /dev/null +++ b/test/tall/regression/1600/1602.stmt @@ -0,0 +1,12 @@ +>>> (indent 4) + return switch (_advance().type) { + TokenType.float || TokenType.int || TokenType.string => LiteralExpression( + _previous.value!, + ), + }; +<<< + return switch (_advance().type) { + TokenType.float || + TokenType.int || + TokenType.string => LiteralExpression(_previous.value!), + }; \ No newline at end of file