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

Merge main into release/6.1 #911

Merged
merged 8 commits into from
Jan 8, 2025
43 changes: 35 additions & 8 deletions Sources/SwiftFormat/Rules/NoEmptyLineOpeningClosingBraces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ public final class NoEmptyLinesOpeningClosingBraces: SyntaxFormatRule {
}

func rewritten(_ token: TokenSyntax) -> TokenSyntax {
let (trimmedLeadingTrivia, count) = token.leadingTrivia.trimmingSuperfluousNewlines()
let (trimmedLeadingTrivia, count) = token.leadingTrivia.trimmingSuperfluousNewlines(
fromClosingBrace: token.tokenKind == .rightBrace
)
if trimmedLeadingTrivia.sourceLength != token.leadingTriviaLength {
diagnose(.removeEmptyLinesBefore(count), on: token, anchor: .start)
return token.with(\.leadingTrivia, trimmedLeadingTrivia)
Expand All @@ -83,7 +85,7 @@ public final class NoEmptyLinesOpeningClosingBraces: SyntaxFormatRule {
if let first = collection.first, first.leadingTrivia.containsNewlines,
let index = collection.index(of: first)
{
let (trimmedLeadingTrivia, count) = first.leadingTrivia.trimmingSuperfluousNewlines()
let (trimmedLeadingTrivia, count) = first.leadingTrivia.trimmingSuperfluousNewlines(fromClosingBrace: false)
if trimmedLeadingTrivia.sourceLength != first.leadingTriviaLength {
diagnose(.removeEmptyLinesAfter(count), on: first, anchor: .leadingTrivia(0))
var first = first
Expand All @@ -96,24 +98,49 @@ public final class NoEmptyLinesOpeningClosingBraces: SyntaxFormatRule {
}

extension Trivia {
func trimmingSuperfluousNewlines() -> (Trivia, Int) {
func trimmingSuperfluousNewlines(fromClosingBrace: Bool) -> (Trivia, Int) {
var trimmmed = 0
var pendingNewlineCount = 0
let pieces = self.indices.reduce([TriviaPiece]()) { (partialResult, index) in
let piece = self[index]
// Collapse consecutive newlines into a single one
if case .newlines(let count) = piece {
if let last = partialResult.last, last.isNewline {
trimmmed += count
return partialResult
if fromClosingBrace {
if index == self.count - 1 {
// For the last index(newline right before the closing brace), collapse into a single newline
trimmmed += count - 1
return partialResult + [.newlines(1)]
} else {
pendingNewlineCount += count
return partialResult
}
} else {
trimmmed += count - 1
return partialResult + [.newlines(1)]
if let last = partialResult.last, last.isNewline {
trimmmed += count
return partialResult
} else if index == 0 {
// For leading trivia not associated with a closing brace, collapse the first newline into a single one
trimmmed += count - 1
return partialResult + [.newlines(1)]
} else {
return partialResult + [piece]
}
}
}
// Remove spaces/tabs surrounded by newlines
if piece.isSpaceOrTab, index > 0, index < self.count - 1, self[index - 1].isNewline, self[index + 1].isNewline {
return partialResult
}
// Handle pending newlines if there are any
if pendingNewlineCount > 0 {
if index < self.count - 1 {
let newlines = TriviaPiece.newlines(pendingNewlineCount)
pendingNewlineCount = 0
return partialResult + [newlines] + [piece]
} else {
return partialResult + [.newlines(1)] + [piece]
}
}
// Retain other trivia pieces
return partialResult + [piece]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,60 @@ final class NoEmptyLinesOpeningClosingBracesTests: LintOrFormatRuleTestCase {
]
)
}

func testNoEmptyLinesOpeningClosingBracesInFunctionBeginningAndEndingWithComment() {
assertFormatting(
NoEmptyLinesOpeningClosingBraces.self,
input: """
func myFunc() {
// Some comment here

// Do a thing
var x = doAThing()

// Do a thing

var y = doAThing()

// Some other comment here
}
""",
expected: """
func myFunc() {
// Some comment here

// Do a thing
var x = doAThing()

// Do a thing

var y = doAThing()

// Some other comment here
}
"""
)
}

func testNoEmptyLinesOpeningClosingBracesInFunctionWithEmptyLinesOnly() {
assertFormatting(
NoEmptyLinesOpeningClosingBraces.self,
input: """
func myFunc() {





1️⃣}
""",
expected: """
func myFunc() {
}
""",
findings: [
FindingSpec("1️⃣", message: "remove empty lines before '}'")
]
)
}
}
Loading