Skip to content

Commit db2958c

Browse files
committed
Order conditional imports based on configuration and add nested test case
1 parent 64032a7 commit db2958c

File tree

6 files changed

+144
-3
lines changed

6 files changed

+144
-3
lines changed

Documentation/Configuration.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,19 @@ too long.
296296

297297
**default:** `false`
298298

299+
---
300+
301+
### `orderedImports`
302+
**type:** object
303+
304+
**description:** Configuration for the `OrderedImports` rule.
305+
306+
- `includeConditionalImports` _(boolean)_: Determines whether imports within conditional compilation blocks (`#if`, `#elseif`, `#else`) should be ordered. When `true`, imports inside conditional blocks will be sorted and organized according to the same rules as top-level imports. When `false`, imports within conditional blocks are left in their original order.
307+
308+
**default:** `{ "includeConditionalImports" : false }`
309+
310+
---
311+
299312
> TODO: Add support for enabling/disabling specific syntax transformations in
300313
> the pipeline.
301314

Documentation/RuleDocumentation.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@ The order of the import groups is 1) regular imports, 2) declaration imports, 3)
411411
imports, and 4) @testable imports. These groups are separated by a single blank line. Blank lines in
412412
between the import declarations are removed.
413413

414+
By default, imports within conditional compilation blocks (`#if`, `#elseif`, `#else`) are not ordered. This behavior can be controlled via the `orderedImports.includeConditionalImports`
415+
configuration option.
416+
414417
Lint: If an import appears anywhere other than the beginning of the file it resides in,
415418
not lexicographically ordered, or not in the appropriate import group, a lint error is
416419
raised.

Sources/SwiftFormat/API/Configuration+Default.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,6 @@ extension Configuration {
4343
self.multiElementCollectionTrailingCommas = true
4444
self.reflowMultilineStringLiterals = .never
4545
self.indentBlankLines = false
46+
self.orderedImports = OrderedImportsConfiguration()
4647
}
4748
}

Sources/SwiftFormat/API/Configuration.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public struct Configuration: Codable, Equatable {
4848
case multiElementCollectionTrailingCommas
4949
case reflowMultilineStringLiterals
5050
case indentBlankLines
51+
case orderedImports
5152
}
5253

