| // 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 |
| bool allowNewlineInChild(State state, Piece child) { |
| if (child == _header) { |
| return state != State.unsplit; |
| } else { |
| return _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); |
| |
| // TODO(perf): Investigate whether it's worth using `separate:` here. |
| 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'; |
| } |