55 "dissect/pkg/gopls"
66 "dissect/pkg/goutils"
77 "fmt"
8+ "go/ast"
89 "log/slog"
910 "os"
1011 "path/filepath"
@@ -112,7 +113,7 @@ func runMove(cmd *cobra.Command, args []string) {
112113 slog .Info ("Successfully moved all identifiers" )
113114}
114115
115- // moveIdentifier moves a single identifier from source to target file
116+ // moveIdentifier moves a single identifier from source to target file using AST operations
116117func moveIdentifier (sourceFile string , identifier string , targetFile string , moduleRoot string ) error {
117118 // Check if target file exists
118119 targetExists := false
@@ -125,11 +126,11 @@ func moveIdentifier(sourceFile string, identifier string, targetFile string, mod
125126 // If target doesn't exist, we need to create it with the right package
126127 if ! targetExists {
127128 // Get package name from source file
128- _ , node , err := goutils .ReadGoFile (sourceFile )
129+ _ , sourceNode , err := goutils .ReadGoFile (sourceFile )
129130 if err != nil {
130131 return fmt .Errorf ("error reading source file: %w" , err )
131132 }
132- packageName := node .Name .Name
133+ packageName := sourceNode .Name .Name
133134
134135 // Create target directory if needed
135136 targetDir := filepath .Dir (targetFile )
@@ -152,97 +153,58 @@ func moveIdentifier(sourceFile string, identifier string, targetFile string, mod
152153 }
153154 slog .Debug ("Extracted identifier to temp file" , "identifier" , identifier , "tempFile" , tempFile )
154155
155- // Now we need to move the content from tempFile to targetFile
156- // Read the temp file
157- tempContent , err := os .ReadFile (tempFile )
156+ // Remove the temp file when done
157+ defer os .Remove (tempFile )
158+
159+ // Parse the temp file to extract the function declaration and imports using AST
160+ _ , tempNode , err := goutils .ReadGoFile (tempFile )
158161 if err != nil {
159- return fmt .Errorf ("error reading temp file: %w" , err )
162+ return fmt .Errorf ("error parsing temp file: %w" , err )
160163 }
161164
162- // Read target file to verify it exists
163- _ , err = os . ReadFile (targetFile )
165+ // Parse the target file
166+ targetFset , targetNode , err := goutils . ReadGoFile (targetFile )
164167 if err != nil {
165- return fmt .Errorf ("error reading target file: %w" , err )
168+ return fmt .Errorf ("error parsing target file: %w" , err )
166169 }
167170
168- // For now, we'll use a simple approach: append the function to the target file
169- // This is a simplified version - a production version would:
170- // 1. Parse both files
171- // 2. Merge imports properly
172- // 3. Add the function declaration
173- // 4. Format the result
171+ // Find the function in the temp file
172+ var funcToMove * ast.FuncDecl
173+ for _ , decl := range tempNode .Decls {
174+ if fn , ok := decl .(* ast.FuncDecl ); ok {
175+ funcToMove = fn
176+ break
177+ }
178+ }
174179
175- // Parse temp file content to extract function and imports
176- tempStr := string (tempContent )
180+ if funcToMove == nil {
181+ return fmt .Errorf ("no function found in temp file" )
182+ }
177183
178- // Remove the temp file
179- defer os .Remove (tempFile )
184+ // Merge imports from temp file to target file
185+ // Build a map of existing imports in target
186+ existingImports := make (map [string ]bool )
187+ for _ , imp := range targetNode .Imports {
188+ existingImports [imp .Path .Value ] = true
189+ }
180190
181- // Extract only the function declaration from temp file (skip package and imports)
182- lines := strings .Split (tempStr , "\n " )
183- var functionLines []string
184- skipImports := false
185- inImportBlock := false
186-
187- for i , line := range lines {
188- trimmed := strings .TrimSpace (line )
189-
190- // Skip package declaration
191- if strings .HasPrefix (trimmed , "package " ) {
192- continue
193- }
194-
195- // Skip import statements
196- if strings .HasPrefix (trimmed , "import (" ) {
197- inImportBlock = true
198- skipImports = true
199- continue
200- }
201- if strings .HasPrefix (trimmed , "import " ) && ! inImportBlock {
202- skipImports = true
203- continue
204- }
205- if inImportBlock && trimmed == ")" {
206- inImportBlock = false
207- skipImports = false
208- continue
209- }
210- if skipImports && inImportBlock {
211- continue
212- }
213-
214- // Skip empty lines at the beginning
215- if len (functionLines ) == 0 && trimmed == "" {
216- continue
217- }
218-
219- // Start collecting from the function declaration
220- if strings .HasPrefix (trimmed , "func " ) || len (functionLines ) > 0 {
221- functionLines = append (functionLines , line )
222- }
223-
224- // Stop after collecting if we hit the next line after a function
225- if len (functionLines ) > 0 && i + 1 < len (lines ) &&
226- strings .TrimSpace (lines [i + 1 ]) == "" &&
227- strings .HasPrefix (trimmed , "}" ) {
228- break
191+ // Add new imports from temp file
192+ for _ , imp := range tempNode .Imports {
193+ if ! existingImports [imp .Path .Value ] {
194+ targetNode .Imports = append (targetNode .Imports , imp )
229195 }
230196 }
231197
232- // Append to target file
233- f , err := os .OpenFile (targetFile , os .O_APPEND | os .O_WRONLY , 0644 )
234- if err != nil {
235- return fmt .Errorf ("error opening target file for append: %w" , err )
236- }
237- defer f .Close ()
198+ // Add the function declaration to the target file
199+ targetNode .Decls = append (targetNode .Decls , funcToMove )
238200
239- // Add a blank line before the function
240- _ , err = f . WriteString ( " \n " + strings . Join ( functionLines , " \n " ) + " \n " )
201+ // Write the modified target file back using AST
202+ err = goutils . WriteGoFile ( targetFile , targetFset , targetNode )
241203 if err != nil {
242- return fmt .Errorf ("error appending to target file: %w" , err )
204+ return fmt .Errorf ("error writing target file: %w" , err )
243205 }
244206
245- // Run goimports to fix imports and formatting
207+ // Run goimports to organize imports and format properly
246208 err = commands .RunGoimportsOnFile (targetFile )
247209 if err != nil {
248210 return fmt .Errorf ("error running goimports: %w" , err )
0 commit comments