blob: 1471a5827a45e4f69970f809b88b232d6f642286 [file] [log] [blame]
// Copyright (c) 2023, 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 '../constants.dart';
import 'piece.dart';
/// A piece for an if statement or element.
///
/// We also use this for while statements, which are formatted exactly like an
/// if statement with no else clause.
class IfPiece extends Piece {
/// Whether this is an if statement versus if collection element.
final bool _isStatement;
final List<_IfSection> _sections = [];
IfPiece({required bool isStatement}) : _isStatement = isStatement;
void add(Piece header, Piece statement, {required bool isBlock}) {
_sections.add(_IfSection(header, statement, isBlock));
}
@override
List<State> get additionalStates => [State.split];
@override
void applyConstraints(State state, Constrain constrain) {
// If an if element, any spread collection's split state must follow the
// surrounding if element's: we either split all the spreads or none of
// them. And if any of the non-spread then or else branches split, then the
// spreads do too.
if (!_isStatement) {
for (var section in _sections) {
if (section.isBlock) {
constrain(section.statement, state);
}
}
}
}
@override
void forEachChild(void Function(Piece piece) callback) {
for (var section in _sections) {
callback(section.header);
callback(section.statement);
}
}
@override
void format(CodeWriter writer, State state) {
for (var i = 0; i < _sections.length; i++) {
var section = _sections[i];
// A split in the condition forces the branches to split.
writer.pushAllowNewlines(state == State.split);
writer.format(section.header);
if (!section.isBlock) {
writer.pushIndent(Indent.block);
writer.splitIf(state == State.split);
}
// TODO(perf): Investigate whether it's worth using `separate:` here.
writer.format(section.statement);
// Reset the indentation for the subsequent `else` or `} else` line.
if (!section.isBlock) writer.popIndent();
if (i < _sections.length - 1) {
writer.splitIf(state == State.split && !section.isBlock);
}
writer.popAllowNewlines();
}
}
}
/// A single section in a chain of if-elses.
///
/// For the first then branch, the [header] is the `if (condition)` part and
/// the statement is the then branch. For all `else if` branches, the [header]
/// is the `else if (condition)` and the statement is the subsequent then
/// branch. For the final `else` branch, if there is one, the [header] is just
/// `else` and the statement is the else branch.
class _IfSection {
final Piece header;
final Piece statement;
/// Whether the [statement] piece is from a block or a spread collection
/// literal.
final bool isBlock;
_IfSection(this.header, this.statement, this.isBlock);
}