diff --git a/packages/coding-agent/src/edit/diff.ts b/packages/coding-agent/src/edit/diff.ts index e9703d229..2defef075 100644 --- a/packages/coding-agent/src/edit/diff.ts +++ b/packages/coding-agent/src/edit/diff.ts @@ -96,40 +96,57 @@ export function generateDiffString(oldContent: string, newContent: string, conte const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed); if (lastWasChange || nextPartIsChange) { - let linesToShow = raw; - let skipStart = 0; - let skipEnd = 0; + const contextLimit = Math.max(0, contextLines); + let leadingSkip = 0; + let middleSkip = 0; + let trailingSkip = 0; + let linesToShow: string[]; - if (!lastWasChange) { - // Show only last N lines as leading context - skipStart = Math.max(0, raw.length - contextLines); - linesToShow = raw.slice(skipStart); + if (lastWasChange && nextPartIsChange) { + if (raw.length > contextLimit * 2) { + const leadingContext = raw.slice(0, contextLimit); + const trailingContext = raw.slice(raw.length - contextLimit); + middleSkip = raw.length - leadingContext.length - trailingContext.length; + linesToShow = [...leadingContext, ...trailingContext]; + } else { + linesToShow = raw; + } + } else if (nextPartIsChange) { + leadingSkip = Math.max(0, raw.length - contextLimit); + linesToShow = raw.slice(leadingSkip); + } else { + trailingSkip = Math.max(0, raw.length - contextLimit); + linesToShow = raw.slice(0, contextLimit); } - if (!nextPartIsChange && linesToShow.length > contextLines) { - // Show only first N lines as trailing context - skipEnd = linesToShow.length - contextLines; - linesToShow = linesToShow.slice(0, contextLines); - } - - // Add ellipsis if we skipped lines at start - if (skipStart > 0) { + if (leadingSkip > 0) { output.push(formatNumberedDiffLine(" ", oldLineNum, "...")); - oldLineNum += skipStart; - newLineNum += skipStart; + oldLineNum += leadingSkip; + newLineNum += leadingSkip; } - for (const line of linesToShow) { + const firstChunkLength = middleSkip > 0 ? contextLimit : linesToShow.length; + for (const line of linesToShow.slice(0, firstChunkLength)) { output.push(formatNumberedDiffLine(" ", oldLineNum, line)); oldLineNum++; newLineNum++; } - // Add ellipsis if we skipped lines at end - if (skipEnd > 0) { + if (middleSkip > 0) { output.push(formatNumberedDiffLine(" ", oldLineNum, "...")); - oldLineNum += skipEnd; - newLineNum += skipEnd; + oldLineNum += middleSkip; + newLineNum += middleSkip; + for (const line of linesToShow.slice(firstChunkLength)) { + output.push(formatNumberedDiffLine(" ", oldLineNum, line)); + oldLineNum++; + newLineNum++; + } + } + + if (trailingSkip > 0) { + output.push(formatNumberedDiffLine(" ", oldLineNum, "...")); + oldLineNum += trailingSkip; + newLineNum += trailingSkip; } } else { // Skip these context lines entirely diff --git a/packages/coding-agent/test/tools/edit-diff.test.ts b/packages/coding-agent/test/tools/edit-diff.test.ts new file mode 100644 index 000000000..8445e37c1 --- /dev/null +++ b/packages/coding-agent/test/tools/edit-diff.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "bun:test"; +import { generateDiffString } from "../../src/edit/diff"; + +describe("generateDiffString", () => { + it("collapses unchanged lines between distant edits", () => { + const oldLines = Array.from({ length: 20 }, (_, index) => `line ${index + 1}`); + const newLines = [...oldLines]; + newLines[1] = "line 2 changed"; + newLines[17] = "line 18 changed"; + + const result = generateDiffString(oldLines.join("\n"), newLines.join("\n"), 2); + const diffLines = result.diff.split("\n"); + + expect(diffLines).toContain(" 5|..."); + expect(diffLines).toContain("-2|line 2"); + expect(diffLines).toContain("+2|line 2 changed"); + expect(diffLines).toContain("-18|line 18"); + expect(diffLines).toContain("+18|line 18 changed"); + expect(diffLines).not.toContain(" 8|line 8"); + expect(diffLines).not.toContain(" 12|line 12"); + }); +});