improve multiline text editing reliability by tracking positions and applying edits in reverse order
This commit is contained in:
parent
dc9f56720f
commit
1ec75e87fc
1 changed files with 40 additions and 15 deletions
|
|
@ -216,6 +216,29 @@ async function searchFiles(
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
lineNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTextPosition(content: string, searchText: string): Position {
|
||||||
|
// Handle different line endings
|
||||||
|
const normalized = content.replace(/\r\n/g, '\n');
|
||||||
|
const searchNormalized = searchText.replace(/\r\n/g, '\n');
|
||||||
|
|
||||||
|
const pos = normalized.indexOf(searchNormalized);
|
||||||
|
if (pos === -1) {
|
||||||
|
throw new Error(`Text not found:\n${searchText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: pos,
|
||||||
|
end: pos + searchText.length,
|
||||||
|
lineNumber: normalized.slice(0, pos).split('\n').length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Edit preview type
|
// Edit preview type
|
||||||
interface EditPreview {
|
interface EditPreview {
|
||||||
original: string;
|
original: string;
|
||||||
|
|
@ -225,37 +248,39 @@ interface EditPreview {
|
||||||
}
|
}
|
||||||
|
|
||||||
// File editing utilities
|
// File editing utilities
|
||||||
async function applyFileEdits(filePath: string, edits: Array<{oldText: string, newText: string}>, dryRun: boolean = false): Promise<string | EditPreview[]> {
|
async function applyFileEdits(filePath: string, edits: Array<{oldText: string, newText: string}>, dryRun = false): Promise<string | EditPreview[]> {
|
||||||
let content = await fs.readFile(filePath, 'utf-8');
|
let content = await fs.readFile(filePath, 'utf-8');
|
||||||
const previews: EditPreview[] = [];
|
const previews: EditPreview[] = [];
|
||||||
|
|
||||||
for (const edit of edits) {
|
// Find all positions first
|
||||||
const pos = content.indexOf(edit.oldText);
|
const positions = edits.map(edit => ({
|
||||||
if (pos === -1) {
|
edit,
|
||||||
throw new Error(
|
position: findTextPosition(content, edit.oldText)
|
||||||
`Search text not found in ${filePath}:\n${edit.oldText}`
|
}));
|
||||||
);
|
|
||||||
}
|
// Sort by position in reverse order
|
||||||
|
positions.sort((a, b) => b.position.start - a.position.start);
|
||||||
const lineNumber = content.slice(0, pos).split(/\r?\n/).length;
|
|
||||||
|
// Apply edits from end to start
|
||||||
|
for (const {edit, position} of positions) {
|
||||||
const preview = [
|
const preview = [
|
||||||
`@@ line ${lineNumber} @@`,
|
`@@ line ${position.lineNumber} @@`,
|
||||||
'<<<<<<< ORIGINAL',
|
'<<<<<<< ORIGINAL',
|
||||||
edit.oldText,
|
edit.oldText,
|
||||||
'=======',
|
'=======',
|
||||||
edit.newText,
|
edit.newText,
|
||||||
'>>>>>>> MODIFIED'
|
'>>>>>>> MODIFIED'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
previews.push({
|
previews.push({
|
||||||
original: edit.oldText,
|
original: edit.oldText,
|
||||||
modified: edit.newText,
|
modified: edit.newText,
|
||||||
lineNumber,
|
lineNumber: position.lineNumber,
|
||||||
preview
|
preview
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
content = content.slice(0, pos) + edit.newText + content.slice(pos + edit.oldText.length);
|
content = content.slice(0, position.start) + edit.newText + content.slice(position.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue