| // 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 piece for the `for (...)` part of a for statement or element. |
| final 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.pushCollapsibleIndent(); |
| 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's block |
| /// shaped: |
| /// |
| /// 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) { |
| /// ... |
| /// } |
| abstract base 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; |
| |
| factory ForInPiece( |
| Piece variable, |
| Piece sequence, { |
| bool canBlockSplitSequence = false, |
| required bool is3Dot7, |
| }) { |
| if (is3Dot7) { |
| return _ForInPiece3Dot7( |
| variable, |
| sequence, |
| canBlockSplitSequence: canBlockSplitSequence, |
| ); |
| } else { |
| return _ForInPiece(variable, sequence); |
| } |
| } |
| |
| ForInPiece._(this._variable, this._sequence); |
| |
| @override |
| List<State> get additionalStates => const [State.split]; |
| |
| @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); |
| } |
| } |
| |
| /// A [ForInPiece] subclass for 3.8 and later style. |
| final class _ForInPiece extends ForInPiece { |
| _ForInPiece(super._variable, super._sequence) : super._(); |
| |
| @override |
| Set<Shape> allowedChildShapes(State state, Piece child) => switch (state) { |
| // Always allow block-splitting the sequence if it supports it. |
| State.unsplit when child == _sequence => const {Shape.inline, Shape.block}, |
| State.unsplit => Shape.onlyInline, |
| _ => Shape.all, |
| }; |
| } |
| |
| /// A [ForInPiece] subclass for 3.7 style. |
| final class _ForInPiece3Dot7 extends ForInPiece { |
| /// If `true` then the sequence expression supports being block-formatted, |
| /// like: |
| /// |
| /// for (var e in [ |
| /// element1, |
| /// element2, |
| /// ]) { |
| /// // ... |
| /// } |
| final bool _canBlockSplitSequence; |
| |
| _ForInPiece3Dot7( |
| super._variable, |
| super._sequence, { |
| bool canBlockSplitSequence = false, |
| }) : _canBlockSplitSequence = canBlockSplitSequence, |
| super._(); |
| |
| @override |
| Set<Shape> allowedChildShapes(State state, Piece child) { |
| if (state == State.split) return Shape.all; |
| |
| // Always allow block-splitting the sequence if it supports it. |
| return Shape.anyIf(child == _sequence && _canBlockSplitSequence); |
| } |
| } |