fix(suggestdiff): preserve case and indentation changes (#1256)

* fix(suggestdiff): preserve case and indentation changes

* test(suggestdiff): cover fuzzy changes with context
This commit is contained in:
Metehan Kızılcık 2026-09-15 11:29:31 +03:00 • committed by GitHub
parent 01ae248f1b
commit b3dbcb634c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 71 additions and 9 deletions

View file

@ -51,7 +51,13 @@ func ComputeLineDiff(oldLines, newLines []string) []DiffLine {
back := make([]DiffLine, 0, max(m, n)*2)
for i > 0 || j > 0 {
if i > 0 && j > 0 && strings.EqualFold(strings.TrimSpace(oldLines[i-1]), strings.TrimSpace(newLines[j-1])) {
back = append(back, DiffLine{Type: DiffContext, Content: oldLines[i-1]})
if oldLines[i-1] == newLines[j-1] {
back = append(back, DiffLine{Type: DiffContext, Content: oldLines[i-1]})
} else {
// Fuzzy equality is only for alignment; preserve raw changes in the rendered diff.
back = append(back, DiffLine{Type: DiffAdded, Content: newLines[j-1]})
back = append(back, DiffLine{Type: DiffDeleted, Content: oldLines[i-1]})
}
i--
j--
} else if j > 0 && (i == 0 || lcs[i][j-1] >= lcs[i-1][j]) {

View file

@ -72,14 +72,6 @@ func TestComputeLineDiff(t *testing.T) {
wantAdds: 0,
wantDels: 1,
},
{
name: "case insensitive match with whitespace",
old: []string{" Hello "},
new: []string{"hello"},
wantLen: 1,
wantAdds: 0,
wantDels: 0,
},
{
name: "multi-line edit",
old: []string{"func main() {", " fmt.Println(\"old\")", "}"},
@ -115,6 +107,70 @@ func TestComputeLineDiff(t *testing.T) {
}
}
func TestComputeLineDiff_FuzzyMatchPreservesRawChanges(t *testing.T) {
tests := []struct {
name string
old string
new string
}{
{
name: "case-only change",
old: "const maxRetries = 3",
new: "const MaxRetries = 3",
},
{
name: "indentation-only change",
old: "\treturn nil",
new: "\t\treturn nil",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ComputeLineDiff([]string{tt.old}, []string{tt.new})
if len(got) != 2 {
t.Fatalf("diff = %v, want delete + add", got)
}
if got[0].Type != DiffDeleted || got[0].Content != tt.old {
t.Errorf("deleted line = %+v, want %q", got[0], tt.old)
}
if got[1].Type != DiffAdded || got[1].Content != tt.new {
t.Errorf("added line = %+v, want %q", got[1], tt.new)
}
})
}
}
func TestComputeLineDiff_FuzzyMatchPreservesRawChangesWithContext(t *testing.T) {
old := []string{
"func validate() error {",
"\treturn nil",
"}",
}
new := []string{
"func validate() error {",
"\t\treturn nil",
"}",
}
got := ComputeLineDiff(old, new)
want := []DiffLine{
{Type: DiffContext, Content: "func validate() error {"},
{Type: DiffDeleted, Content: "\treturn nil"},
{Type: DiffAdded, Content: "\t\treturn nil"},
{Type: DiffContext, Content: "}"},
}
if len(got) != len(want) {
t.Fatalf("diff = %v, want %v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("diff[%d] = %+v, want %+v", i, got[i], want[i])
}
}
}
func TestComputeLineDiff_ContextContent(t *testing.T) {
old := []string{"a", "b", "c"}
new := []string{"a", "x", "c"}