blob: 877768ab4100cce03b9d8b01d3cf013e5e08f5c0 [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 'piece.dart';
/// A variable declaration.
///
/// Used for local variable declaration statements, top-level variable
/// declarations and field declarations. Also used to handle splitting between
/// a function or function type's return type and the rest of the function.
///
/// Typed and untyped variables have slightly different splitting logic.
/// Untyped variables never split after the keyword but do indent subsequent
/// variables:
///
/// var longVariableName = initializer,
/// anotherVariable = anotherInitializer;
///
/// Typed variables can split that way too:
///
/// String longVariableName = initializer,
/// anotherVariable = anotherInitializer;
///
/// But they can also split after the type annotation. When that happens, the
/// variables aren't indented:
///
/// VeryLongTypeName
/// longVariableName = initializer,
/// anotherVariable = anotherInitializer;
final class VariablePiece extends Piece {
/// Split between each variable in a multiple variable declaration.
static const State _betweenVariables = State(1);
/// Split after the type annotation and between each variable.
static const State _afterType = State(2, cost: 2);
/// Creates a [VariablePiece].
///
/// The [hasType] parameter should be `true` if the variable declaration has
/// a type annotation.
this(
/// The leading keywords (`var`, `final`, `late`) and optional type
/// annotation.
final Piece _header;
/// Each individual variable being declared.
final List<Piece> _variables, {
/// Whether the variable declaration has a type annotation.
required final bool _hasType,
/// Whether we are using the 3.7 style.
required final bool _is3Dot7,
});
@override
List<State> get additionalStates => [
if (_variables.length > 1) _betweenVariables,
if (_hasType) _afterType,
];
@override
Set<Shape> allowedChildShapes(State state, Piece child) {
// If the variable doesn't allow any other states (because it's just
// `var x` etc.) then allow any shape. That way, if there's a comment
// inside, the solver doesn't get confused trying to invalidate the
// VariablePiece.
if (!_is3Dot7 && _variables.length == 1 && !_hasType) return Shape.all;
if (child == _header) {
return Shape.anyIf(state != State.unsplit);
} else {
return Shape.anyIf(_variables.length == 1 || state != State.unsplit);
}
}
@override
void format(CodeWriter writer, State state) {
writer.format(_header);
// If we split at the variables (but not the type), then indent the
// variables and their initializers.
if (state == _betweenVariables) writer.pushIndent(Indent.expression);
// Split after the type annotation.
writer.splitIf(state == _afterType);
for (var i = 0; i < _variables.length; i++) {
// Split between variables.
if (i > 0) writer.splitIf(state != State.unsplit);
writer.format(_variables[i]);
}
if (state == _betweenVariables) writer.popIndent();
}
@override
void forEachChild(void Function(Piece piece) callback) {
callback(_header);
_variables.forEach(callback);
}
@override
String get debugName => 'Var';
}