Skip to content

Fix closing brace indentation with --trimwhitespace nonblank-lines#2477

Merged
calda merged 2 commits intodevelopfrom
copilot/fix-indentation-closing-brace
Mar 25, 2026
Merged

Fix closing brace indentation with --trimwhitespace nonblank-lines#2477
calda merged 2 commits intodevelopfrom
copilot/fix-indentation-closing-brace

Conversation

Copy link
Contributor

Copilot AI commented Mar 25, 2026

When --trimwhitespace nonblank-lines is set, blank lines before closing braces retain trailing whitespace. blankLinesAtEndOfScope would remove only the linebreak token when eliminating such a blank line, leaving the trailing space stranded on the same line as the closing brace's indent token. The indent rule then replaced that stranded space rather than the brace's own indent, producing doubled indentation.

Changes

  • Sources/FormattingHelpers.swift — In removeTrailingBlankLinesIfPresent, after removing the last linebreak of a trailing blank line, also remove any trailing whitespace token that preceded it (identified by the pattern linebreak → space → linebreak).

  • Tests/Rules/BlankLinesAtEndOfScopeTests.swift — Added testBlankLinesAtEndOfScopeRemovedWithTrailingWhitespace covering blank lines with trailing whitespace before a closing brace under truncateBlankLines: false.

Example

// Input (blank line has trailing spaces, --trimwhitespace nonblank-lines)
func foo() {
    if let bar = bar {
        bar()
    }
    8 spaces here
}

// Before fix
func foo() {
    if let bar = bar {
        bar()
    }
     }  // ← extra space

// After fix
func foo() {
    if let bar = bar {
        bar()
    }
}
Original prompt

This section details on the original issue you should resolve

<issue_title>Closing brace is indented incorrectly with --trimwhitespace nonblank-lines</issue_title>
<issue_description>The indentation of the closing brace of func deletePackage() in the example below is 5 spaces instead of 4. This incorrectly formatted code then gets cached, so running the formatter again doesn't fix it (unless the cache is bypassed with --cache ignore).

Unformatted code

final class AudioPackageStoreProtocolMock: AudioPackageStoreProtocol {
    init() { }




    private(set) var deletePackageCallCount = 0
    var deletePackageArgValues = [Product.Format]()
    var deletePackageHandler: ((Product.Format) async throws -> ())?
    func deletePackage(ofFormat format: Product.Format) async throws {
        deletePackageCallCount += 1
        deletePackageArgValues.append(format)
        if let deletePackageHandler = deletePackageHandler {
            try await deletePackageHandler(format)
        }
        
    }
}

Formatted code

final class AudioPackageStoreProtocolMock: AudioPackageStoreProtocol
{
    init() { }

    private(set) var deletePackageCallCount = 0
    var deletePackageArgValues = [Product.Format]()
    var deletePackageHandler: ((Product.Format) async throws -> Void)?
    func deletePackage(ofFormat format: Product.Format) async throws
    {
        deletePackageCallCount += 1
        deletePackageArgValues.append(format)
        if let deletePackageHandler
        {
            try await deletePackageHandler(format)
        }
     } // ← extra space before this closing brace
}

Environment

SwiftFormat: 0.60.1
Swift compiler: 6.2.4
.swift-version: 6.2

Config
# format options
--allman true
--commas inline
--elseposition next-line
--emptybraces spaced
--guardelse same-line
--importgrouping testable-last

--indentcase true
--nospaceoperators ...
--onelineforeach ignore
--patternlet inline
--selfrequired log
--stripunusedargs closure-only
--trimwhitespace nonblank-lines # Avoid conflict with CTRL+i shortcut
--wrap-arguments after-first
--wrap-parameters after-first
--wrap-collections after-first
--wrap-conditions after-first
--max-width 300
--allow-partial-wrapping false

# file options
--exclude Documentation,Scripts

# default rules
--disable opaqueGenericParameters
--disable strongOutlets
--disable hoistAwait
--disable docComments
--disable redundantProperty
--disable wrapPropertyBodies
--disable wrapFunctionBodies
--disable redundantSendable

# opt-in rules
--enable blankLinesBetweenImports
--enable noExplicitOwnership

<agent_instructions>Create a failing test case for this bug report using the provided repro steps

final class AudioPackageStoreProtocolMock: AudioPackageStoreProtocol {
    init() { }




    private(set) var deletePackageCallCount = 0
    var deletePackageArgValues = [Product.Format]()
    var deletePackageHandler: ((Product.Format) async throws -> ())?
    func deletePackage(ofFormat format: Product.Format) async throws {
        deletePackageCallCount += 1
        deletePackageArgValues.append(format)
        if let deletePackageHandler = deletePackageHandler {
            try await deletePackageHandler(format)
        }
        
    }
}

```</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@calda</author><body>
I can't reproduce the issue. If you share a full Swift file that reproduces the issue when formatted using the given config, I can take another look.</body></comment_new>
</comments>

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@codecov
Copy link

codecov bot commented Mar 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.29%. Comparing base (518a4c1) to head (8082a29).
⚠️ Report is 1 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2477      +/-   ##
===========================================
- Coverage    95.30%   95.29%   -0.01%     
===========================================
  Files          167      167              
  Lines        25330    25337       +7     
===========================================
+ Hits         24141    24146       +5     
- Misses        1189     1191       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI changed the title [WIP] Fix indentation of closing brace in deletePackage function Fix closing brace indentation with --trimwhitespace nonblank-lines Mar 25, 2026
Copilot AI requested a review from calda March 25, 2026 04:17
@calda calda marked this pull request as ready for review March 25, 2026 04:20
@calda calda merged commit cc466c8 into develop Mar 25, 2026
19 of 20 checks passed
@calda calda deleted the copilot/fix-indentation-closing-brace branch March 25, 2026 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants