Skip to content

Commit 9ebbe6b

Browse files
Copilotneongreen
andcommitted
Rewrite move command to use proper AST operations instead of string manipulation
Co-authored-by: neongreen <1523306+neongreen@users.noreply.github.com>
1 parent e9038ef commit 9ebbe6b

1 file changed

Lines changed: 40 additions & 78 deletions

File tree

dissect/cmd/move.go

Lines changed: 40 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
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
116117
func 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

Comments
 (0)