4
4
import 'dart:math' as math;
5
5
6
6
import 'chunk.dart' ;
7
+ import 'constants.dart' ;
7
8
import 'dart_formatter.dart' ;
8
9
import 'debug.dart' as debug;
9
10
import 'line_writer.dart' ;
@@ -12,7 +13,6 @@ import 'nesting_level.dart';
12
13
import 'rule/rule.dart' ;
13
14
import 'source_code.dart' ;
14
15
import 'style_fix.dart' ;
15
- import 'whitespace.dart' ;
16
16
17
17
/// Matches if the last character of a string is an identifier character.
18
18
final _trailingIdentifierChar = RegExp (r'[a-zA-Z0-9_]$' );
@@ -45,13 +45,11 @@ class ChunkBuilder {
45
45
46
46
final List <Chunk > _chunks;
47
47
48
- /// The whitespace that should be written to [_chunks] before the next
49
- /// non-whitespace token.
48
+ /// The number of newlines that should be written before the next
49
+ /// non-whitespace token.
50
50
///
51
- /// This ensures that changes to indentation and nesting also apply to the
52
- /// most recent split, even if the visitor "creates" the split before changing
53
- /// indentation or nesting.
54
- Whitespace _pendingWhitespace = Whitespace .none;
51
+ /// This will always be 0, 1, or 2.
52
+ int _pendingNewlines = 0 ;
55
53
56
54
/// Whether a non-breaking space should be written before the next text.
57
55
bool _pendingSpace = false ;
@@ -108,17 +106,6 @@ class ChunkBuilder {
108
106
/// When this is non-zero, splits are ignored.
109
107
int _preventSplitNesting = 0 ;
110
108
111
- /// Whether there is pending whitespace that depends on the number of
112
- /// newlines in the source.
113
- ///
114
- /// This is used to avoid calculating the newlines between tokens unless
115
- /// actually needed since doing so is slow when done between every single
116
- /// token pair.
117
- bool get needsToPreserveNewlines =>
118
- _pendingWhitespace == Whitespace .oneOrTwoNewlines ||
119
- _pendingWhitespace == Whitespace .splitOrTwoNewlines ||
120
- _pendingWhitespace == Whitespace .splitOrNewline;
121
-
122
109
/// The number of characters of code that can fit in a single line.
123
110
int get pageWidth => _formatter.pageWidth;
124
111
@@ -171,10 +158,18 @@ class ChunkBuilder {
171
158
_afterComment = false ;
172
159
}
173
160
174
- /// Writes a [WhitespaceChunk] of [type] .
175
- void writeWhitespace (Whitespace type,
176
- {bool flushLeft = false , bool nest = false }) {
177
- _pendingWhitespace = type;
161
+ /// Writes one or two hard newlines.
162
+ ///
163
+ /// Doesn't immediately write them. That way line breaking is correctly
164
+ /// interleaved with any comments that appear before the next token.
165
+ ///
166
+ /// If [isDouble] is `true` , inserts an extra blank line. If [flushLeft] is
167
+ /// `true` , the next line will start at column 1 and ignore indentation and
168
+ /// nesting. If [nest] is `true` then the next line will use expression
169
+ /// nesting.
170
+ void writeNewline (
171
+ {bool isDouble = false , bool flushLeft = false , bool nest = false }) {
172
+ _pendingNewlines = isDouble ? 2 : 1 ;
178
173
_pendingFlushLeft = flushLeft;
179
174
_pendingNested = nest;
180
175
}
@@ -194,7 +189,7 @@ class ChunkBuilder {
194
189
// chunk is safe since the rule that uses the chunk will itself get
195
190
// discarded because no chunk references it.
196
191
if (_preventSplitNesting > 0 ) {
197
- _pendingWhitespace = Whitespace .none ;
192
+ _pendingNewlines = 0 ;
198
193
_pendingNested = false ;
199
194
200
195
if (space) _pendingSpace = true ;
@@ -203,12 +198,9 @@ class ChunkBuilder {
203
198
204
199
// If a hard split after a comment is already pending, then prefer that over
205
200
// a soft split.
206
- if (_pendingWhitespace.minimumLines > 0 ) {
207
- return Chunk .dummy ();
208
- }
201
+ if (_pendingNewlines > 0 ) return Chunk .dummy ();
209
202
210
- return _writeSplit (
211
- isHard: false , isDouble: false , nest: nest, space: space);
203
+ return _writeSplit (isHard: false , nest: nest, space: space);
212
204
}
213
205
214
206
/// Outputs the series of [comments] and associated whitespace that appear
@@ -233,14 +225,13 @@ class ChunkBuilder {
233
225
// Normally, a blank line is required after `library`, but since there is
234
226
// one after the comment, we don't need one before it. This is mainly so
235
227
// that commented out directives stick with their preceding group.
236
- if (_pendingWhitespace == Whitespace .twoNewlines &&
237
- comments.first.linesBefore < 2 ) {
228
+ if (_pendingNewlines == 2 && comments.first.linesBefore < 2 ) {
238
229
if (linesBeforeToken > 1 ) {
239
- writeWhitespace ( Whitespace .newline );
230
+ writeNewline ( );
240
231
} else {
241
232
for (var i = 1 ; i < comments.length; i++ ) {
242
233
if (comments[i].linesBefore > 1 ) {
243
- writeWhitespace ( Whitespace .newline );
234
+ writeNewline ( );
244
235
break ;
245
236
}
246
237
}
@@ -260,9 +251,9 @@ class ChunkBuilder {
260
251
//
261
252
// When that happens, we need to make sure to preserve the split at the end
262
253
// of the first sequence of comments if there is one.
263
- if (_afterComment && _pendingWhitespace != Whitespace .none ) {
254
+ if (_afterComment && _pendingNewlines > 0 ) {
264
255
comments.first.linesBefore = 1 ;
265
- writeWhitespace ( Whitespace .none) ;
256
+ _pendingNewlines = 0 ;
266
257
}
267
258
268
259
// Edge case: if the comments are completely inline (i.e. just a series of
@@ -278,17 +269,15 @@ class ChunkBuilder {
278
269
//
279
270
// /* a */ /* b */ import 'a.dart';
280
271
if (linesBeforeToken == 0 &&
281
- _pendingWhitespace.minimumLines > comments.first.linesBefore &&
272
+ _pendingNewlines > comments.first.linesBefore &&
282
273
comments.every ((comment) => comment.type == CommentType .inlineBlock)) {
283
- comments.first.linesBefore = _pendingWhitespace.minimumLines ;
274
+ comments.first.linesBefore = _pendingNewlines ;
284
275
}
285
276
286
277
// Write each comment and the whitespace between them.
287
278
for (var i = 0 ; i < comments.length; i++ ) {
288
279
var comment = comments[i];
289
280
290
- preserveNewlines (comment.linesBefore);
291
-
292
281
// See if the comment should follow text on the current line.
293
282
var chunk = _chunkForComment (comment, token);
294
283
if (chunk != null ) {
@@ -302,13 +291,11 @@ class ChunkBuilder {
302
291
}
303
292
} else {
304
293
// Split before the comment if it starts a line.
305
- if (_pendingWhitespace == Whitespace .none ) {
294
+ if (_pendingNewlines == 0 ) {
306
295
if (comment.linesBefore > 0 &&
307
296
(_afterComment || comment.type != CommentType .inlineBlock)) {
308
- writeWhitespace (
309
- _needsBlankLineBeforeComment (comment)
310
- ? Whitespace .twoNewlines
311
- : Whitespace .newline,
297
+ writeNewline (
298
+ isDouble: _needsBlankLineBeforeComment (comment),
312
299
flushLeft: comment.flushLeft,
313
300
nest: true );
314
301
} else if (_chunks.isNotEmpty) {
@@ -351,19 +338,14 @@ class ChunkBuilder {
351
338
}
352
339
353
340
if (linesAfter > 0 ) {
354
- writeWhitespace (
355
- _pendingWhitespace == Whitespace .twoNewlines || linesAfter > 1
356
- ? Whitespace .twoNewlines
357
- : Whitespace .newline,
358
- nest: true );
341
+ writeNewline (
342
+ isDouble: _pendingNewlines == 2 || linesAfter > 1 , nest: true );
359
343
}
360
344
}
361
345
362
346
// If the comment has text following it (aside from a grouping character),
363
347
// it needs a trailing space.
364
348
_pendingSpace = _needsSpaceAfterComment (comments.last, token);
365
-
366
- preserveNewlines (linesBeforeToken);
367
349
_afterComment = true ;
368
350
}
369
351
@@ -422,41 +404,11 @@ class ChunkBuilder {
422
404
_writeText (' $line ' , chunk);
423
405
}
424
406
425
- writeWhitespace ( Whitespace .newline );
407
+ writeNewline ( );
426
408
_emitPendingWhitespace ();
427
409
}
428
410
}
429
411
430
- /// If the current pending whitespace allows some source discretion, pins
431
- /// that down given that the source contains [numLines] newlines at that
432
- /// point and writes any needed split.
433
- void preserveNewlines (int numLines) {
434
- switch (_pendingWhitespace) {
435
- case Whitespace .splitOrNewline:
436
- if (numLines > 0 ) {
437
- _writeSplit (nest: true );
438
- } else {
439
- split (space: true );
440
- }
441
- break ;
442
-
443
- case Whitespace .splitOrTwoNewlines:
444
- if (numLines > 1 ) {
445
- _writeSplit (isDouble: true , nest: true );
446
- } else {
447
- split (space: true );
448
- }
449
- break ;
450
-
451
- case Whitespace .oneOrTwoNewlines:
452
- _writeSplit (isDouble: numLines > 1 , nest: false );
453
- break ;
454
-
455
- default :
456
- break ;
457
- }
458
- }
459
-
460
412
/// Creates a new indentation level [spaces] deeper than the current one.
461
413
///
462
414
/// If omitted, [spaces] defaults to [Indent.block] .
@@ -739,9 +691,9 @@ class ChunkBuilder {
739
691
/// any ambiguous whitespace into a concrete choice.
740
692
void _emitPendingWhitespace (
741
693
{bool isDouble = false , bool mergeEmptySplits = true }) {
742
- if (_pendingWhitespace == Whitespace .none ) return ;
694
+ if (_pendingNewlines == 0 ) return ;
743
695
744
- if (_pendingWhitespace == Whitespace .twoNewlines ) isDouble = true ;
696
+ if (_pendingNewlines == 2 ) isDouble = true ;
745
697
_writeSplit (
746
698
isDouble: isDouble,
747
699
nest: _pendingNested,
@@ -838,7 +790,7 @@ class ChunkBuilder {
838
790
// Not at the beginning of a line.
839
791
if (_chunks.last.text.isEmpty) return false ;
840
792
841
- if (_pendingWhitespace != Whitespace .none ) return false ;
793
+ if (_pendingNewlines > 0 ) return false ;
842
794
843
795
// Magic generic method comments like "Foo/*<T>*/" don't get spaces.
844
796
if (_isGenericMethodComment (comment) && token == '(' ) {
@@ -903,7 +855,7 @@ class ChunkBuilder {
903
855
isHard: isHard, isDouble: isDouble, space: space);
904
856
}
905
857
906
- _pendingWhitespace = Whitespace .none ;
858
+ _pendingNewlines = 0 ;
907
859
_pendingNested = false ;
908
860
909
861
if (chunk.rule.isHardened) _handleHardSplit ();
0 commit comments