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.
This commit is contained in:
@@ -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<string>(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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user