blob: 90738ec5d2f2205dc0218bd789baab6eea783831 [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 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;
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);
/// 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.
final bool _hasType;
/// Creates a [VariablePiece].
///
/// The [hasType] parameter should be `true` if the variable declaration has
/// a type annotation.
VariablePiece(this._header, this._variables, {required bool hasType})
: _hasType = hasType;
@override
List<State> get additionalStates => [
if (_variables.length > 1) _betweenVariables,
if (_hasType) _afterType,
];
@override
void format(CodeWriter writer, State state) {
writer.format(_header, allowNewlines: state != State.unsplit);
// 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);
// TODO(perf): Investigate whether it's worth using `separate:` here.
// Force multiple variables to split if an initializer does.
writer.format(_variables[i],
allowNewlines: _variables.length == 1 || state != State.unsplit);
}
if (state == _betweenVariables) writer.popIndent();
}
@override
void forEachChild(void Function(Piece piece) callback) {
callback(_header);
_variables.forEach(callback);
}
@override
String get debugName => 'Var';
}