@@ -17,15 +17,11 @@ import 'solution.dart';
17
17
/// an instance of this class. It has methods that the piece can call to add
18
18
/// output text to the resulting code, recursively format child pieces, insert
19
19
/// whitespace, etc.
20
- ///
21
- /// This class also accumulates the score (the relative desireability of a set
22
- /// of formatting choices) that the resulting code has by tracking things like
23
- /// how many characters of code overflow the page width.
24
20
class CodeWriter {
25
21
final int _pageWidth;
26
22
27
- /// The state values for the pieces being written .
28
- final PieceStateSet _pieceStates ;
23
+ /// The solution this [CodeWriter] is generating code for .
24
+ final Solution _solution ;
29
25
30
26
/// Buffer for the code being written.
31
27
final StringBuffer _buffer = StringBuffer ();
@@ -43,20 +39,9 @@ class CodeWriter {
43
39
/// [Whitespace.blankLine] .
44
40
int _pendingIndent = 0 ;
45
41
46
- /// The cost of the currently chosen line splits.
47
- int _cost = 0 ;
48
-
49
- /// The total number of characters of code that have overflowed the page
50
- /// width so far.
51
- int _overflow = 0 ;
52
-
53
42
/// The number of characters in the line currently being written.
54
43
int _column = 0 ;
55
44
56
- /// Whether this solution has encountered a mandatory newline (like from a
57
- /// line comment or a statement terminator) where no newline is permitted.
58
- bool _hasInvalidNewline = false ;
59
-
60
45
/// The stack of state for each [Piece] being formatted.
61
46
///
62
47
/// For each piece being formatted from a call to [format()] , we keep track of
@@ -67,7 +52,7 @@ class CodeWriter {
67
52
///
68
53
/// This is used to increase the cumulative nesting as we recurse into pieces
69
54
/// and then unwind that as child pieces are completed.
70
- final List <_PieceOptions > _pieceOptions = [_PieceOptions ( 0 , true ) ];
55
+ final List <_PieceOptions > _options = [];
71
56
72
57
/// Whether we have already found the first line where whose piece should be
73
58
/// used to expand further solutions.
@@ -101,31 +86,14 @@ class CodeWriter {
101
86
/// solution if the line ends up overflowing.
102
87
final List <Piece > _currentUnsolvedPieces = [];
103
88
104
- /// The options for the current innermost piece being formatted.
105
- _PieceOptions get _options => _pieceOptions.last;
106
-
107
- /// The offset in the formatted code where the selection starts.
108
- ///
109
- /// This is `null` until the piece containing the selection start is reached
110
- /// at which point it gets set. It remains `null` if there is no selection.
111
- int ? _selectionStart;
112
-
113
- /// The offset in the formatted code where the selection ends.
114
- ///
115
- /// This is `null` until the piece containing the selection end is reached
116
- /// at which point it gets set. It remains `null` if there is no selection.
117
- int ? _selectionEnd;
118
-
119
- CodeWriter (this ._pageWidth, this ._pieceStates);
89
+ CodeWriter (this ._pageWidth, this ._solution);
120
90
121
- /// Returns the finished code produced by formatting the tree of pieces and
122
- /// the final score .
123
- Solution finish () {
91
+ /// Returns the final formatted text and the next piece that can be expanded
92
+ /// from the solution this [CodeWriter] is writing, if any .
93
+ ( String , Piece ? ) finish () {
124
94
_finishLine ();
125
95
126
- return Solution (_pieceStates, _buffer.toString (), _selectionStart,
127
- _selectionEnd, _nextPieceToExpand,
128
- isValid: ! _hasInvalidNewline, overflow: _overflow, cost: _cost);
96
+ return (_buffer.toString (), _nextPieceToExpand);
129
97
}
130
98
131
99
/// Appends [text] to the output.
@@ -158,10 +126,17 @@ class CodeWriter {
158
126
/// piece to [indent] , relative to the indentation of the surrounding piece.
159
127
///
160
128
/// Replaces any previous indentation set by this piece.
161
- ///
162
129
// TODO(tall): Add another API that adds/subtracts existing indentation.
163
130
void setIndent (int indent) {
164
- _options.indent = _pieceOptions[_pieceOptions.length - 2 ].indent + indent;
131
+ var parentIndent = 0 ;
132
+
133
+ // If there is a surrounding Piece, then set the indent relative to that
134
+ // piece's current indentation.
135
+ if (_options.length > 1 ) {
136
+ parentIndent = _options[_options.length - 2 ].indent;
137
+ }
138
+
139
+ _options.last.indent = parentIndent + indent;
165
140
}
166
141
167
142
/// Inserts a newline if [condition] is true.
@@ -215,7 +190,7 @@ class CodeWriter {
215
190
void whitespace (Whitespace whitespace, {bool flushLeft = false }) {
216
191
if (whitespace case Whitespace .newline || Whitespace .blankLine) {
217
192
_handleNewline ();
218
- _pendingIndent = flushLeft ? 0 : _options.indent;
193
+ _pendingIndent = flushLeft ? 0 : _options.last. indent;
219
194
}
220
195
221
196
_pendingWhitespace = _pendingWhitespace.collapse (whitespace);
@@ -224,31 +199,29 @@ class CodeWriter {
224
199
/// Sets whether newlines are allowed to occur from this point on for the
225
200
/// current piece.
226
201
void setAllowNewlines (bool allowed) {
227
- _options.allowNewlines = allowed;
202
+ _options.last. allowNewlines = allowed;
228
203
}
229
204
230
205
/// Format [piece] and insert the result into the code being written and
231
206
/// returned by [finish()] .
232
207
void format (Piece piece) {
233
- _pieceOptions.add (_PieceOptions (_options.indent, _options.allowNewlines));
208
+ _options.add (_PieceOptions (piece, _options.lastOrNull? .indent ?? 0 ,
209
+ _options.lastOrNull? .allowNewlines ?? true ));
234
210
235
- var isUnsolved = ! _pieceStates .isBound (piece) && piece.states.length > 1 ;
211
+ var isUnsolved = ! _solution .isBound (piece) && piece.states.length > 1 ;
236
212
if (isUnsolved) _currentUnsolvedPieces.add (piece);
237
213
238
- var state = _pieceStates.pieceState (piece);
239
- _cost += piece.stateCost (state);
240
-
241
214
// TODO(perf): Memoize this. Might want to create a nested PieceWriter
242
215
// instead of passing in `this` so we can better control what state needs
243
216
// to be used as the key in the memoization table.
244
- piece.format (this , state );
217
+ piece.format (this , _solution. pieceState (piece) );
245
218
246
219
if (isUnsolved) _currentUnsolvedPieces.removeLast ();
247
220
248
- var childOptions = _pieceOptions .removeLast ();
221
+ var childOptions = _options .removeLast ();
249
222
250
223
// If the child [piece] contains a newline then this one transitively does.
251
- if (childOptions.hasNewline) _handleNewline ();
224
+ if (childOptions.hasNewline && _options.isNotEmpty ) _handleNewline ();
252
225
}
253
226
254
227
/// Format [piece] if not null.
@@ -258,30 +231,26 @@ class CodeWriter {
258
231
259
232
/// Sets [selectionStart] to be [start] code units into the output.
260
233
void startSelection (int start) {
261
- assert (_selectionStart == null );
262
-
263
234
_flushWhitespace ();
264
- _selectionStart = _buffer.length + start;
235
+ _solution. startSelection ( _buffer.length + start) ;
265
236
}
266
237
267
238
/// Sets [selectionEnd] to be [end] code units into the output.
268
239
void endSelection (int end) {
269
- assert (_selectionEnd == null );
270
-
271
240
_flushWhitespace ();
272
- _selectionEnd = _buffer.length + end;
241
+ _solution. endSelection ( _buffer.length + end) ;
273
242
}
274
243
275
244
/// Notes that a newline has been written.
276
245
///
277
246
/// If this occurs in a place where newlines are prohibited, then invalidates
278
247
/// the solution.
279
248
void _handleNewline () {
280
- if (! _options.allowNewlines) _hasInvalidNewline = true ;
249
+ if (! _options.last. allowNewlines) _solution. invalidate (_options.last.piece) ;
281
250
282
251
// Note that this piece contains a newline so that we can propagate that
283
252
// up to containing pieces too.
284
- _options.hasNewline = true ;
253
+ _options.last. hasNewline = true ;
285
254
}
286
255
287
256
/// Write any pending whitespace.
@@ -314,15 +283,15 @@ class CodeWriter {
314
283
void _finishLine () {
315
284
// If the completed line is too long, track the overflow.
316
285
if (_column >= _pageWidth) {
317
- _overflow += _column - _pageWidth;
286
+ _solution. addOverflow ( _column - _pageWidth) ;
318
287
}
319
288
320
289
// If we found a problematic line, and there is a piece on the line that
321
290
// we can try to split, then remember that piece so that the solution will
322
291
// expand it next.
323
292
if (! _foundExpandLine &&
324
293
_nextPieceToExpand != null &&
325
- (_column > _pageWidth || _hasInvalidNewline )) {
294
+ (_column > _pageWidth || ! _solution.isValid )) {
326
295
// We found a problematic line, so remember it and the piece on it.
327
296
_foundExpandLine = true ;
328
297
} else if (! _foundExpandLine) {
@@ -359,6 +328,9 @@ enum Whitespace {
359
328
360
329
/// The mutable state local to a single piece being formatted.
361
330
class _PieceOptions {
331
+ /// The piece being formatted with these options.
332
+ final Piece piece;
333
+
362
334
/// The absolute number of spaces of leading indentation coming from
363
335
/// block-like structure or explicit extra indentation (aligning constructor
364
336
/// initializers, `show` clauses, etc.).
@@ -373,5 +345,5 @@ class _PieceOptions {
373
345
/// Whether any newlines have occurred in this piece or any of its children.
374
346
bool hasNewline = false ;
375
347
376
- _PieceOptions (this .indent, this .allowNewlines);
348
+ _PieceOptions (this .piece, this . indent, this .allowNewlines);
377
349
}
0 commit comments