mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
Revert recent #if indentation changes related to method chains (#2345)
This commit is contained in:
+12
-113
@@ -28,7 +28,6 @@ public extension FormatRule {
|
||||
var linewrapStack = [false]
|
||||
var lineIndex = 0
|
||||
var preserveIfdefDepth = 0
|
||||
var noIndentIfdefDepth = 0
|
||||
|
||||
@discardableResult
|
||||
func applyIndent(_ indent: String, at index: Int) -> Int {
|
||||
@@ -58,39 +57,6 @@ public extension FormatRule {
|
||||
scopeStack.removeLast()
|
||||
}
|
||||
|
||||
// Returns the change in index after applying indent if needed for noIndent ifdef
|
||||
func applyNoIndentIfdefFix(ifdefIndent: String, at startIndex: Int) -> Int {
|
||||
let currentIndent = formatter.currentIndentForLine(at: startIndex)
|
||||
let isNested = noIndentIfdefDepth > 1
|
||||
if currentIndent.count < ifdefIndent.count ||
|
||||
(isNested && currentIndent.count > ifdefIndent.count)
|
||||
{
|
||||
return applyIndent(ifdefIndent, at: startIndex)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func matchingIfdefIndent(forDirectiveAt directiveIndex: Int, fallbackIndent: String) -> String {
|
||||
var nestedEndifCount = 0
|
||||
var index = directiveIndex - 1
|
||||
while index >= 0 {
|
||||
let token = formatter.tokens[index]
|
||||
switch token {
|
||||
case .endOfScope("#endif"):
|
||||
nestedEndifCount += 1
|
||||
case .startOfScope("#if"):
|
||||
if nestedEndifCount == 0 {
|
||||
return formatter.currentIndentForLine(at: index)
|
||||
}
|
||||
nestedEndifCount -= 1
|
||||
default:
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
return fallbackIndent
|
||||
}
|
||||
|
||||
var i = i
|
||||
switch token {
|
||||
case let .startOfScope(string):
|
||||
@@ -149,26 +115,7 @@ public extension FormatRule {
|
||||
i += applyIndent(indent, at: formatter.startOfLine(at: i))
|
||||
indent += formatter.options.indent
|
||||
case .noIndent:
|
||||
let currentIndent = formatter.currentIndentForLine(at: i)
|
||||
// Check if previous non-blank line starts with #if/#else/#elseif (directly nested)
|
||||
var directlyNestedInIfdef = false
|
||||
if noIndentIfdefDepth > 0,
|
||||
let prevLinebreakIndex = formatter.index(of: .linebreak, before: i)
|
||||
{
|
||||
let prevLineStartIndex = formatter.startOfLine(at: prevLinebreakIndex, excludingIndent: true)
|
||||
if let prevLineStartToken = formatter.token(at: prevLineStartIndex),
|
||||
[.startOfScope("#if"), .keyword("#else"), .keyword("#elseif")].contains(prevLineStartToken)
|
||||
{
|
||||
directlyNestedInIfdef = true
|
||||
}
|
||||
}
|
||||
if currentIndent.count < indent.count || directlyNestedInIfdef {
|
||||
// Under-indented, or directly nested #if (should be at outer #if level)
|
||||
i += applyIndent(indent, at: formatter.startOfLine(at: i))
|
||||
} else {
|
||||
// At or above expected level (e.g., in method chain), keep it
|
||||
indent = currentIndent
|
||||
}
|
||||
i += applyIndent(indent, at: formatter.startOfLine(at: i))
|
||||
case .preserve:
|
||||
indent = formatter.currentIndentForLine(at: i)
|
||||
case .outdent:
|
||||
@@ -176,8 +123,6 @@ public extension FormatRule {
|
||||
}
|
||||
if formatter.options.ifdefIndent == .preserve {
|
||||
preserveIfdefDepth += 1
|
||||
} else if formatter.options.ifdefIndent == .noIndent {
|
||||
noIndentIfdefDepth += 1
|
||||
}
|
||||
case "{" where formatter.isFirstStackedClosureArgument(at: i):
|
||||
guard var prevIndex = formatter.index(of: .nonSpace, before: i) else {
|
||||
@@ -281,19 +226,12 @@ public extension FormatRule {
|
||||
}
|
||||
let start = formatter.startOfLine(at: i)
|
||||
switch formatter.options.ifdefIndent {
|
||||
case .indent:
|
||||
case .indent, .noIndent:
|
||||
i += applyIndent(indent, at: start)
|
||||
case .noIndent:
|
||||
// #else/#elseif should be at same level as corresponding #if
|
||||
let fallbackIndent = indentStack.last ?? ""
|
||||
let targetIndent = matchingIfdefIndent(forDirectiveAt: i, fallbackIndent: fallbackIndent)
|
||||
if formatter.currentIndentForLine(at: start) != targetIndent {
|
||||
i += applyIndent(targetIndent, at: start)
|
||||
}
|
||||
case .preserve:
|
||||
break
|
||||
case .outdent:
|
||||
i += applyIndent("", at: start)
|
||||
case .preserve:
|
||||
break
|
||||
}
|
||||
case .keyword("@unknown") where scopeStack.last != .startOfScope("#if"):
|
||||
var indent = indentStack[indentStack.count - 2]
|
||||
@@ -359,9 +297,6 @@ public extension FormatRule {
|
||||
// Handle end of scope
|
||||
if let scope = scopeStack.last, token.isEndOfScope(scope) {
|
||||
let indentCount = indentCounts.last! - 1
|
||||
// Capture #if indent before popScope for noIndent handling
|
||||
let ifdefIndentBeforePop = (token == .endOfScope("#endif") && formatter.options.ifdefIndent == .noIndent)
|
||||
? indentStack.last : nil
|
||||
popScope()
|
||||
guard !token.isLinebreak, lineIndex > scopeStartLineIndexes.last ?? -1 else {
|
||||
break
|
||||
@@ -412,16 +347,6 @@ public extension FormatRule {
|
||||
|
||||
if token == .endOfScope("#endif"), formatter.options.ifdefIndent == .outdent {
|
||||
i += applyIndent("", at: start)
|
||||
} else if token == .endOfScope("#endif"), formatter.options.ifdefIndent == .noIndent {
|
||||
// #endif should be at same level as corresponding #if
|
||||
// Align to the indent of the matching #if when available.
|
||||
let fallbackIndent = ifdefIndentBeforePop ?? indentStack.last ?? ""
|
||||
let targetIndent = matchingIfdefIndent(forDirectiveAt: i, fallbackIndent: fallbackIndent)
|
||||
if formatter.currentIndentForLine(at: start) != targetIndent {
|
||||
i += applyIndent(targetIndent, at: start)
|
||||
}
|
||||
} else if token == .endOfScope("#endif"), formatter.options.ifdefIndent == .preserve {
|
||||
// Do nothing - preserve current position
|
||||
} else {
|
||||
var indent = indentStack.last ?? ""
|
||||
if token.isSwitchCaseOrDefault,
|
||||
@@ -442,26 +367,19 @@ public extension FormatRule {
|
||||
popScope()
|
||||
}
|
||||
switch formatter.options.ifdefIndent {
|
||||
case .indent:
|
||||
case .indent, .noIndent:
|
||||
i += applyIndent(indent, at: formatter.startOfLine(at: i))
|
||||
case .noIndent:
|
||||
// #endif should be at same level as corresponding #if
|
||||
i += applyNoIndentIfdefFix(ifdefIndent: indentStack.last ?? indent, at: formatter.startOfLine(at: i))
|
||||
case .preserve:
|
||||
break
|
||||
case .outdent:
|
||||
i += applyIndent("", at: formatter.startOfLine(at: i))
|
||||
case .preserve:
|
||||
break
|
||||
}
|
||||
if scopeStack.last == .startOfScope("#if") {
|
||||
popScope()
|
||||
}
|
||||
}
|
||||
if token == .endOfScope("#endif") {
|
||||
if formatter.options.ifdefIndent == .preserve {
|
||||
preserveIfdefDepth = max(preserveIfdefDepth - 1, 0)
|
||||
} else if formatter.options.ifdefIndent == .noIndent {
|
||||
noIndentIfdefDepth = max(noIndentIfdefDepth - 1, 0)
|
||||
}
|
||||
if token == .endOfScope("#endif"), formatter.options.ifdefIndent == .preserve {
|
||||
preserveIfdefDepth = max(preserveIfdefDepth - 1, 0)
|
||||
}
|
||||
}
|
||||
switch token {
|
||||
@@ -539,9 +457,7 @@ public extension FormatRule {
|
||||
|
||||
// Determine current indent
|
||||
var indent = indentStack.last ?? ""
|
||||
if linewrapped, lineIndex == scopeStartLineIndexes.last,
|
||||
!(formatter.options.ifdefIndent == .noIndent && noIndentIfdefDepth > 0)
|
||||
{
|
||||
if linewrapped, lineIndex == scopeStartLineIndexes.last {
|
||||
indent = indentStack.count > 1 ? indentStack[indentStack.count - 2] : ""
|
||||
}
|
||||
lineIndex += 1
|
||||
@@ -659,12 +575,6 @@ public extension FormatRule {
|
||||
} else if linewrapped {
|
||||
// Don't indent line starting with dot if previous line was just a closing brace
|
||||
var lastToken = formatter.tokens[lastNonSpaceOrLinebreakIndex]
|
||||
let isConditionalAssignmentLinebreak =
|
||||
formatter.options.ifdefIndent == .noIndent &&
|
||||
noIndentIfdefDepth > 0 &&
|
||||
scopeStack.last == .operator("=", .infix) &&
|
||||
lastNonSpaceOrLinebreakIndex > -1 &&
|
||||
formatter.tokens[lastNonSpaceOrLinebreakIndex] == .operator("=", .infix)
|
||||
if formatter.options.allmanBraces, nextToken == .startOfScope("{"),
|
||||
formatter.isStartOfClosure(at: nextNonSpaceIndex)
|
||||
{
|
||||
@@ -686,13 +596,6 @@ public extension FormatRule {
|
||||
[.keyword("#else"), .keyword("#elseif"), .endOfScope("#endif")].contains(startToken)
|
||||
{
|
||||
indent = formatter.currentIndentForLine(at: lineStart)
|
||||
} else if formatter.options.ifdefIndent == .noIndent,
|
||||
let startToken,
|
||||
[.startOfScope("#if"), .keyword("#else"), .keyword("#elseif")].contains(startToken)
|
||||
{
|
||||
// For noIndent mode, content directly after #if/#else/#elseif should
|
||||
// stay at the directive's level, not be indented as a method chain
|
||||
// (indent already set to indentStack.last which is the #if level)
|
||||
} else if formatter.tokens[lineStart ..< lastNonSpaceOrLinebreakIndex].allSatisfy({
|
||||
$0.isEndOfScope || $0.isSpaceOrComment
|
||||
}) {
|
||||
@@ -709,14 +612,10 @@ public extension FormatRule {
|
||||
indent += formatter.options.indent
|
||||
}
|
||||
} else if !formatter.options.xcodeIndentation || !formatter.isWrappedDeclaration(at: i) {
|
||||
if !isConditionalAssignmentLinebreak {
|
||||
indent += formatter.linewrapIndent(at: i)
|
||||
}
|
||||
}
|
||||
} else if !formatter.options.xcodeIndentation || !formatter.isWrappedDeclaration(at: i) {
|
||||
if !isConditionalAssignmentLinebreak {
|
||||
indent += formatter.linewrapIndent(at: i)
|
||||
}
|
||||
} else if !formatter.options.xcodeIndentation || !formatter.isWrappedDeclaration(at: i) {
|
||||
indent += formatter.linewrapIndent(at: i)
|
||||
}
|
||||
|
||||
linewrapStack[linewrapStack.count - 1] = true
|
||||
|
||||
@@ -3880,13 +3880,13 @@ final class IndentTests: XCTestCase {
|
||||
Text("Hello, world!")
|
||||
// Comment above
|
||||
#if os(macOS)
|
||||
.padding()
|
||||
.padding()
|
||||
#endif
|
||||
|
||||
Text("Hello, world!")
|
||||
#if os(macOS)
|
||||
// Comment inside
|
||||
.padding()
|
||||
// Comment inside
|
||||
.padding()
|
||||
#endif
|
||||
|
||||
// swiftformat:options --ifdef outdent
|
||||
@@ -4575,22 +4575,8 @@ final class IndentTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
class Bar {
|
||||
func foo() {
|
||||
Text("Hello")
|
||||
#if os(iOS)
|
||||
.font(.largeTitle)
|
||||
#elseif os(macOS)
|
||||
.font(.headline)
|
||||
#else
|
||||
.font(.headline)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testIfDefPostfixMemberSyntaxNoIndenting2() {
|
||||
@@ -4659,929 +4645,6 @@ final class IndentTests: XCTestCase {
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
// MARK: - noIndent spec scenarios
|
||||
|
||||
func testNoIndentBasicIfBlock() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfdefFixesOverIndentedElse() {
|
||||
let input = """
|
||||
func example(flag: Bool) {
|
||||
#if os(macOS)
|
||||
if flag {
|
||||
return
|
||||
}
|
||||
#else
|
||||
return
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func example(flag: Bool) {
|
||||
#if os(macOS)
|
||||
if flag {
|
||||
return
|
||||
}
|
||||
#else
|
||||
return
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(indent: " ", ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfdefFixesOverIndentedEndif() {
|
||||
let input = """
|
||||
func example() {
|
||||
#if DEBUG
|
||||
return
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func example() {
|
||||
#if DEBUG
|
||||
return
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(indent: " ", ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfdefFragmentWithUnmatchedDirectivesInsideScope() {
|
||||
let input = """
|
||||
{
|
||||
#endif
|
||||
#else
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
{
|
||||
#endif
|
||||
#else
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent, fragment: true)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfElse() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#else
|
||||
print("release")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#else
|
||||
print("release")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfElseIf() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if os(iOS)
|
||||
print("iOS")
|
||||
#elseif os(macOS)
|
||||
print("macOS")
|
||||
#else
|
||||
print("other")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if os(iOS)
|
||||
print("iOS")
|
||||
#elseif os(macOS)
|
||||
print("macOS")
|
||||
#else
|
||||
print("other")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainAfterClosingBrace() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Hello")
|
||||
}
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Hello")
|
||||
}
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainInlineWithModifiers() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentAlreadyCorrectLevel() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentUnderIndentedIfGetFixed() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentSwitchCases() {
|
||||
let input = """
|
||||
switch value {
|
||||
case .a:
|
||||
break
|
||||
#if DEBUG
|
||||
case .b:
|
||||
break
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
switch value {
|
||||
case .a:
|
||||
break
|
||||
#if DEBUG
|
||||
case .b:
|
||||
break
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentNestedIfBlocks() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if os(iOS)
|
||||
#if DEBUG
|
||||
print("iOS debug")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if os(iOS)
|
||||
#if DEBUG
|
||||
print("iOS debug")
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMultipleStatements() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
let x = 1
|
||||
let y = 2
|
||||
print(x + y)
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
let x = 1
|
||||
let y = 2
|
||||
print(x + y)
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainMultipleModifiers() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
#if os(iOS)
|
||||
.font(.title)
|
||||
.foregroundColor(.blue)
|
||||
.padding()
|
||||
#elseif os(macOS)
|
||||
.font(.headline)
|
||||
.padding(.all, 20)
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
#if os(iOS)
|
||||
.font(.title)
|
||||
.foregroundColor(.blue)
|
||||
.padding()
|
||||
#elseif os(macOS)
|
||||
.font(.headline)
|
||||
.padding(.all, 20)
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainContinuesAfterEndif() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
.background(Color.white)
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
.background(Color.white)
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentFileScope() {
|
||||
let input = """
|
||||
#if DEBUG
|
||||
let debugMode = true
|
||||
#else
|
||||
let debugMode = false
|
||||
#endif
|
||||
"""
|
||||
let output = """
|
||||
#if DEBUG
|
||||
let debugMode = true
|
||||
#else
|
||||
let debugMode = false
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentTypeMembers() {
|
||||
let input = """
|
||||
struct Foo {
|
||||
#if DEBUG
|
||||
var debugValue: Int
|
||||
#endif
|
||||
|
||||
var normalValue: String
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
struct Foo {
|
||||
#if DEBUG
|
||||
var debugValue: Int
|
||||
#endif
|
||||
|
||||
var normalValue: String
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentWithCommentsInside() {
|
||||
let input = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
// This is a debug comment
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func foo() {
|
||||
#if DEBUG
|
||||
// This is a debug comment
|
||||
print("debug")
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentPreservesCorrectLinewrapPosition() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentComplexNestedView() {
|
||||
let input = """
|
||||
var body: some View {
|
||||
HStack {
|
||||
List {
|
||||
Text("Item")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
#if os(iOS)
|
||||
.introspect(.list, on: .iOS(.v15)) { _ in }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v12)) { _ in }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
HStack {
|
||||
List {
|
||||
Text("Item")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
#if os(iOS)
|
||||
.introspect(.list, on: .iOS(.v15)) { _ in }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v12)) { _ in }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
// MARK: - noIndent advanced scenarios (swiftui-introspect patterns)
|
||||
|
||||
func testNoIndentFileLevelWrapperWithNestedCode() {
|
||||
// Pattern: File wrapped in #if with deeply nested struct/function content
|
||||
let input = """
|
||||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct ListTests {
|
||||
typealias PlatformList = UIScrollView
|
||||
|
||||
@Test func introspect() async throws {
|
||||
let entity = try await introspection { spy in
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentFileLevelWrapperWithNestedIfdef() {
|
||||
// Pattern: File-level #if with nested #if for platform-specific typealias
|
||||
let input = """
|
||||
#if !os(tvOS)
|
||||
import SwiftUI
|
||||
|
||||
struct ListTests {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformList = UIScrollView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformList = NSTableView
|
||||
#endif
|
||||
|
||||
func test() {
|
||||
print("test")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainWithCommentInsideIfdef() {
|
||||
// Pattern: #if block with comment before the modifier
|
||||
let input = """
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Text("Hello")
|
||||
}
|
||||
.navigationViewStyle(.columns)
|
||||
#if os(iOS)
|
||||
// NB: this is necessary for introspection to work
|
||||
.introspect(.navigationView, on: .iOS(.v15)) {
|
||||
$0.preferredDisplayMode = .oneOverSecondary
|
||||
}
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Text("Hello")
|
||||
}
|
||||
.navigationViewStyle(.columns)
|
||||
#if os(iOS)
|
||||
// NB: this is necessary for introspection to work
|
||||
.introspect(.navigationView, on: .iOS(.v15)) {
|
||||
$0.preferredDisplayMode = .oneOverSecondary
|
||||
}
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMethodChainWithTrailingClosureInsideIfdef() {
|
||||
// Pattern: #if with modifier containing trailing closure
|
||||
let input = """
|
||||
var body: some View {
|
||||
List {
|
||||
Text("Item")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
#if os(iOS)
|
||||
.introspect(.list, on: .iOS(.v15, .v16)) { tableView in
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
}
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
List {
|
||||
Text("Item")
|
||||
}
|
||||
.listStyle(.plain)
|
||||
#if os(iOS)
|
||||
.introspect(.list, on: .iOS(.v15, .v16)) { tableView in
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
}
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentNestedIfdefAtLinewrapLevel() {
|
||||
// Pattern: #if at linewrap level inside file-level #if wrapper
|
||||
let input = """
|
||||
#if !os(tvOS)
|
||||
struct Tests {
|
||||
func test() {
|
||||
let view = HStack {
|
||||
List {
|
||||
Text("Item")
|
||||
#if os(iOS)
|
||||
.introspect(.list, on: .iOS(.v15), scope: .ancestor) { _ in }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v12), scope: .ancestor) { _ in }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.inset)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentMultipleConsecutiveIfdefBlocks() {
|
||||
// Pattern: Multiple #if blocks in the same method chain
|
||||
let input = """
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.font(.title)
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
#if DEBUG
|
||||
.border(Color.red)
|
||||
#endif
|
||||
.background(Color.white)
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentIfdefWithMultiplePlatformBranches() {
|
||||
// Pattern: Complex #if with multiple #elseif branches
|
||||
let input = """
|
||||
struct Tests {
|
||||
func introspect() {
|
||||
let view = NavigationView {
|
||||
Text("Content")
|
||||
}
|
||||
.navigationViewStyle(.columns)
|
||||
#if os(iOS)
|
||||
.introspect(.navigationView, on: .iOS(.v13, .v14, .v15)) { spy in
|
||||
spy.preferredDisplayMode = .oneOverSecondary
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
.introspect(.navigationView, on: .tvOS(.v13, .v14, .v15)) { spy in
|
||||
// tvOS specific
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.navigationView, on: .macOS(.v10_15, .v11, .v12)) { spy in
|
||||
// macOS specific
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
struct Tests {
|
||||
func introspect() {
|
||||
let view = NavigationView {
|
||||
Text("Content")
|
||||
}
|
||||
.navigationViewStyle(.columns)
|
||||
#if os(iOS)
|
||||
.introspect(.navigationView, on: .iOS(.v13, .v14, .v15)) { spy in
|
||||
spy.preferredDisplayMode = .oneOverSecondary
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
.introspect(.navigationView, on: .tvOS(.v13, .v14, .v15)) { spy in
|
||||
// tvOS specific
|
||||
}
|
||||
#elseif os(macOS)
|
||||
.introspect(.navigationView, on: .macOS(.v10_15, .v11, .v12)) { spy in
|
||||
// macOS specific
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentIfdefInsideClosureArgument() {
|
||||
// Pattern: #if inside a closure that's a function argument
|
||||
let input = """
|
||||
func test() {
|
||||
let result = try await introspection(of: UIScrollView.self) { spy1, spy2 in
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { spy in
|
||||
spy1(spy)
|
||||
}
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 2")
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
func test() {
|
||||
let result = try await introspection(of: UIScrollView.self) { spy1, spy2 in
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
#if os(iOS)
|
||||
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { spy in
|
||||
spy1(spy)
|
||||
}
|
||||
#endif
|
||||
|
||||
List {
|
||||
Text("Item 2")
|
||||
}
|
||||
.listStyle(.grouped)
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentIfdefAfterClosingBraceInChain() {
|
||||
// Pattern: #if immediately after a closing brace in method chain
|
||||
let input = """
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Hello")
|
||||
}
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let output = """
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Hello")
|
||||
}
|
||||
#if os(iOS)
|
||||
.padding()
|
||||
#endif
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testNoIndentDeepNestedIfdefInFileLevelWrapper() {
|
||||
// Pattern: Deeply nested #if inside file-level #if wrapper
|
||||
let input = """
|
||||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct SearchFieldTests {
|
||||
typealias PlatformSearchField = UISearchBar
|
||||
|
||||
@Test func introspect() async throws {
|
||||
try await introspection(of: PlatformSearchField.self) { spy in
|
||||
TabView {
|
||||
NavigationView {
|
||||
Text("Customized")
|
||||
.searchable(text: .constant(""))
|
||||
#if os(iOS)
|
||||
.introspect(.searchField, on: .iOS(.v15, .v16)) { searchBar in
|
||||
spy(searchBar)
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
.introspect(.searchField, on: .tvOS(.v15, .v16)) { searchBar in
|
||||
spy(searchBar)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let output = """
|
||||
#if !os(macOS)
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
struct SearchFieldTests {
|
||||
typealias PlatformSearchField = UISearchBar
|
||||
|
||||
@Test func introspect() async throws {
|
||||
try await introspection(of: PlatformSearchField.self) { spy in
|
||||
TabView {
|
||||
NavigationView {
|
||||
Text("Customized")
|
||||
.searchable(text: .constant(""))
|
||||
#if os(iOS)
|
||||
.introspect(.searchField, on: .iOS(.v15, .v16)) { searchBar in
|
||||
spy(searchBar)
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
.introspect(.searchField, on: .tvOS(.v15, .v16)) { searchBar in
|
||||
spy(searchBar)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentIfdefPreservesCorrectEndifLevel() {
|
||||
// Pattern: Ensure #endif stays at same level as #if when at linewrap position
|
||||
let input = """
|
||||
#if !os(tvOS)
|
||||
struct ListTests {
|
||||
func test() {
|
||||
let view = HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
#if os(iOS) || os(visionOS)
|
||||
.introspect(.list, on: .iOS(.v14, .v15)) { _ in }
|
||||
.introspect(.list, on: .iOS(.v16, .v17)) { _ in }
|
||||
#elseif os(macOS)
|
||||
.introspect(.list, on: .macOS(.v11, .v12)) { _ in }
|
||||
#endif
|
||||
}
|
||||
.listStyle(.inset)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentIfdefWithNestedTypealiasAndFunctions() {
|
||||
// Pattern: Struct with #if for typealias followed by functions
|
||||
let input = """
|
||||
#if !os(tvOS)
|
||||
struct ListWithInsetStyleTests {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithInsetStyle = UIScrollView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListWithInsetStyle = NSTableView
|
||||
#endif
|
||||
|
||||
@Test func introspect() async throws {
|
||||
let (entity1, entity2) = try await introspection(of: PlatformListWithInsetStyle.self) { spy1, spy2 in
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.inset)
|
||||
#if os(iOS) || os(visionOS)
|
||||
.introspect(.list(style: .inset), on: .iOS(.v14, .v15), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .inset), on: .macOS(.v11, .v12), customize: spy1)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let output = """
|
||||
#if !os(tvOS)
|
||||
struct ListWithInsetStyleTests {
|
||||
#if canImport(UIKit)
|
||||
typealias PlatformListWithInsetStyle = UIScrollView
|
||||
#elseif canImport(AppKit)
|
||||
typealias PlatformListWithInsetStyle = NSTableView
|
||||
#endif
|
||||
|
||||
@Test func introspect() async throws {
|
||||
let (entity1, entity2) = try await introspection(of: PlatformListWithInsetStyle.self) { spy1, spy2 in
|
||||
HStack {
|
||||
List {
|
||||
Text("Item 1")
|
||||
}
|
||||
.listStyle(.inset)
|
||||
#if os(iOS) || os(visionOS)
|
||||
.introspect(.list(style: .inset), on: .iOS(.v14, .v15), customize: spy1)
|
||||
#elseif os(macOS)
|
||||
.introspect(.list(style: .inset), on: .macOS(.v11, .v12), customize: spy1)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, output, rule: .indent, options: options, exclude: [.unusedArguments])
|
||||
}
|
||||
|
||||
func testNoIndentIfdefInsideImmediatelyInvokedClosure() {
|
||||
// Pattern: #if inside a closure that's immediately invoked for conditional values
|
||||
let input = """
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
Text("Hello")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: {
|
||||
#if os(iOS)
|
||||
.cancellationAction
|
||||
#else
|
||||
.automatic
|
||||
#endif
|
||||
}()) {
|
||||
Button("Cancel") {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
let options = FormatOptions(ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testIfDefPostfixMemberSyntaxPreserveKeepsAlignment() {
|
||||
let input = """
|
||||
struct Example: View {
|
||||
@@ -6542,27 +5605,6 @@ final class IndentTests: XCTestCase {
|
||||
testFormatting(for: input, output, rule: .indent, exclude: [.wrapMultilineStatementBraces])
|
||||
}
|
||||
|
||||
func testIndentIfExpressionAssignmentInsideNoIndentIfdef() {
|
||||
let input = """
|
||||
#if os(iOS)
|
||||
func makeRecipients(subtitle: String) {
|
||||
let recipients: [INPerson] =
|
||||
if subtitle.isEmpty {
|
||||
[]
|
||||
} else {
|
||||
[
|
||||
subtitle,
|
||||
]
|
||||
}
|
||||
_ = recipients
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
|
||||
let options = FormatOptions(indent: " ", ifdefIndent: .noIndent)
|
||||
testFormatting(for: input, rule: .indent, options: options)
|
||||
}
|
||||
|
||||
func testIndentIfExpressionAssignmentOnSameLine() {
|
||||
let input = """
|
||||
let foo = if let bar {
|
||||
|
||||
Reference in New Issue
Block a user