Files
SwiftFormat/Sources/Rules/TrailingClosures.swift
T
2024-11-09 11:26:22 +00:00

69 lines
2.8 KiB
Swift

//
// TrailingClosures.swift
// SwiftFormat
//
// Created by Nick Lockwood on 1/17/17.
// Copyright © 2024 Nick Lockwood. All rights reserved.
//
import Foundation
public extension FormatRule {
/// Convert closure arguments to trailing closure syntax where possible
static let trailingClosures = FormatRule(
help: "Use trailing closure syntax where applicable.",
options: ["trailingclosures", "nevertrailing"]
) { formatter in
let useTrailing = Set([
"async", "asyncAfter", "sync", "autoreleasepool",
] + formatter.options.trailingClosures)
let nonTrailing = Set([
"performBatchUpdates",
"expect", // Special case to support autoclosure arguments in the Nimble framework
] + formatter.options.neverTrailing)
formatter.forEach(.startOfScope("(")) { i, _ in
guard let prevToken = formatter.last(.nonSpaceOrCommentOrLinebreak, before: i),
case let .identifier(name) = prevToken, // TODO: are trailing closures allowed in other cases?
!nonTrailing.contains(name), !formatter.isConditionalStatement(at: i)
else {
return
}
guard let closingIndex = formatter.index(of: .endOfScope(")"), after: i), let closingBraceIndex =
formatter.index(of: .nonSpaceOrComment, before: closingIndex, if: { $0 == .endOfScope("}") }),
let openingBraceIndex = formatter.index(of: .startOfScope("{"), before: closingBraceIndex),
formatter.index(of: .endOfScope("}"), before: openingBraceIndex) == nil
else {
return
}
guard formatter.next(.nonSpaceOrCommentOrLinebreak, after: closingIndex) != .startOfScope("{"),
var startIndex = formatter.index(of: .nonSpaceOrLinebreak, before: openingBraceIndex)
else {
return
}
switch formatter.tokens[startIndex] {
case .delimiter(","), .startOfScope("("):
break
case .delimiter(":"):
guard useTrailing.contains(name) else {
return
}
if let commaIndex = formatter.index(of: .delimiter(","), before: openingBraceIndex) {
startIndex = commaIndex
} else if formatter.index(of: .startOfScope("("), before: openingBraceIndex) == i {
startIndex = i
} else {
return
}
default:
return
}
let wasParen = (startIndex == i)
formatter.removeParen(at: closingIndex)
formatter.replaceTokens(in: startIndex ..< openingBraceIndex, with:
wasParen ? [.space(" ")] : [.endOfScope(")"), .space(" ")])
}
}
}