Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One blank line between declarations #905

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/SwiftFormat/API/Configuration+Default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ extension Configuration {
self.multiElementCollectionTrailingCommas = true
self.reflowMultilineStringLiterals = .never
self.indentBlankLines = false
self.alwaysBreakOnNewScopes = false
}
}
9 changes: 9 additions & 0 deletions Sources/SwiftFormat/API/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public struct Configuration: Codable, Equatable {
case multiElementCollectionTrailingCommas
case reflowMultilineStringLiterals
case indentBlankLines
case alwaysBreakOnNewScopes
}

/// A dictionary containing the default enabled/disabled states of rules, keyed by the rules'
Expand Down Expand Up @@ -268,6 +269,9 @@ public struct Configuration: Codable, Equatable {
/// If false (the default), the whitespace in blank lines will be removed entirely.
public var indentBlankLines: Bool

/// Determines whether to always break on new scopes.
public var alwaysBreakOnNewScopes: Bool

/// Creates a new `Configuration` by loading it from a configuration file.
public init(contentsOf url: URL) throws {
let data = try Data(contentsOf: url)
Expand Down Expand Up @@ -383,6 +387,10 @@ public struct Configuration: Codable, Equatable {
)
?? defaults.indentBlankLines

self.alwaysBreakOnNewScopes =
try container.decodeIfPresent(Bool.self, forKey: .alwaysBreakOnNewScopes)
?? false

// 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
// default-initialized. To get an empty rules dictionary, one can explicitly
Expand Down Expand Up @@ -422,6 +430,7 @@ public struct Configuration: Codable, Equatable {
try container.encode(multiElementCollectionTrailingCommas, forKey: .multiElementCollectionTrailingCommas)
try container.encode(reflowMultilineStringLiterals, forKey: .reflowMultilineStringLiterals)
try container.encode(rules, forKey: .rules)
try container.encode(alwaysBreakOnNewScopes, forKey: .alwaysBreakOnNewScopes)
}

/// Returns the URL of the configuration file that applies to the given file or directory.
Expand Down
19 changes: 13 additions & 6 deletions Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3226,20 +3226,27 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor {
) where BodyContents.Element: SyntaxProtocol {
guard let node = node, let contentsKeyPath = contentsKeyPath else { return }

let isEmpty = areBracesCompletelyEmpty(node, contentsKeyPath: contentsKeyPath)
let newlineBehavior: NewlineBehavior
if config.alwaysBreakOnNewScopes && !isEmpty {
// Force a newline after the left brace.
newlineBehavior = .hard(count: 1)
} else {
newlineBehavior = openBraceNewlineBehavior
}

if shouldResetBeforeLeftBrace {
before(
node.leftBrace,
tokens: .break(.reset, size: 1, newlines: .elective(ignoresDiscretionary: true))
)
}
if !areBracesCompletelyEmpty(node, contentsKeyPath: contentsKeyPath) {
after(
node.leftBrace,
tokens: .break(.open, size: 1, newlines: openBraceNewlineBehavior),
.open
)

if !isEmpty {
after(node.leftBrace, tokens: .break(.open, size: 1, newlines: newlineBehavior), .open)
before(node.rightBrace, tokens: .break(.close, size: 1), .close)
} else {
// If empty scope, keep on the same line if allowed.
after(node.leftBrace, tokens: .break(.open, size: 0, newlines: openBraceNewlineBehavior))
before(node.rightBrace, tokens: .break(.close, size: 0))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import SwiftFormat

final class AlwaysBreakOnNewScopesTests: PrettyPrintTestCase {
func testAlwaysBreakOnNewScopesEnabled() {
let input =
"""
class A {
func foo() -> Int { return 1 }
}
"""

let expected =
"""
class A {
func foo() -> Int {
return 1
}
}

"""
var config = Configuration.forTesting
config.alwaysBreakOnNewScopes = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testAlwaysBreakOnNewScopesDisabled() {
let input =
"""
class A {
func foo() -> Int { return 1 }
}
"""

let expected =
"""
class A {
func foo() -> Int { return 1 }
}

"""
var config = Configuration.forTesting
config.alwaysBreakOnNewScopes = false
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testAlwaysBreakOnNewScopesUnlessScopeIsEmpty() {
let input =
"""
class A {
func foo() -> Int { }
}
"""

let expected =
"""
class A {
func foo() -> Int {}
}

"""
var config = Configuration.forTesting
config.alwaysBreakOnNewScopes = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}

func testAlwaysBreakOnNewScopesNestedScopes() {
let input =
"""
class A {
func foo() -> Int { if true { 1 } else { 2 } }
}
"""

let expected =
"""
class A {
func foo() -> Int {
if true {
1
} else {
2
}
}
}

"""
var config = Configuration.forTesting
config.alwaysBreakOnNewScopes = true
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80, configuration: config)
}
}