@@ -3,24 +3,24 @@ import SwiftSyntax
33
44/// Distributes Swift declarations across multiple files
55public class CodeDistributor {
6-
6+
77 public init ( ) { }
8-
9- /// Distributes declarations from a source file, keeping the first declaration in the original file
10- /// and moving all others to appropriately named separate files.
11- ///
12- /// - Parameters:
13- /// - tree: The syntax tree to distribute
14- /// - originalFileName: The original filename to use for the modified file
15- /// - Returns: Distribution result with modified original file and new files
16- /// - Throws: Distribution errors
8+
9+ /// Distributes declarations from a source file, keeping the first declaration in the original file
10+ /// and moving all others to appropriately named separate files.
11+ ///
12+ /// - Parameters:
13+ /// - tree: The syntax tree to distribute
14+ /// - originalFileName: The original filename to use for the modified file
15+ /// - Returns: Distribution result with modified original file and new files
16+ /// - Throws: Distribution errors
1717 public func distributeKeepingFirst( tree: SyntaxTree , originalFileName: String ) throws -> DistributionResult {
18- // Extract imports and declarations
18+ // Extract imports and declarations
1919 let overview = CodeOverview ( tree: tree, minVisibility: . private) // Include all declarations
2020 let imports = overview. imports
2121 let declarations = overview. declarations
22-
23- // Separate type vs non-type declarations
22+
23+ // Separate type vs non-type declarations
2424 var typeDeclarations : [ DeclarationOverview ] = [ ]
2525 var nonTypeDeclarations : [ DeclarationOverview ] = [ ]
2626 for decl in declarations {
@@ -30,14 +30,14 @@ public class CodeDistributor {
3030 nonTypeDeclarations. append ( decl)
3131 }
3232 }
33-
34- // Build files for type declarations, merging multiple extensions with the same filename
33+
34+ // Build files for type declarations, merging multiple extensions with the same filename
3535 var fileBuilders : [ String : ( content: String , declarations: [ DeclarationOverview ] ) ] = [ : ]
3636 let importHeader : String = {
37- guard !imports. isEmpty else { return " " }
38- return imports. map { " import \( $0) " } . joined ( separator: " \n " ) + " \n \n "
39- } ( )
40-
37+ guard !imports. isEmpty else { return " " }
38+ return imports. map { " import \( $0) " } . joined ( separator: " \n " ) + " \n \n "
39+ } ( )
40+
4141 for declaration in typeDeclarations {
4242 let fileName = generateFileName ( for: declaration, tree: tree)
4343 let declSource = generateDeclarationSource ( declaration, in: tree. sourceFile)
@@ -50,78 +50,78 @@ public class CodeDistributor {
5050 fileBuilders [ fileName] = ( initialContent, [ declaration] )
5151 }
5252 }
53-
53+
5454 let newFiles : [ GeneratedFile ] = fileBuilders. map { key, value in
55- GeneratedFile ( fileName: key, content: value. content, imports: imports, declarations: value. declarations)
56- }
57-
58- // Build modified original file (for non-type code)
55+ GeneratedFile ( fileName: key, content: value. content, imports: imports, declarations: value. declarations)
56+ }
57+
58+ // Build modified original file (for non-type code)
5959 var modifiedOriginalFile : GeneratedFile ? = nil
6060 if !nonTypeDeclarations. isEmpty {
6161 let content = try generateFileContent ( imports: imports, targetDeclarations: nonTypeDeclarations, sourceFile: tree. sourceFile)
6262 modifiedOriginalFile = GeneratedFile ( fileName: originalFileName, content: content, imports: imports, declarations: nonTypeDeclarations)
6363 }
64-
64+
6565 return DistributionResult ( modifiedOriginalFile: modifiedOriginalFile, newFiles: newFiles)
6666 }
67-
68- /// Removes specific declarations from a source file
67+
68+ /// Removes specific declarations from a source file
6969 internal func removeDeclarations( _ declarationsToRemove: [ DeclarationOverview ] , from sourceFile: SourceFileSyntax ) throws -> SourceFileSyntax {
70- // Get the indices of declarations to remove (1-based paths converted to 0-based indices)
70+ // Get the indices of declarations to remove (1-based paths converted to 0-based indices)
7171 let indicesToRemove = Set ( declarationsToRemove. compactMap { declaration -> Int ? in
72- let pathComponents = declaration. path. split ( separator: " . " ) . compactMap { Int ( $0) }
73- guard let firstIndex = pathComponents. first, firstIndex > 0 else { return nil }
74-
75- // Convert to 0-based index in declaration statements (not all statements)
76- return firstIndex - 1
77- } )
78-
79- // Filter statements to remove target declarations
72+ let pathComponents = declaration. path. split ( separator: " . " ) . compactMap { Int ( $0) }
73+ guard let firstIndex = pathComponents. first, firstIndex > 0 else { return nil }
74+
75+ // Convert to 0-based index in declaration statements (not all statements)
76+ return firstIndex - 1
77+ } )
78+
79+ // Filter statements to remove target declarations
8080 var newStatements : [ CodeBlockItemSyntax ] = [ ]
8181 var declarationIndex = 0
82-
82+
8383 for statement in sourceFile. statements {
84- // Check if this is a declaration statement (not import or other)
84+ // Check if this is a declaration statement (not import or other)
8585 if let declSyntax = statement. item. as ( DeclSyntax . self) {
86- // Skip import declarations - they don't count towards declaration indices
86+ // Skip import declarations - they don't count towards declaration indices
8787 if declSyntax. is ( ImportDeclSyntax . self) {
8888 newStatements. append ( statement)
8989 continue
9090 }
91-
92- // Check if this declaration should be removed
91+
92+ // Check if this declaration should be removed
9393 if indicesToRemove. contains ( declarationIndex) {
94- // Skip this declaration (remove it)
94+ // Skip this declaration (remove it)
9595 declarationIndex += 1
9696 continue
9797 } else {
98- // Keep this declaration
98+ // Keep this declaration
9999 newStatements. append ( statement)
100100 declarationIndex += 1
101101 }
102102 } else {
103- // Non-declaration statement, keep it
103+ // Non-declaration statement, keep it
104104 newStatements. append ( statement)
105105 }
106106 }
107-
108- // Create new source file with modified statements
107+
108+ // Create new source file with modified statements
109109 return sourceFile. with ( \. statements, CodeBlockItemListSyntax ( newStatements) )
110110 }
111-
112- /// Generates file content for a single declaration
111+
112+ /// Generates file content for a single declaration
113113 internal func generateFileContentForDeclaration( _ declaration: DeclarationOverview , imports: [ String ] , sourceFile: SourceFileSyntax ) throws -> String {
114114 var content = " "
115- // Add imports
115+ // Add imports
116116 if !imports. isEmpty {
117117 for importName in imports {
118118 content += " import \( importName) \n "
119119 }
120120 content += " \n " // Only one newline after all imports
121121 }
122- // Add the target declaration
122+ // Add the target declaration
123123 if let declSyntax = findDeclarationSyntax ( for: declaration, in: sourceFile) {
124- // Apply access control rewriting for extracted declarations
124+ // Apply access control rewriting for extracted declarations
125125 let rewriter = AccessControlRewriter ( )
126126 let rewrittenDecl = rewriter. visit ( declSyntax)
127127 var declString = rewrittenDecl. description
@@ -130,90 +130,90 @@ public class CodeDistributor {
130130 }
131131 return content
132132 }
133-
134- /// Generates appropriate filename for a declaration
133+
134+ /// Generates appropriate filename for a declaration
135135 internal func generateFileName( for declaration: DeclarationOverview , tree: SyntaxTree ) -> String {
136136 if declaration. type == " extension " {
137137 return generateExtensionFileName ( for: declaration, tree: tree)
138138 } else {
139139 return " \( declaration. name) .swift "
140140 }
141141 }
142-
143- /// Generates filename for extension declarations
142+
143+ /// Generates filename for extension declarations
144144 internal func generateExtensionFileName( for declaration: DeclarationOverview , tree: SyntaxTree ) -> String {
145- // Try to extract the extended type name and protocol conformances
145+ // Try to extract the extended type name and protocol conformances
146146 if let extensionInfo = extractExtensionInfo ( for: declaration, tree: tree) {
147147 if !extensionInfo. protocols. isEmpty {
148- // Extension with protocol conformance: Type+Protocol.swift
148+ // Extension with protocol conformance: Type+Protocol.swift
149149 let protocolName = extensionInfo. protocols. joined ( separator: " + " )
150150 return " \( extensionInfo. typeName) + \( protocolName) .swift "
151151 } else {
152- // Extension without protocol: Type+Extensions.swift
152+ // Extension without protocol: Type+Extensions.swift
153153 return " \( extensionInfo. typeName) +Extensions.swift "
154154 }
155155 } else {
156- // Fallback
156+ // Fallback
157157 return " \( declaration. name) +Extensions.swift "
158158 }
159159 }
160-
161- /// Information extracted from an extension declaration
160+
161+ /// Information extracted from an extension declaration
162162 internal struct ExtensionInfo {
163163 let typeName : String
164164 let protocols : [ String ]
165165 }
166-
167- /// Extracts type name and protocol conformances from an extension
166+
167+ /// Extracts type name and protocol conformances from an extension
168168 internal func extractExtensionInfo( for declaration: DeclarationOverview , tree: SyntaxTree ) -> ExtensionInfo ? {
169- // Find the extension syntax node using the declaration path
169+ // Find the extension syntax node using the declaration path
170170 guard let declSyntax = findDeclarationSyntax ( for: declaration, in: tree. sourceFile) else {
171- return nil
172- }
173-
174- // Try to cast to ExtensionDeclSyntax
171+ return nil
172+ }
173+
174+ // Try to cast to ExtensionDeclSyntax
175175 guard let extensionNode = declSyntax. as ( ExtensionDeclSyntax . self) else {
176- return nil
177- }
178-
176+ return nil
177+ }
178+
179179 let typeName = extensionNode. extendedType. description. trimmingCharacters ( in: . whitespacesAndNewlines)
180-
180+
181181 var protocols : [ String ] = [ ]
182182 if let inheritanceClause = extensionNode. inheritanceClause {
183183 for inheritedType in inheritanceClause. inheritedTypes {
184184 let protocolName = inheritedType. type. description. trimmingCharacters ( in: . whitespacesAndNewlines)
185185 protocols. append ( protocolName)
186186 }
187187 }
188-
188+
189189 return ExtensionInfo ( typeName: typeName, protocols: protocols)
190190 }
191-
192- /// Finds the syntax node for a declaration using its path
191+
192+ /// Finds the syntax node for a declaration using its path
193193 internal func findDeclarationSyntax( for declaration: DeclarationOverview , in sourceFile: SourceFileSyntax ) -> DeclSyntax ? {
194194 let pathComponents = declaration. path. split ( separator: " . " ) . compactMap { Int ( $0) }
195-
195+
196196 guard let firstIndex = pathComponents. first, firstIndex > 0 else { return nil }
197-
198- // Filter to only declaration statements (not imports or other statements)
197+
198+ // Filter to only declaration statements (not imports or other statements)
199199 let declarationStatements = sourceFile. statements. compactMap { statement -> DeclSyntax ? in
200- // Skip import declarations and other non-declaration statements
201- if let declSyntax = statement. item. as ( DeclSyntax . self) {
202- // Check if it's an import declaration
203- if declSyntax. is ( ImportDeclSyntax . self) {
204- return nil // Skip imports
205- }
206- return declSyntax
200+ // Skip import declarations and other non-declaration statements
201+ if let declSyntax = statement. item. as ( DeclSyntax . self) {
202+ // Check if it's an import declaration
203+ if declSyntax. is ( ImportDeclSyntax . self) {
204+ return nil // Skip imports
207205 }
208- return nil
206+ return declSyntax
209207 }
210-
208+ return nil
209+ }
210+
211211 guard firstIndex <= declarationStatements. count else { return nil }
212-
212+
213213 return declarationStatements [ firstIndex - 1 ]
214214 }
215-
216- /// Returns true if the given DeclSyntax is a type declaration (class, struct, enum, protocol, actor, extension)
215+
216+ /// Returns true if the given DeclSyntax is a type declaration (class, struct, enum, protocol, actor, extension)
217217 internal func isTypeDeclaration( _ decl: DeclSyntax ) -> Bool {
218218 return decl. is ( ClassDeclSyntax . self) ||
219219 decl. is ( StructDeclSyntax . self) ||
@@ -222,21 +222,21 @@ public class CodeDistributor {
222222 decl. is ( ActorDeclSyntax . self) ||
223223 decl. is ( ExtensionDeclSyntax . self)
224224 }
225-
226- /// Generates the complete source file content for given imports and specific target declarations
225+
226+ /// Generates the complete source file content for given imports and specific target declarations
227227 internal func generateFileContent( imports: [ String ] , targetDeclarations: [ DeclarationOverview ] , sourceFile: SourceFileSyntax ) throws -> String {
228228 var content = " "
229- // Add imports
229+ // Add imports
230230 if !imports. isEmpty {
231231 for importName in imports {
232232 content += " import \( importName) \n "
233233 }
234234 content += " \n " // Only one newline after all imports
235235 }
236- // Add only the target declarations
236+ // Add only the target declarations
237237 for (index, targetDeclaration) in targetDeclarations. enumerated ( ) {
238238 if let declSyntax = findDeclarationSyntax ( for: targetDeclaration, in: sourceFile) {
239- // Remove leading newlines from the declaration
239+ // Remove leading newlines from the declaration
240240 var declString = declSyntax. description
241241 declString = declString. trimmingCharacters ( in: . newlines)
242242 content += declString
@@ -247,18 +247,18 @@ public class CodeDistributor {
247247 }
248248 return content
249249 }
250-
251- /// Generates source text for a declaration (without import headers)
250+
251+ /// Generates source text for a declaration (without import headers)
252252 internal func generateDeclarationSource( _ declaration: DeclarationOverview , in sourceFile: SourceFileSyntax ) -> String {
253253 guard let declSyntax = findDeclarationSyntax ( for: declaration, in: sourceFile) else {
254- return " "
255- }
256- // Always apply AccessControlRewriter to the whole declaration (including top-level types)
254+ return " "
255+ }
256+ // Always apply AccessControlRewriter to the whole declaration (including top-level types)
257257 let rewriter = AccessControlRewriter ( )
258258 let rewrittenDecl = rewriter. visit ( declSyntax)
259- // Preserve original formatting by not trimming newlines aggressively
259+ // Preserve original formatting by not trimming newlines aggressively
260260 let result = rewrittenDecl. description
261- // Only trim leading/trailing whitespace, preserve internal structure
261+ // Only trim leading/trailing whitespace, preserve internal structure
262262 return result. trimmingCharacters ( in: . whitespacesAndNewlines)
263263 }
264264}
0 commit comments