@@ -91,11 +91,6 @@ public class PrettyPrinter {
91
91
/// Indicates whether or not the printer is currently at the beginning of a line.
92
92
private var isAtStartOfLine = true
93
93
94
- /// Indicates whether the kind of the last break was one that triggers a continuation line (i.e.,
95
- /// a `.continue`, an `.open(.continuation)`, or a `.close` break that causes
96
- /// `currentLineIsContinuation` to become true).
97
- private var wasLastBreakKindContinue = false
98
-
99
94
/// Tracks how many printer control tokens to suppress firing breaks are active.
100
95
private var activeBreakSuppressionCount = 0
101
96
@@ -159,8 +154,8 @@ public class PrettyPrinter {
159
154
outputBuffer. append ( String ( str) )
160
155
}
161
156
162
- /// Ensures that the given number of newlines to the output stream ( taking into account any
163
- /// pre-existing consecutive newlines) .
157
+ /// Writes newlines into the output stream, taking into account any pre-existing consecutive
158
+ /// newlines and the maximum allowed number of blank lines .
164
159
///
165
160
/// This function does some implicit collapsing of consecutive newlines to ensure that the
166
161
/// results are consistent when breaks and explicit newlines coincide. For example, imagine a
@@ -171,28 +166,20 @@ public class PrettyPrinter {
171
166
/// subtract the previously written newlines during the second call so that we end up with the
172
167
/// correct number overall.
173
168
///
174
- /// - Parameters:
175
- /// - count: The number of newlines to write.
176
- /// - kind: Indicates whether the newlines are flexible, discretionary, or mandatory newlines.
177
- /// Refer to the documentation of `NewlineKind` for details on how each of these are printed.
178
- private func writeNewlines( _ count: Int , kind: NewlineKind ) {
179
- // We add 1 because it takes 2 newlines to create a blank line.
169
+ /// - Parameter newlines: The number and type of newlines to write.
170
+ private func writeNewlines( _ newlines: NewlineBehavior ) {
180
171
let numberToPrint : Int
181
- if kind == . mandatory {
172
+ switch newlines {
173
+ case . elective:
174
+ numberToPrint = consecutiveNewlineCount == 0 ? 1 : 0
175
+ case . soft( let count, _) :
176
+ // We add 1 to the max blank lines because it takes 2 newlines to create the first blank line.
177
+ numberToPrint = min ( count - consecutiveNewlineCount, configuration. maximumBlankLines + 1 )
178
+ case . hard( let count) :
182
179
numberToPrint = count
183
- } else {
184
- let maximumNewlines = configuration. maximumBlankLines + 1
185
- if count <= maximumNewlines {
186
- numberToPrint = count - consecutiveNewlineCount
187
- } else {
188
- numberToPrint = maximumNewlines - consecutiveNewlineCount
189
- }
190
-
191
- guard ( kind == . discretionary && numberToPrint > 0 ) || consecutiveNewlineCount == 0 else {
192
- return
193
- }
194
180
}
195
181
182
+ guard numberToPrint > 0 else { return }
196
183
writeRaw ( String ( repeating: " \n " , count: numberToPrint) )
197
184
lineNumber += numberToPrint
198
185
isAtStartOfLine = true
@@ -259,8 +246,7 @@ public class PrettyPrinter {
259
246
260
247
// Create a line break if needed. Calculate the indentation required and adjust spaceRemaining
261
248
// accordingly.
262
- case . break( let kind, let size, _) :
263
- wasLastBreakKindContinue = false
249
+ case . break( let kind, let size, let newline) :
264
250
var mustBreak = forceBreakStack. last ?? false
265
251
266
252
// Tracks whether the current line should be considered a continuation line, *if and only if
@@ -304,12 +290,6 @@ public class PrettyPrinter {
304
290
305
291
continuationStack. append ( currentLineIsContinuation)
306
292
307
- // If the open break kind is a continuation and it fired, then we don't want to set this
308
- // flag because the active open break will provide the continuation indentation for the
309
- // remaining lines. If the break doesn't fire, we need to set it so that the remaining lines
310
- // get the appropriate indentation.
311
- wasLastBreakKindContinue = openKind == . continuation && !continuationBreakWillFire
312
-
313
293
// Once we've reached an open break and preserved the continuation state, the "scope" we now
314
294
// enter is *not* a continuation scope. If it was one, we'll re-enter it when we reach the
315
295
// corresponding close.
@@ -382,11 +362,9 @@ public class PrettyPrinter {
382
362
383
363
// Restore the continuation state of the scope we were in before the open break occurred.
384
364
currentLineIsContinuation = currentLineIsContinuation || wasContinuationWhenOpened
385
- wasLastBreakKindContinue = wasContinuationWhenOpened
386
365
isContinuationIfBreakFires = wasContinuationWhenOpened
387
366
388
367
case . continue:
389
- wasLastBreakKindContinue = true
390
368
isContinuationIfBreakFires = true
391
369
392
370
case . same:
@@ -396,9 +374,24 @@ public class PrettyPrinter {
396
374
mustBreak = currentLineIsContinuation
397
375
}
398
376
399
- if !isBreakingSupressed && ( ( !isAtStartOfLine && length > spaceRemaining) || mustBreak) {
377
+ var overrideBreakingSuppressed = false
378
+ switch newline {
379
+ case . elective: break
380
+ case . soft( _, let discretionary) :
381
+ // A discretionary newline (i.e. from the source) should create a line break even if the
382
+ // rules for breaking are disabled.
383
+ overrideBreakingSuppressed = discretionary
384
+ mustBreak = true
385
+ case . hard:
386
+ // A hard newline must always create a line break, regardless of the context.
387
+ overrideBreakingSuppressed = true
388
+ mustBreak = true
389
+ }
390
+
391
+ let suppressBreaking = isBreakingSupressed && !overrideBreakingSuppressed
392
+ if !suppressBreaking && ( ( !isAtStartOfLine && length > spaceRemaining) || mustBreak) {
400
393
currentLineIsContinuation = isContinuationIfBreakFires
401
- writeNewlines ( 1 , kind : . flexible )
394
+ writeNewlines ( newline )
402
395
lastBreak = true
403
396
} else {
404
397
if isAtStartOfLine {
@@ -417,24 +410,6 @@ public class PrettyPrinter {
417
410
case . space( let size, _) :
418
411
enqueueSpaces ( size)
419
412
420
- // Apply `count` line breaks, calculate the indentation required, and adjust spaceRemaining.
421
- case . newlines( let count, let kind) :
422
- // If a newline immediately followed an open-continue break, then this is effectively the
423
- // same as if it had fired. Activate it, and reset the last-break-kind flag so that the
424
- // indentation of subsequent lines is contributed by that break and not by inherited
425
- // continuation state.
426
- if let lastActiveOpenBreak = activeOpenBreaks. last,
427
- lastActiveOpenBreak. index == idx - 1 ,
428
- lastActiveOpenBreak. kind == . continuation
429
- {
430
- activeOpenBreaks [ activeOpenBreaks. count - 1 ] . contributesContinuationIndent = true
431
- wasLastBreakKindContinue = false
432
- }
433
-
434
- currentLineIsContinuation = wasLastBreakKindContinue
435
- writeNewlines ( count, kind: kind)
436
- lastBreak = true
437
-
438
413
// Print any indentation required, followed by the text content of the syntax token.
439
414
case . syntax( let text) :
440
415
guard !text. isEmpty else { break }
@@ -515,42 +490,27 @@ public class PrettyPrinter {
515
490
516
491
// Break lengths are equal to its size plus the token or group following it. Calculate the
517
492
// length of any prior break tokens.
518
- case . break( _, let size, _ ) :
493
+ case . break( _, let size, let newline ) :
519
494
if let index = delimIndexStack. last, case . break = tokens [ index] {
520
495
lengths [ index] += total
521
496
delimIndexStack. removeLast ( )
522
497
}
523
-
524
498
lengths. append ( - total)
525
499
delimIndexStack. append ( i)
526
- total += size
500
+
501
+ if case . elective = newline {
502
+ total += size
503
+ } else {
504
+ // `size` is never used in this case, because the break always fires. Use `maxLineLength`
505
+ // to ensure enclosing groups are large enough to force preceding breaks to fire.
506
+ total += maxLineLength
507
+ }
527
508
528
509
// Space tokens have a length equal to its size.
529
510
case . space( let size, _) :
530
511
lengths. append ( size)
531
512
total += size
532
513
533
- // The length of newlines are equal to the maximum allowed line length. Calculate the length
534
- // of any prior break tokens.
535
- case . newlines:
536
- if let index = delimIndexStack. last, case . break = tokens [ index] {
537
- if index == i - 1 {
538
- // A break immediately preceding a newline should have a length of zero, so that it
539
- // doesn't fire.
540
- lengths [ index] = 0
541
- } else {
542
- lengths [ index] += total
543
- }
544
- delimIndexStack. removeLast ( )
545
- }
546
-
547
- // Since newlines must always cause a line-break, we set their length as the full allowed
548
- // width of the line. This causes any enclosing groups to have a length exceeding the line
549
- // limit, and so the group must break and indent. e.g. single-line versus multi-line
550
- // function bodies.
551
- lengths. append ( maxLineLength)
552
- total += maxLineLength
553
-
554
514
// Syntax tokens have a length equal to the number of columns needed to print its contents.
555
515
case . syntax( let text) :
556
516
lengths. append ( text. count)
@@ -615,11 +575,9 @@ public class PrettyPrinter {
615
575
printDebugIndent ( )
616
576
print ( " [SYNTAX \" \( syntax) \" Length: \( length) Idx: \( idx) ] " )
617
577
618
- case . break( let kind, let size, let ignoresDiscretionary ) :
578
+ case . break( let kind, let size, let newline ) :
619
579
printDebugIndent ( )
620
- print (
621
- " [BREAK Kind: \( kind) Size: \( size) Length: \( length) "
622
- + " Ignores Discretionary NL: \( ignoresDiscretionary) Idx: \( idx) ] " )
580
+ print ( " [BREAK Kind: \( kind) Size: \( size) Length: \( length) NL: \( newline) Idx: \( idx) ] " )
623
581
624
582
case . open( let breakstyle) :
625
583
printDebugIndent ( )
@@ -636,10 +594,6 @@ public class PrettyPrinter {
636
594
printDebugIndent ( )
637
595
print ( " [CLOSE Idx: \( idx) ] " )
638
596
639
- case . newlines( let N, let required) :
640
- printDebugIndent ( )
641
- print ( " [NEWLINES N: \( N) Required: \( required) Length: \( length) Idx: \( idx) ] " )
642
-
643
597
case . space( let size, let flexible) :
644
598
printDebugIndent ( )
645
599
print ( " [SPACE Size: \( size) Flexible: \( flexible) Length: \( length) Idx: \( idx) ] " )
0 commit comments