From 3a7c18bc52f87d23284a34a942cfd78460f2763c Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Tue, 11 Nov 2025 12:56:30 -0700 Subject: [PATCH] webview-ui: fix chat diff mis-render for EOF append by collapsing identical -/+ pairs into context; add unit tests --- .../utils/__tests__/parseUnifiedDiff.spec.ts | 31 ++++++++++++++++++ webview-ui/src/utils/parseUnifiedDiff.ts | 32 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 webview-ui/src/utils/__tests__/parseUnifiedDiff.spec.ts diff --git a/webview-ui/src/utils/__tests__/parseUnifiedDiff.spec.ts b/webview-ui/src/utils/__tests__/parseUnifiedDiff.spec.ts new file mode 100644 index 0000000000..a465891b11 --- /dev/null +++ b/webview-ui/src/utils/__tests__/parseUnifiedDiff.spec.ts @@ -0,0 +1,31 @@ +import { parseUnifiedDiff } from "@/utils/parseUnifiedDiff" + +describe("parseUnifiedDiff - collapse identical -/+ pairs", () => { + it("collapses deletion+addition of identical text into a single context line", () => { + // Typical trailing-newline-only change at EOF with an additional appended line + const diff = ["@@ -1,1 +1,2 @@", "-old", "+old", "+new", ""].join("\n") + + const lines = parseUnifiedDiff(diff) + + // Should normalize the replace of identical line into context, plus the appended line + expect(lines.map((l) => l.type)).toEqual(["context", "addition"]) + expect(lines[0].content).toBe("old") + expect(lines[1].content).toBe("new") + // Line numbers should be preserved appropriately + expect(lines[0].oldLineNum).toBe(1) + expect(lines[0].newLineNum).toBe(1) + expect(lines[1].oldLineNum).toBeNull() + expect(lines[1].newLineNum).toBe(2) + }) + + it("does not collapse when content differs (true replacement)", () => { + const diff = ["@@ -1,1 +1,1 @@", "-old", "+new", ""].join("\n") + + const lines = parseUnifiedDiff(diff) + + // Keep as deletion + addition for a real replacement + expect(lines.map((l) => l.type)).toEqual(["deletion", "addition"]) + expect(lines[0].content).toBe("old") + expect(lines[1].content).toBe("new") + }) +}) diff --git a/webview-ui/src/utils/parseUnifiedDiff.ts b/webview-ui/src/utils/parseUnifiedDiff.ts index bed84c4ca9..72bef6bb91 100644 --- a/webview-ui/src/utils/parseUnifiedDiff.ts +++ b/webview-ui/src/utils/parseUnifiedDiff.ts @@ -88,7 +88,37 @@ export function parseUnifiedDiff(source: string, filePath?: string): DiffLine[] prevHunk = hunk } - return lines + // Collapse "- line" then "+ same line" pairs into a single context line. + // This normalizes diffs where the only change is adding a trailing newline + // (common when appending to a file missing EOF newline). VS Code's diff + // shows these as unchanged; our chat view should too. + const collapseReplacePairs = (input: DiffLine[]): DiffLine[] => { + const out: DiffLine[] = [] + for (let i = 0; i < input.length; i++) { + const cur = input[i] + const next = input[i + 1] + if ( + cur && + next && + cur.type === "deletion" && + next.type === "addition" && + cur.content === next.content + ) { + out.push({ + oldLineNum: cur.oldLineNum, + newLineNum: next.newLineNum, + type: "context", + content: cur.content, + }) + i++ // skip the paired addition + continue + } + out.push(cur) + } + return out + } + + return collapseReplacePairs(lines) } catch { // swallow parse errors and render nothing rather than breaking the UI return []