| // 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'; |
| } |