mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
Add new format rule for wrapping case bodies (#2476)
This commit is contained in:
@@ -136,6 +136,7 @@
|
||||
* [unusedPrivateDeclarations](#unusedPrivateDeclarations)
|
||||
* [urlMacro](#urlMacro)
|
||||
* [validateTestCases](#validateTestCases)
|
||||
* [wrapCaseBodies](#wrapCaseBodies)
|
||||
* [wrapConditionalBodies](#wrapConditionalBodies)
|
||||
* [wrapEnumCases](#wrapEnumCases)
|
||||
* [wrapMultilineConditionalAssignment](#wrapMultilineConditionalAssignment)
|
||||
@@ -4338,6 +4339,22 @@ Option | Description
|
||||
</details>
|
||||
<br/>
|
||||
|
||||
## wrapCaseBodies
|
||||
|
||||
Wrap the bodies of inline switch cases onto a new line.
|
||||
|
||||
<details>
|
||||
<summary>Examples</summary>
|
||||
|
||||
```diff
|
||||
- case .foo: return bar
|
||||
+ case .foo:
|
||||
+ return bar
|
||||
```
|
||||
|
||||
</details>
|
||||
<br/>
|
||||
|
||||
## wrapConditionalBodies
|
||||
|
||||
Wrap the bodies of inline conditional statements onto a new line.
|
||||
|
||||
@@ -141,6 +141,7 @@ let ruleRegistry: [String: FormatRule] = [
|
||||
"wrap": .wrap,
|
||||
"wrapArguments": .wrapArguments,
|
||||
"wrapAttributes": .wrapAttributes,
|
||||
"wrapCaseBodies": .wrapCaseBodies,
|
||||
"wrapConditionalBodies": .wrapConditionalBodies,
|
||||
"wrapEnumCases": .wrapEnumCases,
|
||||
"wrapFunctionBodies": .wrapFunctionBodies,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// WrapCaseBodies.swift
|
||||
// SwiftFormat
|
||||
//
|
||||
// Created by Kim de Vos on 3/23/26.
|
||||
// Copyright © 2026 Nick Lockwood. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension FormatRule {
|
||||
static let wrapCaseBodies = FormatRule(
|
||||
help: "Wrap the bodies of inline switch cases onto a new line.",
|
||||
disabledByDefault: true,
|
||||
sharedOptions: ["linebreaks", "indent"]
|
||||
) { formatter in
|
||||
formatter.forEach(.endOfScope("case")) { i, _ in
|
||||
formatter.wrapCaseBody(at: i)
|
||||
}
|
||||
formatter.forEach(.endOfScope("default")) { i, _ in
|
||||
formatter.wrapCaseBody(at: i)
|
||||
}
|
||||
} examples: {
|
||||
"""
|
||||
```diff
|
||||
- case .foo: return bar
|
||||
+ case .foo:
|
||||
+ return bar
|
||||
```
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
extension Formatter {
|
||||
func wrapCaseBody(at caseIndex: Int) {
|
||||
guard let colonIndex = index(of: .startOfScope(":"), after: caseIndex),
|
||||
var firstTokenIndex = index(of: .nonSpaceOrComment, after: colonIndex),
|
||||
!tokens[firstTokenIndex].isLinebreak,
|
||||
!tokens[firstTokenIndex].isEndOfScope
|
||||
else { return }
|
||||
|
||||
insertLinebreak(at: firstTokenIndex)
|
||||
|
||||
if tokens[firstTokenIndex - 1].isSpace {
|
||||
removeToken(at: firstTokenIndex - 1)
|
||||
firstTokenIndex -= 1
|
||||
}
|
||||
|
||||
let movedTokenIndex = firstTokenIndex + 1
|
||||
let indent = currentIndentForLine(at: caseIndex) + options.indent
|
||||
insertSpace(indent, at: movedTokenIndex)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
//
|
||||
// WrapCaseBodiesTests.swift
|
||||
// SwiftFormatTests
|
||||
//
|
||||
// Created by Kim de Vos on 3/23/26.
|
||||
// Copyright © 2026 Nick Lockwood. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SwiftFormat
|
||||
|
||||
final class WrapCaseBodiesTests: XCTestCase {
|
||||
func testWrapSingleLineCaseBody() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar: return bar
|
||||
default: return baz
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
return bar
|
||||
default:
|
||||
return baz
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testAlreadyWrappedCaseBodiesUnchanged() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
return bar
|
||||
default:
|
||||
return baz
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testWrapDefaultCaseBody() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar: break
|
||||
default: return baz
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
break
|
||||
default:
|
||||
return baz
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testWrapMultiPatternCaseBody() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar, .baz: return quux
|
||||
default: break
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar, .baz:
|
||||
return quux
|
||||
default:
|
||||
break
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies,
|
||||
exclude: [.wrapSwitchCases])
|
||||
}
|
||||
|
||||
func testWrapCaseWithWhereClause() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case let x where x > 0: return x
|
||||
default: return 0
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case let x where x > 0:
|
||||
return x
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testCaseWithCommentAfterColonUnchanged() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar: // comment
|
||||
return bar
|
||||
default:
|
||||
return baz
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, rule: .wrapCaseBodies,
|
||||
exclude: [.blankLineAfterSwitchCase])
|
||||
}
|
||||
|
||||
func testWrapUnknownDefaultCaseBody() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar: return bar
|
||||
@unknown default: return baz
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
return bar
|
||||
@unknown default:
|
||||
return baz
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testWrapNestedSwitchCaseBodies() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
switch baz {
|
||||
case .a: return a
|
||||
case .b: return b
|
||||
default: return c
|
||||
}
|
||||
default: return other
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
switch baz {
|
||||
case .a:
|
||||
return a
|
||||
case .b:
|
||||
return b
|
||||
default:
|
||||
return c
|
||||
}
|
||||
default:
|
||||
return other
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies,
|
||||
exclude: [.blankLineAfterSwitchCase])
|
||||
}
|
||||
|
||||
func testWrapCaseBodiesFullExample() {
|
||||
let input = """
|
||||
extension Int {
|
||||
var foo: String {
|
||||
switch self {
|
||||
case 0: return "zero"
|
||||
case 1: return "one"
|
||||
case 2: return "two"
|
||||
default: return "other"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
extension Int {
|
||||
var foo: String {
|
||||
switch self {
|
||||
case 0:
|
||||
return "zero"
|
||||
case 1:
|
||||
return "one"
|
||||
case 2:
|
||||
return "two"
|
||||
default:
|
||||
return "other"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testWrapCaseBodyWithAssignment() {
|
||||
let input = """
|
||||
switch foo {
|
||||
case .bar: baz = true
|
||||
default: baz = false
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch foo {
|
||||
case .bar:
|
||||
baz = true
|
||||
default:
|
||||
baz = false
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
|
||||
func testWrapSwitchExpressionCaseBodies() {
|
||||
let input = """
|
||||
extension Int {
|
||||
var foo: String {
|
||||
return switch self {
|
||||
case 0: "zero"
|
||||
case 1: "one"
|
||||
case 2: "two"
|
||||
default: "other"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
extension Int {
|
||||
var foo: String {
|
||||
return switch self {
|
||||
case 0:
|
||||
"zero"
|
||||
case 1:
|
||||
"one"
|
||||
case 2:
|
||||
"two"
|
||||
default:
|
||||
"other"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
testFormatting(for: input, output, rule: .wrapCaseBodies)
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,7 @@ extension XCTestCase {
|
||||
.unusedPrivateDeclarations,
|
||||
.preferFinalClasses,
|
||||
.preferExplicitFalse,
|
||||
.wrapCaseBodies,
|
||||
]
|
||||
let exclude = exclude + defaultExclusions.filter { !rules.contains($0) }
|
||||
let formatResult: (output: String, changes: [SwiftFormat.Formatter.Change])
|
||||
|
||||
Reference in New Issue
Block a user