5354
/// A dictionary containing the default enabled/disabled states of rules, keyed by the rules'
@@ -301,6 +302,9 @@ public struct Configuration: Codable, Equatable {
301302
/// If false (the default), the whitespace in blank lines will be removed entirely.
302303
public var indentBlankLines: Bool
303304

305+
/// Configuration for the `OrderedImports` rule.
306+
public var orderedImports: OrderedImportsConfiguration
307+
304308
/// Creates a new `Configuration` by loading it from a configuration file.
305309
public init(contentsOf url: URL) throws {
306310
let data = try Data(contentsOf: url)
@@ -443,6 +447,13 @@ public struct Configuration: Codable, Equatable {
443447
)
444448
?? defaults.indentBlankLines
445449

450+
self.orderedImports =
451+
try container.decodeIfPresent(
452+
OrderedImportsConfiguration.self,
453+
forKey: .orderedImports
454+
)
455+
?? defaults.orderedImports
456+
446457
// If the `rules` key is not present at all, default it to the built-in set
447458
// so that the behavior is the same as if the configuration had been
448459
// default-initialized. To get an empty rules dictionary, one can explicitly
@@ -481,6 +492,8 @@ public struct Configuration: Codable, Equatable {
481492
try container.encode(noAssignmentInExpressions, forKey: .noAssignmentInExpressions)
482493
try container.encode(multiElementCollectionTrailingCommas, forKey: .multiElementCollectionTrailingCommas)
483494
try container.encode(reflowMultilineStringLiterals, forKey: .reflowMultilineStringLiterals)
495+
try container.encode(indentBlankLines, forKey: .indentBlankLines)
496+
try container.encode(orderedImports, forKey: .orderedImports)
484497
try container.encode(rules, forKey: .rules)
485498
}
486499

@@ -546,3 +559,11 @@ public struct NoAssignmentInExpressionsConfiguration: Codable, Equatable {
546559

547560
public init() {}
548561
}
562+
563+
/// Configuration for the `OrderedImports` rule.
564+
public struct OrderedImportsConfiguration: Codable, Equatable {
565+
/// Determines whether imports within conditional compilation blocks should be ordered.
566+
public var includeConditionalImports: Bool = false
567+
568+
public init() {}
569+
}

Sources/SwiftFormat/Rules/OrderedImports.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ public final class OrderedImports: SyntaxFormatRule {
127127
}
128128
}
129129

130-
if let syntaxNode = line.syntaxNode, case .ifConfigCodeBlock(let ifConfigCodeBlock) = syntaxNode {
130+
if context.configuration.orderedImports.includeConditionalImports,
131+
let syntaxNode = line.syntaxNode,
132+
case .ifConfigCodeBlock(let ifConfigCodeBlock) = syntaxNode
133+
{
131134
var ifConfigDecl = ifConfigCodeBlock.item.cast(IfConfigDeclSyntax.self)
132135

133136
let newClauses = ifConfigDecl.clauses.map { clause in

Tests/SwiftFormatTests/Rules/OrderedImportsTests.swift

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,10 @@ final class OrderedImportsTests: LintOrFormatRuleTestCase {
609609
)
610610
}
611611

612-
func testConditionalImports() {
612+
func testConditionalImportsWhenEnabled() {
613+
var configuration = Configuration.forTesting
614+
configuration.orderedImports.includeConditionalImports = true
615+
613616
assertFormatting(
614617
OrderedImports.self,
615618
input: """
@@ -650,7 +653,104 @@ final class OrderedImportsTests: LintOrFormatRuleTestCase {
650653
FindingSpec("2️⃣", message: "sort import statements lexicographically"),
651654
FindingSpec("3️⃣", message: "sort import statements lexicographically"),
652655
FindingSpec("4️⃣", message: "place imports at the top of the file"),
653-
]
656+
],
657+
configuration: configuration
658+
)
659+
}
660+
661+
func testConditionalImportsWhenDisabled() {
662+
var configuration = Configuration.forTesting
663+
configuration.orderedImports.includeConditionalImports = false
664+
665+
assertFormatting(
666+
OrderedImports.self,
667+
input: """
668+
import Zebras
669+
1️⃣import Apples
670+
#if canImport(Darwin)
671+
import Foundation
672+
import Darwin
673+
#elseif canImport(Glibc)
674+
import Glibc
675+
import Foundation
676+
#endif
677+
2️⃣import Aardvarks
678+
679+
foo()
680+
bar()
681+
baz()
682+
""",
683+
expected: """
684+
import Aardvarks
685+
import Apples
686+
import Zebras
687+
688+
#if canImport(Darwin)
689+
import Foundation
690+
import Darwin
691+
#elseif canImport(Glibc)
692+
import Glibc
693+
import Foundation
694+
#endif
695+
696+
foo()
697+
bar()
698+
baz()
699+
""",
700+
findings: [
701+
FindingSpec("1️⃣", message: "sort import statements lexicographically"),
702+
FindingSpec("2️⃣", message: "place imports at the top of the file"),
703+
],
704+
configuration: configuration
705+
)
706+
}
707+
708+
func testNestedConditionalImports() {
709+
var configuration = Configuration()
710+
configuration.orderedImports.includeConditionalImports = true
711+
712+
assertFormatting(
713+
OrderedImports.self,
714+
input: """
715+
import A
716+
#if FOO
717+
import D
718+
#if BAR
719+
import F
720+
1️⃣import E
721+
#else
722+
import H
723+
2️⃣import G
724+
#endif
725+
3️⃣5️⃣import C
726+
#endif
727+
4️⃣import B
728+
""",
729+
expected: """
730+
import A
731+
import B
732+
733+
#if FOO
734+
import C
735+
import D
736+
737+
#if BAR
738+
import E
739+
import F
740+
#else
741+
import G
742+
import H
743+
#endif
744+
#endif
745+
""",
746+
findings: [
747+
FindingSpec("1️⃣", message: "sort import statements lexicographically"),
748+
FindingSpec("2️⃣", message: "sort import statements lexicographically"),
749+
FindingSpec("3️⃣", message: "place imports at the top of the file"),
750+
FindingSpec("4️⃣", message: "place imports at the top of the file"),
751+
FindingSpec("5️⃣", message: "sort import statements lexicographically"),
752+
],
753+
configuration: configuration
654754
)
655755
}
656756

0 commit comments

Comments
 (0)