diff --git a/Rules.md b/Rules.md
index c2c77b96..d775d593 100644
--- a/Rules.md
+++ b/Rules.md
@@ -277,12 +277,18 @@ Insert blank line after import statements.
## blankLineAfterSwitchCase
-Insert a blank line after multiline switch cases (excluding the last case,
+Insert a blank line after switch cases (excluding the last case,
which is followed by a closing brace).
+Option | Description
+--- | ---
+`--blank-line-after-switch-case` | Insert line After switch cases: "always" or "multiline-only" (default)
+
Examples
+`--blank-line-after-switch-case multiline-only` (default)
+
```diff
func handle(_ action: SpaceshipAction) {
switch action {
@@ -304,6 +310,58 @@ which is followed by a closing brace).
}
```
+```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ warpDrive.activate()
+
+ case let .scanPlanet(planet):
+ scanner.scanForArticialLife()
+
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+```
+`--blank-line-after-switch-case always`
+
+```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ navigationComputer.destination = targetedDestination
+ await warpDrive.spinUp()
+ warpDrive.activate()
++
+ case let .scanPlanet(planet):
+ scanner.target = planet
+ scanner.scanAtmosphere()
+ scanner.scanBiosphere()
+ scanner.scanForArticialLife()
++
+ case .handleIncomingEnergyBlast:
+ await energyShields.prepare()
+ energyShields.engage()
+ }
+ }
+```
+
+```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ warpDrive.activate()
++
+ case let .scanPlanet(planet):
+ scanner.scanForArticialLife()
++
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+```
+
diff --git a/Sources/OptionDescriptor.swift b/Sources/OptionDescriptor.swift
index 3e8ce989..6d2f92ce 100644
--- a/Sources/OptionDescriptor.swift
+++ b/Sources/OptionDescriptor.swift
@@ -1347,6 +1347,12 @@ struct _Descriptors {
trueValues: ["true"],
falseValues: ["false"]
)
+ let blankLineAfterSwitchCase = OptionDescriptor(
+ argumentName: "blank-line-after-switch-case",
+ displayName: "Blank Line After Switch Cases",
+ help: "Insert line After switch cases: \"always\" or \"multiline-only\" (default)",
+ keyPath: \.blankLineAfterSwitchCase
+ )
// MARK: - Internal
diff --git a/Sources/Options.swift b/Sources/Options.swift
index fe0a9d71..6002a327 100644
--- a/Sources/Options.swift
+++ b/Sources/Options.swift
@@ -647,6 +647,13 @@ public enum EquatableMacro: Equatable, RawRepresentable, CustomStringConvertible
}
}
+public enum BlankLineAfterSwitchCase: String, CaseIterable {
+ /// Add blank lines after multiline switch cases only
+ case multilineOnly = "multiline-only"
+ /// Always add blank lines after switch cases
+ case always
+}
+
public enum URLMacro: Equatable, RawRepresentable, CustomStringConvertible {
/// No URL macro
case none
@@ -795,6 +802,7 @@ public struct FormatOptions: CustomStringConvertible {
public var urlMacro: URLMacro
public var preferFileMacro: Bool
public var lineBetweenConsecutiveGuards: Bool
+ public var blankLineAfterSwitchCase: BlankLineAfterSwitchCase
/// Deprecated
public var indentComments: Bool
@@ -928,6 +936,7 @@ public struct FormatOptions: CustomStringConvertible {
urlMacro: URLMacro = .none,
preferFileMacro: Bool = true,
lineBetweenConsecutiveGuards: Bool = false,
+ blankLineAfterSwitchCase: BlankLineAfterSwitchCase = .multilineOnly,
// Doesn't really belong here, but hard to put elsewhere
fragment: Bool = false,
ignoreConflictMarkers: Bool = false,
@@ -1050,7 +1059,7 @@ public struct FormatOptions: CustomStringConvertible {
self.urlMacro = urlMacro
self.preferFileMacro = preferFileMacro
self.lineBetweenConsecutiveGuards = lineBetweenConsecutiveGuards
- // Doesn't really belong here, but hard to put elsewhere
+ self.blankLineAfterSwitchCase = blankLineAfterSwitchCase
self.indentComments = indentComments
self.fragment = fragment
self.ignoreConflictMarkers = ignoreConflictMarkers
diff --git a/Sources/Rules/BlankLineAfterSwitchCase.swift b/Sources/Rules/BlankLineAfterSwitchCase.swift
index 78a34ed7..908f6605 100644
--- a/Sources/Rules/BlankLineAfterSwitchCase.swift
+++ b/Sources/Rules/BlankLineAfterSwitchCase.swift
@@ -11,19 +11,22 @@ import Foundation
public extension FormatRule {
static let blankLineAfterSwitchCase = FormatRule(
help: """
- Insert a blank line after multiline switch cases (excluding the last case,
+ Insert a blank line after switch cases (excluding the last case,
which is followed by a closing brace).
""",
disabledByDefault: true,
- orderAfter: [.redundantBreak]
+ orderAfter: [.redundantBreak],
+ options: ["blank-line-after-switch-case"]
) { formatter in
formatter.forEach(.keyword("switch")) { switchIndex, _ in
guard let switchCases = formatter.switchStatementBranchesWithSpacingInfo(at: switchIndex) else { return }
+ let shouldAlwaysInsertBlankLineAfterSwitchCase = formatter.options.blankLineAfterSwitchCase == .always
for switchCase in switchCases.reversed() {
- // Any switch statement that spans multiple lines should be followed by a blank line
+ // Any switch statement should be followed by a blank line, depending on the
+ // `blankLineAfterSwitchCase` option.
// (excluding the last case, which is followed by a closing brace).
- if switchCase.spansMultipleLines,
+ if shouldAlwaysInsertBlankLineAfterSwitchCase || switchCase.spansMultipleLines,
!switchCase.isLastCase,
!switchCase.isFollowedByBlankLine
{
@@ -41,6 +44,8 @@ public extension FormatRule {
}
} examples: {
#"""
+ `--blank-line-after-switch-case multiline-only` (default)
+
```diff
func handle(_ action: SpaceshipAction) {
switch action {
@@ -61,6 +66,58 @@ public extension FormatRule {
}
}
```
+
+ ```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ warpDrive.activate()
+
+ case let .scanPlanet(planet):
+ scanner.scanForArticialLife()
+
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+ ```
+ `--blank-line-after-switch-case always`
+
+ ```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ navigationComputer.destination = targetedDestination
+ await warpDrive.spinUp()
+ warpDrive.activate()
+ +
+ case let .scanPlanet(planet):
+ scanner.target = planet
+ scanner.scanAtmosphere()
+ scanner.scanBiosphere()
+ scanner.scanForArticialLife()
+ +
+ case .handleIncomingEnergyBlast:
+ await energyShields.prepare()
+ energyShields.engage()
+ }
+ }
+ ```
+
+ ```diff
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ case .engageWarpDrive:
+ warpDrive.activate()
+ +
+ case let .scanPlanet(planet):
+ scanner.scanForArticialLife()
+ +
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+ ```
"""#
}
}
diff --git a/Tests/Rules/BlankLineAfterSwitchCaseTests.swift b/Tests/Rules/BlankLineAfterSwitchCaseTests.swift
index 4d851051..b9be130f 100644
--- a/Tests/Rules/BlankLineAfterSwitchCaseTests.swift
+++ b/Tests/Rules/BlankLineAfterSwitchCaseTests.swift
@@ -46,6 +46,39 @@ class BlankLineAfterSwitchCaseTests: XCTestCase {
testFormatting(for: input, output, rule: .blankLineAfterSwitchCase)
}
+ func testAddsBlankLineAfterSingleSwitchCasesWhenBlankLineAroundSingleLineCases() {
+ let input = """
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ // The warp drive can be engaged by pressing a button on the control panel
+ case .engageWarpDrive:
+ warpDrive.activate()
+ // Triggered automatically whenever we detect an energy blast was fired in our direction
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+ """
+
+ let output = """
+ func handle(_ action: SpaceshipAction) {
+ switch action {
+ // The warp drive can be engaged by pressing a button on the control panel
+ case .engageWarpDrive:
+ warpDrive.activate()
+
+ // Triggered automatically whenever we detect an energy blast was fired in our direction
+ case .handleIncomingEnergyBlast:
+ energyShields.engage()
+ }
+ }
+ """
+ testFormatting(for: input,
+ output,
+ rule: .blankLineAfterSwitchCase,
+ options: FormatOptions(blankLineAfterSwitchCase: .always))
+ }
+
func testRemovesBlankLineAfterLastSwitchCase() {
let input = """
func handle(_ action: SpaceshipAction) {