perf(coding-agent): bound completed diff rendering
This commit is contained in:
@@ -255,6 +255,19 @@ describe("editToolRenderer", () => {
|
||||
it("caches completed diff rendering across stable frame renders", async () => {
|
||||
const uiTheme = await getUiTheme();
|
||||
let renderDiffCalls = 0;
|
||||
let statsColorCalls = 0;
|
||||
const countingTheme = new Proxy(uiTheme, {
|
||||
get(target, property) {
|
||||
if (property === "fg") {
|
||||
return (color: Parameters<themeModule.Theme["fg"]>[0], text: string): string => {
|
||||
if (color === "toolDiffAdded" && text === "+1") statsColorCalls++;
|
||||
return target.fg(color, text);
|
||||
};
|
||||
}
|
||||
const value = Reflect.get(target, property, target) as unknown;
|
||||
return typeof value === "function" ? value.bind(target) : value;
|
||||
},
|
||||
});
|
||||
const options = {
|
||||
expanded: false,
|
||||
isPartial: false,
|
||||
@@ -275,17 +288,19 @@ describe("editToolRenderer", () => {
|
||||
},
|
||||
},
|
||||
options,
|
||||
uiTheme,
|
||||
countingTheme,
|
||||
{ file_path: "src/example.ts" },
|
||||
);
|
||||
|
||||
component.render(160);
|
||||
component.render(120);
|
||||
expect(renderDiffCalls).toBe(1);
|
||||
expect(statsColorCalls).toBe(1);
|
||||
|
||||
options.expanded = true;
|
||||
component.render(120);
|
||||
expect(renderDiffCalls).toBe(2);
|
||||
expect(statsColorCalls).toBe(1);
|
||||
});
|
||||
|
||||
it("computes the hashline preview diff once a single-line edit finishes streaming", async () => {
|
||||
@@ -420,6 +435,27 @@ describe("editToolRenderer", () => {
|
||||
expect(lines.filter(line => line.includes("+2/-1"))).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("bounds a completed diff that contains one oversized change hunk", async () => {
|
||||
const uiTheme = await getUiTheme();
|
||||
const diff = Array.from({ length: 1_000 }, (_, i) => `+${i + 1}│line ${i}`).join("\n");
|
||||
const component = editToolRenderer.renderResult(
|
||||
{
|
||||
content: [{ type: "text", text: "Updated demo.ts" }],
|
||||
details: { diff, op: "update" },
|
||||
},
|
||||
{ expanded: false, isPartial: false, renderContext: { editMode: "hashline" } },
|
||||
uiTheme,
|
||||
{ file_path: "demo.ts" },
|
||||
);
|
||||
|
||||
const lines = component.render(160).map(line => Bun.stripANSI(line));
|
||||
const rendered = lines.join("\n");
|
||||
expect(lines.filter(line => line.includes("│line "))).toHaveLength(40);
|
||||
expect(rendered).toContain("+40│line 39");
|
||||
expect(rendered).not.toContain("+41│line 40");
|
||||
expect(rendered).toContain("960 more lines");
|
||||
});
|
||||
|
||||
it("renders completed edit gutters without inherited frame padding", async () => {
|
||||
const uiTheme = await getUiTheme();
|
||||
const component = editToolRenderer.renderResult(
|
||||
|
||||
@@ -286,6 +286,75 @@ describe("truncateDiffByHunk", () => {
|
||||
expect(idxOld).toBeLessThan(idxNew);
|
||||
expect(idxNew).toBeLessThan(idxTrailing);
|
||||
});
|
||||
it("caps one oversized change hunk at the line budget", () => {
|
||||
const diff = makeHunk("+", 0, 1_000).join("\n");
|
||||
const head = truncateDiffByHunk(diff, 4, 32);
|
||||
const tail = truncateDiffByHunk(diff, 4, 32, { fromTail: true });
|
||||
|
||||
expect(head.text.split("\n")).toHaveLength(32);
|
||||
expect(head.text).toStartWith("+ new 0\n");
|
||||
expect(head.text).toEndWith("+ new 31");
|
||||
expect(head.hiddenLines).toBe(968);
|
||||
expect(head.hiddenHunks).toBe(0);
|
||||
|
||||
expect(tail.text.split("\n")).toHaveLength(32);
|
||||
expect(tail.text).toStartWith("+ new 968\n");
|
||||
expect(tail.text).toEndWith("+ new 999");
|
||||
expect(tail.hiddenLines).toBe(968);
|
||||
expect(tail.hiddenHunks).toBe(0);
|
||||
});
|
||||
|
||||
it("keeps an exact-size change hunk unchanged", () => {
|
||||
const diff = makeHunk("-", 0, 32).join("\n");
|
||||
expect(truncateDiffByHunk(diff, 4, 32)).toEqual({
|
||||
text: diff,
|
||||
hiddenHunks: 0,
|
||||
hiddenLines: 0,
|
||||
});
|
||||
});
|
||||
it("drops surrounding context when changes exactly fill the line budget", () => {
|
||||
const diff = [" leading context", ...makeHunk("+", 0, 32), " trailing context"].join("\n");
|
||||
|
||||
for (const result of [truncateDiffByHunk(diff, 4, 32), truncateDiffByHunk(diff, 4, 32, { fromTail: true })]) {
|
||||
expect(result.text.split("\n")).toHaveLength(32);
|
||||
expect(result.text).not.toContain("context");
|
||||
expect(result.hiddenLines).toBe(2);
|
||||
expect(result.hiddenHunks).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not exceed the line budget when context rounding spans multiple hunks", () => {
|
||||
const diff = [
|
||||
" leading context",
|
||||
...makeHunk("+", 0, 15),
|
||||
" middle context a",
|
||||
" middle context b",
|
||||
...makeHunk("-", 100, 15),
|
||||
" trailing context",
|
||||
].join("\n");
|
||||
|
||||
for (const result of [truncateDiffByHunk(diff, 4, 32), truncateDiffByHunk(diff, 4, 32, { fromTail: true })]) {
|
||||
expect(result.text.split("\n")).toHaveLength(32);
|
||||
expect(result.hiddenLines).toBe(2);
|
||||
expect(result.hiddenHunks).toBe(0);
|
||||
}
|
||||
});
|
||||
it("does not count a removed separator as a hidden hunk", () => {
|
||||
const diff = [...makeHunk("+", 0, 16), " hunk separator", ...makeHunk("-", 100, 16)].join("\n");
|
||||
|
||||
for (const result of [truncateDiffByHunk(diff, 4, 32), truncateDiffByHunk(diff, 4, 32, { fromTail: true })]) {
|
||||
expect(result.text.split("\n")).toHaveLength(32);
|
||||
expect(result.hiddenLines).toBe(1);
|
||||
expect(result.hiddenHunks).toBe(0);
|
||||
}
|
||||
});
|
||||
it("reports every hunk excluded by the hunk limit", () => {
|
||||
const diff = buildDiff(6, 1);
|
||||
|
||||
for (const result of [truncateDiffByHunk(diff, 2, 100), truncateDiffByHunk(diff, 2, 100, { fromTail: true })]) {
|
||||
expect(result.hiddenHunks).toBe(4);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatErrorMessage (F4 sanitization)", () => {
|
||||
|
||||
Reference in New Issue
Block a user