-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunified.go
More file actions
132 lines (117 loc) · 3.58 KB
/
unified.go
File metadata and controls
132 lines (117 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package tokendiff
import (
"fmt"
"strings"
)
// DiffHunk represents a single hunk from a unified diff.
type DiffHunk struct {
// OldStart is the starting line number in the old file.
OldStart int
// OldCount is the number of lines from the old file.
OldCount int
// NewStart is the starting line number in the new file.
NewStart int
// NewCount is the number of lines in the new file.
NewCount int
// OldLines contains the removed lines (without the leading "-").
OldLines []string
// NewLines contains the added lines (without the leading "+").
NewLines []string
// ContextBefore contains context lines before the change.
ContextBefore []string
// ContextAfter contains context lines after the change.
ContextAfter []string
}
// UnifiedDiff represents a parsed unified diff.
type UnifiedDiff struct {
// OldFile is the name of the old file (from "---" line).
OldFile string
// NewFile is the name of the new file (from "+++" line).
NewFile string
// Hunks contains all the diff hunks.
Hunks []DiffHunk
}
// ParseUnifiedDiff parses a unified diff string into structured data.
// It handles standard unified diff format as produced by diff -u or git diff.
func ParseUnifiedDiff(input string) ([]UnifiedDiff, error) {
var results []UnifiedDiff
var current *UnifiedDiff
var currentHunk *DiffHunk
inHunk := false
lines := strings.Split(input, "\n")
flushHunk := func() {
if currentHunk != nil && current != nil {
current.Hunks = append(current.Hunks, *currentHunk)
currentHunk = nil
}
inHunk = false
}
for _, line := range lines {
// New file diff
if strings.HasPrefix(line, "--- ") {
flushHunk()
if current != nil {
results = append(results, *current)
}
current = &UnifiedDiff{
OldFile: strings.TrimPrefix(line, "--- "),
}
continue
}
if strings.HasPrefix(line, "+++ ") && current != nil {
current.NewFile = strings.TrimPrefix(line, "+++ ")
continue
}
// Hunk header
if strings.HasPrefix(line, "@@") && current != nil {
flushHunk()
currentHunk = &DiffHunk{}
inHunk = true
// Parse @@ -start,count +start,count @@
var oldStart, oldCount, newStart, newCount int
// Try parsing with counts
n, _ := fmt.Sscanf(line, "@@ -%d,%d +%d,%d @@",
&oldStart, &oldCount, &newStart, &newCount)
if n < 4 {
// Try without counts (single line changes)
fmt.Sscanf(line, "@@ -%d +%d @@", &oldStart, &newStart)
oldCount = 1
newCount = 1
}
currentHunk.OldStart = oldStart
currentHunk.OldCount = oldCount
currentHunk.NewStart = newStart
currentHunk.NewCount = newCount
continue
}
if !inHunk || currentHunk == nil {
continue
}
// Process hunk content
if strings.HasPrefix(line, "-") {
currentHunk.OldLines = append(currentHunk.OldLines, line[1:])
} else if strings.HasPrefix(line, "+") {
currentHunk.NewLines = append(currentHunk.NewLines, line[1:])
} else if strings.HasPrefix(line, " ") {
// Context line
if len(currentHunk.OldLines) == 0 && len(currentHunk.NewLines) == 0 {
currentHunk.ContextBefore = append(currentHunk.ContextBefore, line[1:])
} else {
currentHunk.ContextAfter = append(currentHunk.ContextAfter, line[1:])
}
}
}
// Flush remaining
flushHunk()
if current != nil {
results = append(results, *current)
}
return results, nil
}
// ApplyWordDiff applies word-level diffing to a unified diff hunk.
// It returns the word-level diff result for the changed lines.
func ApplyWordDiff(hunk DiffHunk, opts Options) []Diff {
oldText := strings.Join(hunk.OldLines, "\n")
newText := strings.Join(hunk.NewLines, "\n")
return DiffStrings(oldText, newText, opts)
}