Skip to content

Commit 44f83b4

Browse files
committed
Add AllowWhitespaceOnlyLines configuration
1 parent 4a3def9 commit 44f83b4

File tree

7 files changed

+127
-3
lines changed

7 files changed

+127
-3
lines changed

Documentation/Configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ top-level keys and values:
9494

9595
* `multiElementCollectionTrailingCommas` _(boolean)_: Determines whether multi-element collection literals should have trailing commas.
9696
Defaults to `true`.
97+
98+
* `allowWhitespaceOnlyLines` _(boolean)_: Determines whether lines containing only whitespace should be preserved. When this setting is true, lines that consist solely of whitespace will not have the whitespace removed.
99+
Defaults to `false`
97100

98101
> TODO: Add support for enabling/disabling specific syntax transformations in
99102
> the pipeline.

Sources/SwiftFormat/API/Configuration+Default.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ extension Configuration {
4141
self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
4242
self.multiElementCollectionTrailingCommas = true
4343
self.reflowMultilineStringLiterals = .never
44+
self.allowWhitespaceOnlyLines = false
4445
}
4546
}

Sources/SwiftFormat/API/Configuration.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public struct Configuration: Codable, Equatable {
4646
case noAssignmentInExpressions
4747
case multiElementCollectionTrailingCommas
4848
case reflowMultilineStringLiterals
49+
case allowWhitespaceOnlyLines
4950
}
5051

5152
/// A dictionary containing the default enabled/disabled states of rules, keyed by the rules'
@@ -259,6 +260,12 @@ public struct Configuration: Codable, Equatable {
259260
}
260261

261262
public var reflowMultilineStringLiterals: MultilineStringReflowBehavior
263+
264+
/// Determines whether lines containing only whitespace should be preserved or removed.
265+
///
266+
/// If true, lines that consist solely of whitespace will be preserved as-is without removing
267+
/// the whitespace. If false (the default), the whitespace on such lines will be completely removed.
268+
public var allowWhitespaceOnlyLines: Bool
262269

263270
/// Creates a new `Configuration` by loading it from a configuration file.
264271
public init(contentsOf url: URL) throws {
@@ -352,10 +359,13 @@ public struct Configuration: Codable, Equatable {
352359
try container.decodeIfPresent(
353360
Bool.self, forKey: .multiElementCollectionTrailingCommas)
354361
?? defaults.multiElementCollectionTrailingCommas
355-
356362
self.reflowMultilineStringLiterals =
357363
try container.decodeIfPresent(MultilineStringReflowBehavior.self, forKey: .reflowMultilineStringLiterals)
358364
?? defaults.reflowMultilineStringLiterals
365+
self.allowWhitespaceOnlyLines =
366+
try container.decodeIfPresent(
367+
Bool.self, forKey: .allowWhitespaceOnlyLines)
368+
?? defaults.allowWhitespaceOnlyLines
359369

360370
// If the `rules` key is not present at all, default it to the built-in set
361371
// so that the behavior is the same as if the configuration had been

Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,12 @@ public class PrettyPrinter {
449449

450450
// Print out the number of spaces according to the size, and adjust spaceRemaining.
451451
case .space(let size, _):
452-
outputBuffer.enqueueSpaces(size)
452+
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
453+
let currentIndentationSpaceSize = outputBuffer.currentIndentation.indentation().count
454+
outputBuffer.write(String(repeating: " ", count: size - currentIndentationSpaceSize))
455+
} else {
456+
outputBuffer.enqueueSpaces(size)
457+
}
453458

454459
// Print any indentation required, followed by the text content of the syntax token.
455460
case .syntax(let text):

Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3465,7 +3465,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
34653465

34663466
case .spaces(let n):
34673467
guard leadingIndent == .spaces(0) else { break }
3468-
leadingIndent = .spaces(n)
3468+
if config.allowWhitespaceOnlyLines, trivia.count > index + 1, trivia[index + 1].isNewline {
3469+
appendToken(.space(size: n))
3470+
requiresNextNewline = true
3471+
} else {
3472+
leadingIndent = .spaces(n)
3473+
}
34693474
case .tabs(let n):
34703475
guard leadingIndent == .spaces(0) else { break }
34713476
leadingIndent = .tabs(n)

Sources/_SwiftFormatTestSupport/Configuration+Testing.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ extension Configuration {
4141
config.spacesAroundRangeFormationOperators = false
4242
config.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
4343
config.multiElementCollectionTrailingCommas = true
44+
config.allowWhitespaceOnlyLines = false
4445
return config
4546
}
4647
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import SwiftFormat
2+
3+
final class AllowWhitespaceOnlyLinesTests: PrettyPrintTestCase {
4+
func testAllowWhitespaceOnlyLinesEnabled() {
5+
let input =
6+
"""
7+
class A {
8+
func foo() -> Int {
9+
return 1
10+
}
11+
12+
func bar() -> Int {
13+
return 2
14+
}
15+
}
16+
"""
17+
18+
let expected =
19+
"""
20+
class A {
21+
func foo() -> Int {
22+
return 1
23+
}
24+
25+
func bar() -> Int {
26+
return 2
27+
}
28+
}
29+
30+
"""
31+
var config = Configuration.forTesting
32+
config.allowWhitespaceOnlyLines = true
33+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
34+
}
35+
36+
func testAllowWhitespaceOnlyLinesDisabled() {
37+
let input =
38+
"""
39+
class A {
40+
func foo() -> Int {
41+
return 1
42+
}
43+
44+
func bar() -> Int {
45+
return 2
46+
}
47+
}
48+
"""
49+
50+
let expected =
51+
"""
52+
class A {
53+
func foo() -> Int {
54+
return 1
55+
}
56+
57+
func bar() -> Int {
58+
return 2
59+
}
60+
}
61+
62+
"""
63+
var config = Configuration.forTesting
64+
config.allowWhitespaceOnlyLines = false
65+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
66+
}
67+
68+
func testExpressionsWithUnnecessaryWhitespaces() {
69+
let input =
70+
"""
71+
class A {
72+
func foo() -> Int {
73+
return 1
74+
}
75+
76+
func bar() -> Int {
77+
return 2
78+
}
79+
}
80+
"""
81+
82+
let expected =
83+
"""
84+
class A {
85+
func foo() -> Int {
86+
return 1
87+
}
88+
89+
func bar() -> Int {
90+
return 2
91+
}
92+
}
93+
94+
"""
95+
var config = Configuration.forTesting
96+
config.allowWhitespaceOnlyLines = true
97+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
98+
}
99+
}

0 commit comments

Comments
 (0)