Skip to content

Commit

Permalink
Add AllowWhitespaceOnlyLines configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
TTOzzi committed Sep 6, 2024
1 parent 4a3def9 commit 162a271
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 3 deletions.
3 changes: 3 additions & 0 deletions Documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ top-level keys and values:

* `multiElementCollectionTrailingCommas` _(boolean)_: Determines whether multi-element collection literals should have trailing commas.
Defaults to `true`.

* `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 but will be modified to match the indentation.
Defaults to `false`

> TODO: Add support for enabling/disabling specific syntax transformations in
> the pipeline.
Expand Down
1 change: 1 addition & 0 deletions Sources/SwiftFormat/API/Configuration+Default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ extension Configuration {
self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
self.multiElementCollectionTrailingCommas = true
self.reflowMultilineStringLiterals = .never
self.allowWhitespaceOnlyLines = false
}
}
12 changes: 11 additions & 1 deletion Sources/SwiftFormat/API/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public struct Configuration: Codable, Equatable {
case noAssignmentInExpressions
case multiElementCollectionTrailingCommas
case reflowMultilineStringLiterals
case allowWhitespaceOnlyLines
}

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

public var reflowMultilineStringLiterals: MultilineStringReflowBehavior

/// Determines whether lines containing only whitespace should be preserved or removed.
///
/// If true, lines that consist solely of whitespace will be modified to match the current indentation
/// without removing the whitespace. If false (the default), the whitespace on such lines will be completely removed.
public var allowWhitespaceOnlyLines: Bool

/// Creates a new `Configuration` by loading it from a configuration file.
public init(contentsOf url: URL) throws {
Expand Down Expand Up @@ -352,10 +359,13 @@ public struct Configuration: Codable, Equatable {
try container.decodeIfPresent(
Bool.self, forKey: .multiElementCollectionTrailingCommas)
?? defaults.multiElementCollectionTrailingCommas

self.reflowMultilineStringLiterals =
try container.decodeIfPresent(MultilineStringReflowBehavior.self, forKey: .reflowMultilineStringLiterals)
?? defaults.reflowMultilineStringLiterals
self.allowWhitespaceOnlyLines =
try container.decodeIfPresent(
Bool.self, forKey: .allowWhitespaceOnlyLines)
?? defaults.allowWhitespaceOnlyLines

// If the `rules` key is not present at all, default it to the built-in set
// so that the behavior is the same as if the configuration had been
Expand Down
6 changes: 5 additions & 1 deletion Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,11 @@ public class PrettyPrinter {

// Print out the number of spaces according to the size, and adjust spaceRemaining.
case .space(let size, _):
outputBuffer.enqueueSpaces(size)
if configuration.allowWhitespaceOnlyLines, outputBuffer.isAtStartOfLine {
outputBuffer.write(String())
} else {
outputBuffer.enqueueSpaces(size)
}

// Print any indentation required, followed by the text content of the syntax token.
case .syntax(let text):
Expand Down
7 changes: 6 additions & 1 deletion Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3465,7 +3465,12 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {

case .spaces(let n):
guard leadingIndent == .spaces(0) else { break }
leadingIndent = .spaces(n)
if config.allowWhitespaceOnlyLines, trivia.count > index + 1, trivia[index + 1].isNewline {
appendToken(.space(size: n))
requiresNextNewline = true
} else {
leadingIndent = .spaces(n)
}
case .tabs(let n):
guard leadingIndent == .spaces(0) else { break }
leadingIndent = .tabs(n)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension Configuration {
config.spacesAroundRangeFormationOperators = false
config.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration()
config.multiElementCollectionTrailingCommas = true
config.allowWhitespaceOnlyLines = false
return config
}
}
163 changes: 163 additions & 0 deletions Tests/SwiftFormatTests/PrettyPrint/AllowWhitespaceOnlyLinesTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import SwiftFormat

final class AllowWhitespaceOnlyLinesTests: PrettyPrintTestCase {
func testAllowWhitespaceOnlyLinesEnabled() {
let input =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""
var config = Configuration.forTesting
config.allowWhitespaceOnlyLines = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testAllowWhitespaceOnlyLinesDisabled() {
let input =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
func bar() -> Int {
return 2
}
}
"""
var config = Configuration.forTesting
config.allowWhitespaceOnlyLines = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testLineWithMoreWhitespacesThanIndentation() {
let input =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}\u{0020}\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""
var config = Configuration.forTesting
config.allowWhitespaceOnlyLines = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testLineWithFewerWhitespacesThanIndentation() {
let input =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}
func bar() -> Int {
return 2
}
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""
var config = Configuration.forTesting
config.allowWhitespaceOnlyLines = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testExpressionsWithUnnecessaryWhitespaces() {
let input =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
\u{0020}\u{0020}
func bar() -> Int {
return 2
}
}
"""
var config = Configuration.forTesting
config.allowWhitespaceOnlyLines = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}
}

0 comments on commit 162a271

Please sign in to comment.