mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-06-16 10:34:34 +00:00
Update blankLinesAtStartOfScope rule to support switch cases and closures with captures/params (#1997)
This commit is contained in:
committed by
Cal Stephens
parent
94b850cac0
commit
2fd589f4a0
@@ -191,7 +191,6 @@ extension Declaration {
|
||||
switch keyword {
|
||||
// Properties and property-like declarations
|
||||
case "let", "var", "operator", "precedencegroup":
|
||||
|
||||
if isOverriddenDeclaration, availableTypes.contains(.overriddenProperty) {
|
||||
return .overriddenProperty
|
||||
}
|
||||
|
||||
@@ -9,17 +9,14 @@
|
||||
import Foundation
|
||||
|
||||
public extension FormatRule {
|
||||
/// Remove blank lines immediately after an opening brace, bracket, paren or chevron
|
||||
/// Remove blank lines immediately after an opening brace, bracket, paren, chevron, or colon
|
||||
/// that starts a new scope.
|
||||
static let blankLinesAtStartOfScope = FormatRule(
|
||||
help: "Remove leading blank line at the start of a scope.",
|
||||
options: ["typeblanklines"]
|
||||
) { formatter in
|
||||
formatter.forEach(.startOfScope) { i, token in
|
||||
guard ["{", "(", "[", "<"].contains(token.string),
|
||||
let indexOfFirstLineBreak = formatter.index(of: .nonSpaceOrComment, after: i),
|
||||
// If there is extra code on the same line, ignore it
|
||||
formatter.tokens[indexOfFirstLineBreak].isLinebreak
|
||||
else { return }
|
||||
guard ["{", "(", "[", "<", ":"].contains(token.string) else { return }
|
||||
|
||||
// Consumers can choose whether or not this rule should apply to type bodies
|
||||
if !formatter.options.removeStartOrEndBlankLinesFromTypes,
|
||||
@@ -29,6 +26,23 @@ public extension FormatRule {
|
||||
return
|
||||
}
|
||||
|
||||
// If this is a closure with captures or params, skip to the `in` keyword
|
||||
// before we look for a blank line at the start of the scope.
|
||||
var startOfScope = i
|
||||
if formatter.isStartOfClosure(at: startOfScope),
|
||||
let endOfScope = formatter.endOfScope(at: startOfScope),
|
||||
startOfScope + 1 < endOfScope,
|
||||
let inKeywordIndex = formatter.index(of: .keyword("in"), in: startOfScope + 1 ..< endOfScope),
|
||||
formatter.startOfScope(at: inKeywordIndex) == startOfScope
|
||||
{
|
||||
startOfScope = inKeywordIndex
|
||||
}
|
||||
|
||||
guard let indexOfFirstLineBreak = formatter.index(of: .nonSpaceOrComment, after: startOfScope),
|
||||
// If there is extra code on the same line, ignore it
|
||||
formatter.tokens[indexOfFirstLineBreak].isLinebreak
|
||||
else { return }
|
||||
|
||||
// Find next non-space token
|
||||
var index = indexOfFirstLineBreak + 1
|
||||
var indexOfLastLineBreak = indexOfFirstLineBreak
|
||||
|
||||
@@ -78,7 +78,6 @@ public extension FormatRule {
|
||||
}
|
||||
|
||||
extensionDeclaration.body.forEachRecursiveDeclarationExcludingTypeBodies { bodyDeclaration in
|
||||
|
||||
let visibility = bodyDeclaration.visibility()
|
||||
if memberVisibility > visibility ?? extensionVisibility ?? .internal {
|
||||
if visibility == nil {
|
||||
|
||||
@@ -24,7 +24,6 @@ public extension FormatRule {
|
||||
|| $0.isDeclarationTypeKeyword(including: Array(Token.swiftTypeKeywords))
|
||||
}
|
||||
) { index, token in
|
||||
|
||||
let rangeToSort: ClosedRange<Int>
|
||||
let numberOfLeadingLinebreaks: Int
|
||||
|
||||
|
||||
@@ -118,4 +118,94 @@ class BlankLinesAtStartOfScopeTests: XCTestCase {
|
||||
"""
|
||||
XCTAssertEqual(try lint(input, rules: [.blankLinesAtStartOfScope, .organizeDeclarations]), [])
|
||||
}
|
||||
|
||||
func testRemovesBlankLineFromStartOfSwitchCase() {
|
||||
let input = """
|
||||
switch bool {
|
||||
|
||||
case true:
|
||||
|
||||
print("true")
|
||||
|
||||
case false:
|
||||
|
||||
print("false")
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
switch bool {
|
||||
case true:
|
||||
print("true")
|
||||
|
||||
case false:
|
||||
print("false")
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .blankLinesAtStartOfScope)
|
||||
}
|
||||
|
||||
func testRemovesBlankLineInClosureWithParams() {
|
||||
let input = """
|
||||
presenter.present(viewController, animated: animated) { animated in
|
||||
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
presenter.present(viewController, animated: animated) { animated in
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .blankLinesAtStartOfScope)
|
||||
}
|
||||
|
||||
func testRemovesBlankLineInClosureWithCapture() {
|
||||
let input = """
|
||||
presenter.present(viewController, animated: animated) { [weak self] animated in
|
||||
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
presenter.present(viewController, animated: animated) { [weak self] animated in
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .blankLinesAtStartOfScope)
|
||||
}
|
||||
|
||||
func testRemovesBlankLineInClosureWithActorAnnotion() {
|
||||
let input = """
|
||||
presenter.present(viewController, animated: animated) { @MainActor in
|
||||
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
let output = """
|
||||
presenter.present(viewController, animated: animated) { @MainActor in
|
||||
if animated {
|
||||
self?.completion()
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
testFormatting(for: input, output, rule: .blankLinesAtStartOfScope)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user