From 2ef4eb7931b852b763246d7fa7efce81cd8da99a Mon Sep 17 00:00:00 2001 From: can1357 Date: Wed, 27 May 2026 13:34:19 +0200 Subject: [PATCH] refactor(typescript-edit-benchmark): restructured line pairing to use diff-based matching - Replaced line-by-line comparison with diff-based pairing to correctly handle insertions and deletions that shift line indices. - Added logic to preserve trailing newline semantics from the actual content. - Added test case verifying that indent-only changes are normalized even when earlier insertions shift line positions. --- .../typescript-edit-benchmark/src/verify.ts | 51 +++++++++++++------ .../test/verify.test.ts | 44 ++++++++++++++++ 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/packages/typescript-edit-benchmark/src/verify.ts b/packages/typescript-edit-benchmark/src/verify.ts index b7ebb25ae..85ec5d321 100644 --- a/packages/typescript-edit-benchmark/src/verify.ts +++ b/packages/typescript-edit-benchmark/src/verify.ts @@ -244,27 +244,48 @@ function normalizeBlankLines(text: string): string { } function restoreWhitespaceOnlyLineDiffs(expected: string, actual: string): string { - const expectedLines = expected.split("\n"); - const actualLines = actual.split("\n"); - const max = Math.max(expectedLines.length, actualLines.length); - const out = new Array(max); + const changes = diffLines(expected, actual); + const out: string[] = []; + let pendingRemoved: string[] = []; + let pendingAdded: string[] = []; - for (let i = 0; i < max; i++) { - const expectedLine = expectedLines[i]; - const actualLine = actualLines[i]; - if (expectedLine === undefined || actualLine === undefined) { - out[i] = actualLine ?? ""; + const flush = () => { + const pairs = Math.min(pendingRemoved.length, pendingAdded.length); + for (let i = 0; i < pairs; i++) { + const removedLine = pendingRemoved[i]!; + const addedLine = pendingAdded[i]!; + out.push( + removedLine !== addedLine && equalsIgnoringWhitespace(removedLine, addedLine) ? removedLine : addedLine, + ); + } + // Unmatched added lines (insertions beyond the removal window) stay as-is. + for (let i = pairs; i < pendingAdded.length; i++) { + out.push(pendingAdded[i]!); + } + // Unmatched removed lines have no counterpart in actual — drop them. + pendingRemoved = []; + pendingAdded = []; + }; + + for (const change of changes) { + const lines = splitLines(change.value); + if (change.removed) { + pendingRemoved.push(...lines); continue; } - - if (expectedLine !== actualLine && equalsIgnoringWhitespace(expectedLine, actualLine)) { - out[i] = expectedLine; - } else { - out[i] = actualLine; + if (change.added) { + pendingAdded.push(...lines); + continue; } + flush(); + out.push(...lines); } + flush(); - return out.join("\n"); + // Preserve trailing newline semantics: rejoin with "\n" and add a trailing + // newline iff actual originally ended with one. + const joined = out.join("\n"); + return actual.endsWith("\n") ? `${joined}\n` : joined; } function equalsIgnoringWhitespace(a: string, b: string): boolean { diff --git a/packages/typescript-edit-benchmark/test/verify.test.ts b/packages/typescript-edit-benchmark/test/verify.test.ts index 5f821020d..4987b26e1 100644 --- a/packages/typescript-edit-benchmark/test/verify.test.ts +++ b/packages/typescript-edit-benchmark/test/verify.test.ts @@ -120,4 +120,48 @@ describe("verifyExpectedFiles", () => { await cleanup(); } }); + + it("normalizes indent-only diffs even when earlier insertions shift line indices", async () => { + const { expectedDir, actualDir, cleanup } = await createTempDirs(); + try { + // Force prettier to bail (intentional syntax error: unbalanced brace) so the + // verifier falls back to the whitespace-restore pass on raw content. + const expected = [ + "function broken(", + " // missing close paren on purpose", + " return {", + " a: 1,", + " b: 2,", + " };", + "}", + "", + ].join("\n"); + const actual = [ + "function broken(", + " // missing close paren on purpose", + " const inserted = true;", + " return {", + " a: 1,", + " b: 2,", + " };", + "}", + "", + ].join("\n"); + await Bun.write(path.join(expectedDir, "index.ts"), expected); + await Bun.write(path.join(actualDir, "index.ts"), actual); + + const result = await verifyExpectedFiles(expectedDir, actualDir); + + // The only real change should be the inserted const; the body's indent + // drift must not be reported as added/removed lines. + expect(result.success).toBe(false); + const diff = result.diff ?? ""; + const changeLines = diff.split("\n").filter(line => line.startsWith("+") || line.startsWith("-")); + expect(changeLines.some(line => line.includes("const inserted"))).toBe(true); + expect(changeLines.some(line => line.includes("a:"))).toBe(false); + expect(changeLines.some(line => line.includes("b:"))).toBe(false); + } finally { + await cleanup(); + } + }); });