@@ -17,6 +17,9 @@ import SwiftSyntax
1717/// imports, and 4) @testable imports. These groups are separated by a single blank line. Blank lines in
1818/// between the import declarations are removed.
1919///
20+ /// By default, imports within conditional compilation blocks (`#if`, `#elseif`, `#else`) are not ordered.
21+ /// This behavior can be controlled via the `orderedImports.includeConditionalImports` configuration option.
22+ ///
2023/// Lint: If an import appears anywhere other than the beginning of the file it resides in,
2124/// not lexicographically ordered, or not in the appropriate import group, a lint error is
2225/// raised.
@@ -26,7 +29,13 @@ import SwiftSyntax
2629public final class OrderedImports : SyntaxFormatRule {
2730
2831 public override func visit( _ node: SourceFileSyntax ) -> SourceFileSyntax {
29- let lines = generateLines ( codeBlockItemList: node. statements, context: context)
32+ var newNode = node
33+ newNode. statements = orderImports ( in: node. statements)
34+ return newNode
35+ }
36+
37+ private func orderImports( in codeBlockItemList: CodeBlockItemListSyntax ) -> CodeBlockItemListSyntax {
38+ let lines = generateLines ( codeBlockItemList: codeBlockItemList, context: context)
3039
3140 // Stores the formatted and sorted lines that will be used to reconstruct the list of code block
3241 // items later.
@@ -47,6 +56,10 @@ public final class OrderedImports: SyntaxFormatRule {
4756 // Perform linting on the grouping of the imports.
4857 checkGrouping ( linesSection)
4958
59+ if let firstLine = fileHeader. first, firstLine. type == . blankLine {
60+ fileHeader. removeFirst ( )
61+ }
62+
5063 if let lastLine = fileHeader. last, lastLine. type == . blankLine {
5164 fileHeader. removeLast ( )
5265 }
@@ -118,6 +131,27 @@ public final class OrderedImports: SyntaxFormatRule {
118131 }
119132 }
120133
134+ if context. configuration. orderedImports. includeConditionalImports,
135+ let syntaxNode = line. syntaxNode,
136+ case . ifConfigCodeBlock( let ifConfigCodeBlock) = syntaxNode
137+ {
138+ var ifConfigDecl = ifConfigCodeBlock. item. cast ( IfConfigDeclSyntax . self)
139+
140+ let newClauses = ifConfigDecl. clauses. map { clause in
141+ guard case . statements( let codeBlockItemList) = clause. elements else {
142+ return clause
143+ }
144+ var newClause = clause
145+ var newCodeBlockItemList = orderImports ( in: codeBlockItemList)
146+ newCodeBlockItemList. leadingTrivia = . newline + newCodeBlockItemList. leadingTrivia
147+ newClause. elements = . statements( newCodeBlockItemList)
148+ return newClause
149+ }
150+
151+ ifConfigDecl. clauses = IfConfigClauseListSyntax ( newClauses)
152+ line. syntaxNode = . ifConfigCodeBlock( CodeBlockItemSyntax ( item: . decl( DeclSyntax ( ifConfigDecl) ) ) )
153+ }
154+
121155 // Separate lines into different categories along with any associated comments.
122156 switch line. type {
123157 case . regularImport:
@@ -154,9 +188,7 @@ public final class OrderedImports: SyntaxFormatRule {
154188 formatAndAppend ( linesSection: lines [ lastSliceStartIndex..< lines. endIndex] )
155189 }
156190
157- var newNode = node
158- newNode. statements = CodeBlockItemListSyntax ( convertToCodeBlockItems ( lines: formattedLines) )
159- return newNode
191+ return CodeBlockItemListSyntax ( convertToCodeBlockItems ( lines: formattedLines) )
160192 }
161193
162194 /// Raise lint errors if the different import types appear in the wrong order, and if import
@@ -354,11 +386,16 @@ private func generateLines(
354386 var blockWithoutTrailingTrivia = block
355387 blockWithoutTrailingTrivia. trailingTrivia = [ ]
356388 currentLine. syntaxNode = . importCodeBlock( blockWithoutTrailingTrivia, sortable: sortable)
389+ } else if block. item. is ( IfConfigDeclSyntax . self) {
390+ if currentLine. syntaxNode != nil {
391+ appendNewLine ( )
392+ }
393+ currentLine. syntaxNode = . ifConfigCodeBlock( block)
357394 } else {
358395 if let syntaxNode = currentLine. syntaxNode {
359396 // Multiple code blocks can be merged, as long as there isn't an import statement.
360397 switch syntaxNode {
361- case . importCodeBlock:
398+ case . importCodeBlock, . ifConfigCodeBlock :
362399 appendNewLine ( )
363400 currentLine. syntaxNode = . nonImportCodeBlocks( [ block] )
364401 case . nonImportCodeBlocks( let existingCodeBlocks) :
@@ -400,6 +437,8 @@ private func convertToCodeBlockItems(lines: [Line]) -> [CodeBlockItemSyntax] {
400437 switch syntaxNode {
401438 case . importCodeBlock( let codeBlock, _) :
402439 append ( codeBlockItem: codeBlock)
440+ case . ifConfigCodeBlock( let ifConfigCodeBlock) :
441+ append ( codeBlockItem: ifConfigCodeBlock)
403442 case . nonImportCodeBlocks( let codeBlocks) :
404443 codeBlocks. forEach ( append ( codeBlockItem: ) )
405444 }
@@ -458,6 +497,9 @@ private class Line {
458497 case nonImportCodeBlocks( [ CodeBlockItemSyntax ] )
459498 /// A single code block item whose content must be an import decl.
460499 case importCodeBlock( CodeBlockItemSyntax , sortable: Bool )
500+ /// A single code block item whose content must be an if config decl.
501+ /// This is used to sort conditional imports.
502+ case ifConfigCodeBlock( CodeBlockItemSyntax )
461503 }
462504
463505 /// Stores line comments. `syntaxNode` need not be defined, since a comment can exist by itself on
@@ -478,7 +520,7 @@ private class Line {
478520 var type : LineType {
479521 if let syntaxNode = syntaxNode {
480522 switch syntaxNode {
481- case . nonImportCodeBlocks:
523+ case . nonImportCodeBlocks, . ifConfigCodeBlock :
482524 return . codeBlock
483525 case . importCodeBlock( let importCodeBlock, _) :
484526 guard let importDecl = importCodeBlock. item. as ( ImportDeclSyntax . self) else {
@@ -542,6 +584,8 @@ private class Line {
542584 return codeBlock. firstToken ( viewMode: . sourceAccurate)
543585 case . nonImportCodeBlocks( let codeBlocks) :
544586 return codeBlocks. first? . firstToken ( viewMode: . sourceAccurate)
587+ case . ifConfigCodeBlock( let codeBlock) :
588+ return codeBlock. firstToken ( viewMode: . sourceAccurate)
545589 }
546590 }
547591
@@ -592,6 +636,8 @@ extension Line: CustomStringConvertible {
592636 description += " \( codeBlocks. count) code blocks "
593637 case . importCodeBlock( _, let sortable) :
594638 description += " \( sortable ? " sorted " : " unsorted " ) import \( importName) "
639+ case . ifConfigCodeBlock:
640+ description += " if config code block "
595641 }
596642 }
597643
0 commit comments