Use ranged reads and limited searches and fuzzy editing improvements (#19240)

This commit is contained in:
Christian Gunderman
2026-02-17 23:54:08 +00:00
committed by GitHub
parent 55c628e967
commit ce84b3cb5f
9 changed files with 1174 additions and 145 deletions
+176
View File
@@ -373,6 +373,182 @@ describe('EditTool', () => {
expect(result.occurrences).toBe(1);
});
it('should perform a fuzzy replacement when exact match fails but similarity is high', async () => {
const content =
'const myConfig = {\n enableFeature: true,\n retries: 3\n};';
// Typo: missing comma after true
const oldString =
'const myConfig = {\n enableFeature: true\n retries: 3\n};';
const newString =
'const myConfig = {\n enableFeature: false,\n retries: 5\n};';
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'config.ts',
instruction: 'update config',
old_string: oldString,
new_string: newString,
},
currentContent: content,
abortSignal,
});
expect(result.occurrences).toBe(1);
expect(result.newContent).toBe(newString);
});
it('should NOT perform a fuzzy replacement when similarity is below threshold', async () => {
const content =
'const myConfig = {\n enableFeature: true,\n retries: 3\n};';
// Completely different string
const oldString = 'function somethingElse() {\n return false;\n}';
const newString =
'const myConfig = {\n enableFeature: false,\n retries: 5\n};';
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'config.ts',
instruction: 'update config',
old_string: oldString,
new_string: newString,
},
currentContent: content,
abortSignal,
});
expect(result.occurrences).toBe(0);
expect(result.newContent).toBe(content);
});
it('should NOT perform a fuzzy replacement when the complexity (length * size) is too high', async () => {
// 2000 chars
const longString = 'a'.repeat(2000);
// Create a file with enough lines to trigger the complexity limit
// Complexity = Lines * Length^2
// Threshold = 500,000,000
// 2000^2 = 4,000,000.
// Need > 125 lines. Let's use 200 lines.
const lines = Array(200).fill(longString);
const content = lines.join('\n');
// Mismatch at the end (making it a fuzzy match candidate)
const oldString = longString + 'c';
const newString = 'replacement';
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'test.ts',
instruction: 'update',
old_string: oldString,
new_string: newString,
},
currentContent: content,
abortSignal,
});
// Should return 0 occurrences because fuzzy match is skipped
expect(result.occurrences).toBe(0);
expect(result.newContent).toBe(content);
});
it('should perform multiple fuzzy replacements if multiple valid matches are found', async () => {
const content = `
function doIt() {
console.log("hello");
}
function doIt() {
console.log("hello");
}
`;
// old_string uses single quotes, file uses double.
// This is a fuzzy match (quote difference).
const oldString = `
function doIt() {
console.log('hello');
}
`.trim();
const newString = `
function doIt() {
console.log("bye");
}
`.trim();
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'test.ts',
instruction: 'update',
old_string: oldString,
new_string: newString,
},
currentContent: content,
abortSignal,
});
expect(result.occurrences).toBe(2);
const expectedContent = `
function doIt() {
console.log("bye");
}
function doIt() {
console.log("bye");
}
`;
expect(result.newContent).toBe(expectedContent);
});
it('should correctly rebase indentation in flexible replacement without double-indenting', async () => {
const content = ' if (a) {\n foo();\n }\n';
// old_string and new_string are unindented. They should be rebased to 4-space.
const oldString = 'if (a) {\n foo();\n}';
const newString = 'if (a) {\n bar();\n}';
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'test.ts',
old_string: oldString,
new_string: newString,
},
currentContent: content,
abortSignal,
});
expect(result.occurrences).toBe(1);
// foo() was at 8 spaces (4 base + 4 indent).
// newString has bar() at 4 spaces (0 base + 4 indent).
// Rebased to 4 base, it should be 4 + 4 = 8 spaces.
const expectedContent = ' if (a) {\n bar();\n }\n';
expect(result.newContent).toBe(expectedContent);
});
it('should correctly rebase indentation in fuzzy replacement without double-indenting', async () => {
const content =
' const myConfig = {\n enableFeature: true,\n retries: 3\n };';
// Typo: missing comma. old_string/new_string are unindented.
const fuzzyOld =
'const myConfig = {\n enableFeature: true\n retries: 3\n};';
const fuzzyNew =
'const myConfig = {\n enableFeature: false,\n retries: 5\n};';
const result = await calculateReplacement(mockConfig, {
params: {
file_path: 'test.ts',
old_string: fuzzyOld,
new_string: fuzzyNew,
},
currentContent: content,
abortSignal,
});
expect(result.strategy).toBe('fuzzy');
const expectedContent =
' const myConfig = {\n enableFeature: false,\n retries: 5\n };';
expect(result.newContent).toBe(expectedContent);
});
it('should NOT insert extra newlines when replacing a block preceded by a blank line (regression)', async () => {
const content = '\n function oldFunc() {\n // some code\n }';
const result = await calculateReplacement(mockConfig, {