Spaces:
Paused
Paused
| package diff | |
| import ( | |
| "bufio" | |
| "fmt" | |
| "log" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "strconv" | |
| "strings" | |
| shared "plandex-shared" | |
| "github.com/google/uuid" | |
| ) | |
| func GetDiffs(original, updated string) (string, error) { | |
| // create temp directory | |
| tempDirPath, err := os.MkdirTemp("", "tmp-diffs-*") | |
| if err != nil { | |
| return "", fmt.Errorf("error creating temp dir: %v", err) | |
| } | |
| defer func() { | |
| go os.RemoveAll(tempDirPath) | |
| }() | |
| // write the original file to the temp dir | |
| err = os.WriteFile(filepath.Join(tempDirPath, "original"), []byte(original), 0644) | |
| if err != nil { | |
| return "", fmt.Errorf("error writing original file: %v", err) | |
| } | |
| // write the updated file to the temp dir | |
| err = os.WriteFile(filepath.Join(tempDirPath, "updated"), []byte(updated), 0644) | |
| if err != nil { | |
| return "", fmt.Errorf("error writing updated file: %v", err) | |
| } | |
| cmd := exec.Command("git", "-C", tempDirPath, "diff", "--no-color", "--no-index", "original", "updated") | |
| res, err := cmd.CombinedOutput() | |
| if err != nil { | |
| exitError, ok := err.(*exec.ExitError) | |
| if ok && exitError.ExitCode() == 1 { | |
| // Exit status 1 means diffs were found, which is expected | |
| } else { | |
| log.Printf("Error getting diffs: %v\n", err) | |
| log.Printf("Diff output: %s\n", res) | |
| return "", fmt.Errorf("error getting diffs: %v", err) | |
| } | |
| } | |
| return string(res), nil | |
| } | |
| type change struct { | |
| Old string | |
| New string | |
| Line int | |
| Length int | |
| } | |
| func GetDiffReplacements(original, updated string) ([]*shared.Replacement, error) { | |
| diff, err := GetDiffs(original, updated) | |
| if err != nil { | |
| return nil, fmt.Errorf("error getting git diffs: %v", err) | |
| } | |
| var changes []*change | |
| scanner := bufio.NewScanner(strings.NewReader(diff)) | |
| var currentHunk *change | |
| var oldLines, newLines []string | |
| for scanner.Scan() { | |
| line := scanner.Text() | |
| // Parse hunk header | |
| if strings.HasPrefix(line, "@@") { | |
| // If we have a previous hunk, process it | |
| if currentHunk != nil { | |
| change := processHunk(oldLines, newLines, currentHunk.Line) | |
| if change != nil { | |
| changes = append(changes, change) | |
| } | |
| } | |
| // Parse the new hunk header | |
| lineInfo := strings.Split(line, " ")[1:] // Skip @@ part | |
| oldInfo := strings.Split(lineInfo[0], ",") | |
| startLine, _ := strconv.Atoi(strings.TrimPrefix(oldInfo[0], "-")) | |
| currentHunk = &change{ | |
| Line: startLine, | |
| } | |
| oldLines = []string{} | |
| newLines = []string{} | |
| continue | |
| } | |
| if currentHunk == nil { | |
| continue // Skip until we find a hunk | |
| } | |
| // Process the lines within a hunk | |
| switch { | |
| case strings.HasPrefix(line, "-"): | |
| oldLines = append(oldLines, strings.TrimPrefix(line, "-")) | |
| case strings.HasPrefix(line, "+"): | |
| newLines = append(newLines, strings.TrimPrefix(line, "+")) | |
| case strings.HasPrefix(line, " "): | |
| // Context lines - add to both | |
| line = strings.TrimPrefix(line, " ") | |
| oldLines = append(oldLines, line) | |
| newLines = append(newLines, line) | |
| } | |
| } | |
| if err := scanner.Err(); err != nil { | |
| return nil, fmt.Errorf("error scanning diff: %v", err) | |
| } | |
| // Process the last hunk if exists | |
| if currentHunk != nil { | |
| change := processHunk(oldLines, newLines, currentHunk.Line) | |
| if change != nil { | |
| changes = append(changes, change) | |
| } | |
| } | |
| replacements := make([]*shared.Replacement, len(changes)) | |
| for i, change := range changes { | |
| replacements[i] = &shared.Replacement{ | |
| Id: uuid.New().String(), | |
| Old: change.Old, | |
| New: change.New, | |
| } | |
| } | |
| return replacements, nil | |
| } | |
| func processHunk(oldLines, newLines []string, startLine int) *change { | |
| if len(oldLines) == 0 && len(newLines) == 0 { | |
| return nil | |
| } | |
| return &change{ | |
| Old: strings.Join(oldLines, "\n"), | |
| New: strings.Join(newLines, "\n"), | |
| Line: startLine, | |
| Length: len(oldLines), | |
| } | |
| } | |