| // 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 the `for (...)` part of a for statement or element. |
| class ForPiece extends Piece { |
| /// The `for` keyword. |
| final Piece _forKeyword; |
| |
| /// The part inside `( ... )`, including the parentheses themselves, at the |
| /// header of a for statement. |
| final Piece _parts; |
| |
| /// Whether the contents of the parentheses in the `for (...)` should be |
| /// expression indented or not. |
| /// |
| /// This is usually not necessary because the contents will either be a |
| /// [ListPiece] which adds its own block indentation, or an [AssignPiece] |
| /// which indents as necessary. But in the rare case the for-parts is a |
| /// variable or pattern variable declaration with metadata that splits, we |
| /// need to ensure that the metadata is indented, as in: |
| /// |
| /// for (@LongAnnotation |
| /// @AnotherAnnotation |
| /// var element in list) { ... } |
| final bool _indent; |
| |
| ForPiece(this._forKeyword, this._parts, {required bool indent}) |
| : _indent = indent; |
| |
| @override |
| void format(CodeWriter writer, State state) { |
| writer.format(_forKeyword); |
| writer.space(); |
| if (_indent) writer.pushIndent(Indent.expression, canCollapse: true); |
| writer.format(_parts); |
| if (_indent) writer.popIndent(); |
| } |
| |
| @override |
| void forEachChild(void Function(Piece piece) callback) { |
| callback(_forKeyword); |
| callback(_parts); |
| } |
| } |
| |
| /// A piece for the `<variable> in <expression>` part of a for-in loop. |
| /// |
| /// Can be formatted two ways: |
| /// |
| /// [State.unsplit] No split at all: |
| /// |
| /// for (var x in y) ... |
| /// |
| /// This state also allows splitting the sequence expression if it can be block |
| /// formatted: |
| /// |
| /// for (var i in [ |
| /// element1, |
| /// element2, |
| /// element3, |
| /// ]; |
| /// |
| /// [State.split] Split at the `in` operator and allow expression splitting on |
| /// either side. Allows: |
| /// |
| /// for (var (longVariable && |
| /// anotherVariable) |
| /// in longOperand + |
| /// anotherOperand) { |
| /// ... |
| /// } |
| class ForInPiece extends Piece { |
| /// The variable or pattern initialized with each loop iteration. |
| final Piece _variable; |
| |
| /// The `in` keyword followed by the sequence expression. |
| final Piece _sequence; |
| |
| /// If `true` then the sequence expression supports being block-formatted, |
| /// like: |
| /// |
| /// for (var e in [ |
| /// element1, |
| /// element2, |
| /// ]) { |
| /// // ... |
| /// } |
| final bool _canBlockSplitSequence; |
| |
| ForInPiece(this._variable, this._sequence, |
| {bool canBlockSplitSequence = false}) |
| : _canBlockSplitSequence = canBlockSplitSequence; |
| |
| @override |
| List<State> get additionalStates => const [State.split]; |
| |
| @override |
| bool allowNewlineInChild(State state, Piece child) { |
| if (state == State.split) return true; |
| |
| // Always allow block-splitting the sequence if it supports it. |
| return child == _sequence && _canBlockSplitSequence; |
| } |
| |
| @override |
| void format(CodeWriter writer, State state) { |
| // When splitting at `in`, both operands may split or not and will be |
| // indented if they do. |
| if (state == State.split) writer.pushIndent(Indent.expression); |
| |
| writer.format(_variable); |
| writer.splitIf(state == State.split); |
| writer.format(_sequence); |
| |
| if (state == State.split) writer.popIndent(); |
| } |
| |
| @override |
| void forEachChild(void Function(Piece piece) callback) { |
| callback(_variable); |
| callback(_sequence); |
| } |
| } |