blob: bf53dc58c5070e9c40fcfc61c64c6b5a5e5cc51b [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '../back_end/code_writer.dart';
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 shaped, like:
///
/// pattern => function(
/// argument,
/// ),
static const State _blockSplitBody = State(1, cost: 0);
/// Split after the `=>` before the body.
static const State _beforeBody = State(2);
/// Split before the `when` guard clause and after the `=>`.
static const State _beforeWhenAndBody = State(3);
/// The pattern the value is matched against.
final Piece _pattern;
/// If there is a `when` clause, that clause.
final Piece? _guard;
/// The `=>` token separating the pattern and body.
final Piece _arrow;
/// The case body expression.
final Piece _body;
/// Whether the pattern can be block formatted.
final bool _canBlockSplitPattern;
/// Whether the outermost pattern is a logical-or pattern.
///
/// We format these specially to make them look like parallel cases:
///
/// switch (obj) {
/// firstPattern ||
/// secondPattern ||
/// thirdPattern =>
/// body;
/// }
final bool _patternIsLogicalOr;
CaseExpressionPiece(
this._pattern,
this._guard,
this._arrow,
this._body, {
required bool canBlockSplitPattern,
required bool patternIsLogicalOr,
}) : _canBlockSplitPattern = canBlockSplitPattern,
_patternIsLogicalOr = patternIsLogicalOr;
@override
List<State> get additionalStates => [
_blockSplitBody,
_beforeBody,
if (_guard != null) ...[_beforeWhenAndBody],
];
@override
Set<Shape> allowedChildShapes(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 => Shape.all,
// 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 => Shape.all,
_blockSplitBody when child == _body => Shape.onlyBlock,
_beforeBody when child == _pattern => Shape.anyIf(_guard == null),
_beforeBody when child == _body => Shape.all,
_beforeWhenAndBody => Shape.all,
_ => Shape.onlyInline,
};
}
@override
void format(CodeWriter writer, State state) {
// If there is a split guard, then indent the pattern past it.
var indentPatternForGuard =
!_canBlockSplitPattern &&
!_patternIsLogicalOr &&
state == _beforeWhenAndBody;
if (indentPatternForGuard) writer.pushIndent(Indent.expression);
writer.format(_pattern);
if (indentPatternForGuard) writer.popIndent();
if (_guard case var guard?) {
writer.pushIndent(Indent.expression);
writer.splitIf(state == _beforeWhenAndBody);
writer.format(guard);
writer.popIndent();
}
writer.space();
writer.format(_arrow);
var indentBody = state != State.unsplit && state != _blockSplitBody;
if (indentBody) writer.pushIndent(Indent.block);
writer.splitIf(state == _beforeBody || state == _beforeWhenAndBody);
writer.format(_body);
if (indentBody) writer.popIndent();
}
@override
void forEachChild(void Function(Piece piece) callback) {
callback(_pattern);
if (_guard case var guard?) callback(guard);
callback(_arrow);
callback(_body);
}
}