Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/filesystem/__tests__/lib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,20 @@ describe('Lib Functions', () => {
.rejects.toThrow('Could not find exact match for edit');
});

it('includes line context when edit text is not found', async () => {
const edits = [
{ oldText: 'line2 with typo', newText: 'replacement' }
];

await expect(applyFileEdits('/test/file.txt', edits, false))
.rejects.toThrow(
'Could not find exact match for edit:\nline2 with typo\n\n' +
'First 3 lines of file (3 total lines):\n' +
'1: line1\n2: line2\n3: line3\n\n' +
'Hint: re-read the file and copy the exact current text, including whitespace.'
);
});

it('handles complex multi-line edits with indentation', async () => {
mockFs.readFile.mockResolvedValue('function test() {\n console.log("hello");\n return true;\n}');

Expand Down
28 changes: 27 additions & 1 deletion src/filesystem/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,32 @@ interface FileEdit {
newText: string;
}

function buildEditNotFoundError(edit: FileEdit, content: string): Error {
const contentLines = content.endsWith('\n')
? content.slice(0, -1).split('\n')
: content.split('\n');
const oldLines = normalizeLineEndings(edit.oldText).split('\n');
const anchorLine = oldLines.find(line => line.trim().length > 0)?.trim();
const anchorIndex = anchorLine
? contentLines.findIndex(line => line.trim().includes(anchorLine))
: -1;
const contextStart = Math.max(0, (anchorIndex >= 0 ? anchorIndex : 0) - 2);
const contextEnd = Math.min(contentLines.length, contextStart + 5);
const contextLines = contentLines
.slice(contextStart, contextEnd)
.map((line, index) => `${contextStart + index + 1}: ${line}`)
.join('\n');
const contextHeading = anchorIndex >= 0
? `Closest context around line ${anchorIndex + 1}`
: `First ${contextEnd - contextStart} lines of file`;

return new Error(
`Could not find exact match for edit:\n${edit.oldText}\n\n` +
`${contextHeading} (${contentLines.length} total lines):\n${contextLines}\n\n` +
'Hint: re-read the file and copy the exact current text, including whitespace.'
);
}

export async function applyFileEdits(
filePath: string,
edits: FileEdit[],
Expand Down Expand Up @@ -248,7 +274,7 @@ export async function applyFileEdits(
}

if (!matchFound) {
throw new Error(`Could not find exact match for edit:\n${edit.oldText}`);
throw buildEditNotFoundError(edit, modifiedContent);
}
}

Expand Down
Loading