From 4cad9a83326146b28b98c6dec4aa08c75841551b Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Sat, 17 Sep 2022 17:47:17 -0500 Subject: [PATCH 1/7] Fix more postfix pound if scenarios --- .../TokenStreamCreator.swift | 38 ++++++++--- .../IfConfigTests.swift | 64 +++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index adad4054f..1ce756aed 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1307,7 +1307,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { before(tokenToOpenWith.nextToken(viewMode: .all), tokens: .break(breakKindClose, newlines: .soft), .close) } - if isNestedInPostfixIfConfig(node: Syntax(node)) { + if node.parent?.parent?.parent?.is(PostfixIfConfigExprSyntax.self) == true { before( node.firstToken, tokens: [ @@ -3481,7 +3481,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if let calledMemberAccessExpr = calledExpression.as(MemberAccessExprSyntax.self) { if calledMemberAccessExpr.base != nil { - if isNestedInPostfixIfConfig(node: Syntax(calledMemberAccessExpr)) { + if isNestedInPostfixIfConfig(node: calledMemberAccessExpr) { before(calledMemberAccessExpr.dot, tokens: [.break(.same, size: 0)]) } else { before(calledMemberAccessExpr.dot, tokens: [.break(.contextual, size: 0)]) @@ -3510,18 +3510,40 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } } -private func isNestedInPostfixIfConfig(node: Syntax) -> Bool { - var this: Syntax? = node +private func isNestedInPostfixIfConfig(node: MemberAccessExprSyntax) -> Bool { + func containsDescendent(ancestor: Syntax, node: MemberAccessExprSyntax) -> Bool { + if ancestor.children(viewMode: .sourceAccurate).contains(Syntax(node)) { + return true + } - while this?.parent != nil { - if this?.parent?.is(PostfixIfConfigExprSyntax.self) == true { + for child in ancestor.children(viewMode: .sourceAccurate) { + if containsDescendent(ancestor: child, node: node) { return true } - - this = this?.parent } return false + } + + var this: Syntax? = Syntax(node) + + while this?.parent != nil { + if this?.is(TupleExprElementSyntax.self) == true { + return false + } + + if let postfixIfConfig = this?.as(PostfixIfConfigExprSyntax.self) { + if let ifConfigListSyntax = postfixIfConfig.config.children(viewMode: .sourceAccurate).first?.as(IfConfigClauseListSyntax.self) { + if containsDescendent(ancestor: Syntax(ifConfigListSyntax), node: node) { + return true + } + } + } + + this = this?.parent + } + + return false } extension Syntax { diff --git a/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift b/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift index 9547816f0..e1ad9e913 100644 --- a/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift +++ b/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift @@ -390,4 +390,68 @@ final class IfConfigTests: PrettyPrintTestCase { assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) } + + func testPostfixPoundIfBetweenOtherModifiers() { + let input = + """ + EmptyView() + .padding([.vertical]) + #if os(iOS) + .iOSSpecificModifier() + #endif + .commonModifier() + """ + + let expected = + """ + EmptyView() + .padding([.vertical]) + #if os(iOS) + .iOSSpecificModifier() + #endif + .commonModifier() + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) + } + + func testPostfixPoundIfWithTypeInModifier() { + let input = + """ + EmptyView() + .padding([.vertical]) + #if os(iOS) + .iOSSpecificModifier( + SpecificType() + .onChanged { _ in + // do things + } + .onEnded { _ in + // do things + } + ) + #endif + """ + + let expected = + """ + EmptyView() + .padding([.vertical]) + #if os(iOS) + .iOSSpecificModifier( + SpecificType() + .onChanged { _ in + // do things + } + .onEnded { _ in + // do things + } + ) + #endif + + """ + + assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) + } } From fdc708f2a02ad08bbca4f0d2d38e858b5b487d73 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Sat, 17 Sep 2022 18:43:00 -0500 Subject: [PATCH 2/7] Simplify the implementation --- .../TokenStreamCreator.swift | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 1ce756aed..21e966422 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -3511,20 +3511,6 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } private func isNestedInPostfixIfConfig(node: MemberAccessExprSyntax) -> Bool { - func containsDescendent(ancestor: Syntax, node: MemberAccessExprSyntax) -> Bool { - if ancestor.children(viewMode: .sourceAccurate).contains(Syntax(node)) { - return true - } - - for child in ancestor.children(viewMode: .sourceAccurate) { - if containsDescendent(ancestor: child, node: node) { - return true - } - } - - return false - } - var this: Syntax? = Syntax(node) while this?.parent != nil { @@ -3532,12 +3518,9 @@ private func isNestedInPostfixIfConfig(node: MemberAccessExprSyntax) -> Bool { return false } - if let postfixIfConfig = this?.as(PostfixIfConfigExprSyntax.self) { - if let ifConfigListSyntax = postfixIfConfig.config.children(viewMode: .sourceAccurate).first?.as(IfConfigClauseListSyntax.self) { - if containsDescendent(ancestor: Syntax(ifConfigListSyntax), node: node) { - return true - } - } + if this?.is(IfConfigDeclSyntax.self) == true && + this?.parent?.is(PostfixIfConfigExprSyntax.self) == true { + return true } this = this?.parent From 1361dd51dde7280b8e8cf23c1a12e8c300e9d9f7 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Sun, 18 Sep 2022 11:46:15 -0500 Subject: [PATCH 3/7] Simplify again --- Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 21e966422..953d0661a 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1307,7 +1307,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { before(tokenToOpenWith.nextToken(viewMode: .all), tokens: .break(breakKindClose, newlines: .soft), .close) } - if node.parent?.parent?.parent?.is(PostfixIfConfigExprSyntax.self) == true { + if isNestedInPostfixIfConfig(node: Syntax(node)) { before( node.firstToken, tokens: [ @@ -3481,7 +3481,7 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if let calledMemberAccessExpr = calledExpression.as(MemberAccessExprSyntax.self) { if calledMemberAccessExpr.base != nil { - if isNestedInPostfixIfConfig(node: calledMemberAccessExpr) { + if isNestedInPostfixIfConfig(node: Syntax(calledMemberAccessExpr)) { before(calledMemberAccessExpr.dot, tokens: [.break(.same, size: 0)]) } else { before(calledMemberAccessExpr.dot, tokens: [.break(.contextual, size: 0)]) @@ -3510,8 +3510,8 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } } -private func isNestedInPostfixIfConfig(node: MemberAccessExprSyntax) -> Bool { - var this: Syntax? = Syntax(node) +private func isNestedInPostfixIfConfig(node: Syntax) -> Bool { + var this: Syntax? = node while this?.parent != nil { if this?.is(TupleExprElementSyntax.self) == true { From d7b390fafce18fa8e734c196a6a90fbde430f8a0 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Mon, 6 Feb 2023 16:50:19 -0600 Subject: [PATCH 4/7] Fix formatting of postfix pound ifs --- .../TokenStreamCreator.swift | 13 +++- .../IfConfigTests.swift | 72 +++++++++---------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 953d0661a..d7caf656f 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1308,11 +1308,22 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { } if isNestedInPostfixIfConfig(node: Syntax(node)) { + let breakToken: Token + let previousToken = node.parent?.parent?.previousToken + + if previousToken?.parent?.parent?.parent?.parent?.syntaxNodeType == IfConfigClauseSyntax.self || + previousToken?.text == "}" { + breakToken = .break(.reset) + } else { + breakToken = .break + before(node.parent?.parent?.as(IfConfigDeclSyntax.self)?.poundEndif, tokens: [.break]) + } + before( node.firstToken, tokens: [ .printerControl(kind: .enableBreaking), - .break(.reset), + breakToken, ] ) } else if let condition = node.condition { diff --git a/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift b/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift index e1ad9e913..9a01fe631 100644 --- a/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift +++ b/Tests/SwiftFormatPrettyPrintTests/IfConfigTests.swift @@ -247,10 +247,10 @@ final class IfConfigTests: PrettyPrintTestCase { """ VStack { Text("something") - #if os(iOS) - .iOSSpecificModifier() - #endif - .commonModifier() + #if os(iOS) + .iOSSpecificModifier() + #endif + .commonModifier() } """ @@ -277,13 +277,13 @@ final class IfConfigTests: PrettyPrintTestCase { """ VStack { Text("something") - #if os(iOS) - .iOSSpecificModifier() - .anotherModifier() - .anotherAnotherModifier() - #endif - .commonModifier() - .anotherCommonModifier() + #if os(iOS) + .iOSSpecificModifier() + .anotherModifier() + .anotherAnotherModifier() + #endif + .commonModifier() + .anotherCommonModifier() } """ @@ -311,14 +311,14 @@ final class IfConfigTests: PrettyPrintTestCase { """ VStack { Text("something") - #if os(iOS) || os(watchOS) - #if os(iOS) - .iOSModifier() - #else - .watchOSModifier() + #if os(iOS) || os(watchOS) + #if os(iOS) + .iOSModifier() + #else + .watchOSModifier() + #endif + .iOSAndWatchOSModifier() #endif - .iOSAndWatchOSModifier() - #endif } """ @@ -343,10 +343,10 @@ final class IfConfigTests: PrettyPrintTestCase { """ VStack { textView - #if os(iOS) - .iOSSpecificModifier() - #endif - .commonModifier() + #if os(iOS) + .iOSSpecificModifier() + #endif + .commonModifier() } """ @@ -406,9 +406,9 @@ final class IfConfigTests: PrettyPrintTestCase { """ EmptyView() .padding([.vertical]) - #if os(iOS) - .iOSSpecificModifier() - #endif + #if os(iOS) + .iOSSpecificModifier() + #endif .commonModifier() """ @@ -438,17 +438,17 @@ final class IfConfigTests: PrettyPrintTestCase { """ EmptyView() .padding([.vertical]) - #if os(iOS) - .iOSSpecificModifier( - SpecificType() - .onChanged { _ in - // do things - } - .onEnded { _ in - // do things - } - ) - #endif + #if os(iOS) + .iOSSpecificModifier( + SpecificType() + .onChanged { _ in + // do things + } + .onEnded { _ in + // do things + } + ) + #endif """ From 4fd796fa98c598756e6ffe3ba3e6d4b51a2204f0 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Mon, 6 Feb 2023 17:22:02 -0600 Subject: [PATCH 5/7] Improve implementation --- .../TokenStreamCreator.swift | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index d7caf656f..e62186d26 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1309,14 +1309,15 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if isNestedInPostfixIfConfig(node: Syntax(node)) { let breakToken: Token - let previousToken = node.parent?.parent?.previousToken + let currentIfConfigDecl = node.parent?.parent?.as(IfConfigDeclSyntax.self) + let tokenBeforeCurrentIfConfigDecl = currentIfConfigDecl?.previousToken - if previousToken?.parent?.parent?.parent?.parent?.syntaxNodeType == IfConfigClauseSyntax.self || - previousToken?.text == "}" { + if isNestedInIfConfig(Syntax(tokenBeforeCurrentIfConfigDecl)) || + tokenBeforeCurrentIfConfigDecl?.text == "}" { breakToken = .break(.reset) } else { breakToken = .break - before(node.parent?.parent?.as(IfConfigDeclSyntax.self)?.poundEndif, tokens: [.break]) + before(currentIfConfigDecl?.poundEndif, tokens: [.break]) } before( @@ -3525,6 +3526,9 @@ private func isNestedInPostfixIfConfig(node: Syntax) -> Bool { var this: Syntax? = node while this?.parent != nil { + // This guard handles the situation where a type with its own modifiers + // is nested inside of an if config. That type should not count as being + // in a postfix if config because its entire body is inside the if config. if this?.is(TupleExprElementSyntax.self) == true { return false } @@ -3540,6 +3544,20 @@ private func isNestedInPostfixIfConfig(node: Syntax) -> Bool { return false } +private func isNestedInIfConfig(node: Syntax) -> Bool { + var this: Syntax? = node + + while this?.parent != nil { + if this?.is(IfConfigClauseSyntax.self) == true { + return true + } + + this = this?.parent + } + + return false +} + extension Syntax { /// Creates a pretty-printable token stream for the provided Syntax node. func makeTokenStream(configuration: Configuration, operatorContext: OperatorContext) -> [Token] { From dea0d251800ae632b02dd3b75f3c4625c0e82a29 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Tue, 7 Feb 2023 10:33:49 -0600 Subject: [PATCH 6/7] Fix build issues after bringing in the latest from main --- Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 43e156699..394099f73 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1333,9 +1333,10 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { if isNestedInPostfixIfConfig(node: Syntax(node)) { let breakToken: Token let currentIfConfigDecl = node.parent?.parent?.as(IfConfigDeclSyntax.self) - let tokenBeforeCurrentIfConfigDecl = currentIfConfigDecl?.previousToken - if isNestedInIfConfig(Syntax(tokenBeforeCurrentIfConfigDecl)) || + if let currentIfConfigDecl = currentIfConfigDecl, + let tokenBeforeCurrentIfConfigDecl = currentIfConfigDecl?.previousToken, + isNestedInIfConfig(node: Syntax(tokenBeforeCurrentIfConfigDecl)) || tokenBeforeCurrentIfConfigDecl?.text == "}" { breakToken = .break(.reset) } else { From 79b9d3aa7afdefcb21c2e735a81f0b59713a9955 Mon Sep 17 00:00:00 2001 From: David Brunow <> Date: Tue, 7 Feb 2023 10:40:18 -0600 Subject: [PATCH 7/7] Fix more build errors --- Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift index 394099f73..aee1d892e 100644 --- a/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift +++ b/Sources/SwiftFormatPrettyPrint/TokenStreamCreator.swift @@ -1335,9 +1335,9 @@ fileprivate final class TokenStreamCreator: SyntaxVisitor { let currentIfConfigDecl = node.parent?.parent?.as(IfConfigDeclSyntax.self) if let currentIfConfigDecl = currentIfConfigDecl, - let tokenBeforeCurrentIfConfigDecl = currentIfConfigDecl?.previousToken, + let tokenBeforeCurrentIfConfigDecl = currentIfConfigDecl.previousToken, isNestedInIfConfig(node: Syntax(tokenBeforeCurrentIfConfigDecl)) || - tokenBeforeCurrentIfConfigDecl?.text == "}" { + tokenBeforeCurrentIfConfigDecl.text == "}" { breakToken = .break(.reset) } else { breakToken = .break