Skip to content

Commit e50b4e0

Browse files
committed
fix str replace bug
1 parent 697bd6a commit e50b4e0

File tree

2 files changed

+47
-33
lines changed

2 files changed

+47
-33
lines changed

backend/src/__tests__/process-str-replace.test.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, it } from 'bun:test'
2+
import { applyPatch } from 'diff'
23

34
import { processStrReplace } from '../process-str-replace'
45

@@ -106,21 +107,6 @@ describe('processStrReplace', () => {
106107
}
107108
})
108109

109-
it('should return error when oldStr is empty and file does not exist', async () => {
110-
const newContent = 'const x = 1;\nconst y = 2;\n'
111-
const result = await processStrReplace(
112-
'test.ts',
113-
[{ old: '', new: newContent, allowMultiple: false }],
114-
Promise.resolve(null),
115-
)
116-
117-
expect(result).not.toBeNull()
118-
expect('error' in result).toBe(true)
119-
if ('error' in result) {
120-
expect(result.error).toContain('old string was empty')
121-
}
122-
})
123-
124110
it('should return error if no changes were made', async () => {
125111
const initialContent = 'const x = 1;\nconst y = 2;\n'
126112
const oldStr = 'const z = 3;' // This string doesn't exist in the content
@@ -403,4 +389,31 @@ function test3() {
403389
}
404390
})
405391
})
392+
393+
it('should handle applying multiple replacements on nearby lines', async () => {
394+
const initialContent = 'line 1\nline 2\nline 3\n'
395+
const replacements = [
396+
{
397+
old: 'line 2\n',
398+
new: 'this is a new line\n',
399+
allowMultiple: false,
400+
},
401+
{
402+
old: 'line 3\n',
403+
new: 'new line 3\n',
404+
allowMultiple: false,
405+
},
406+
]
407+
408+
const result = await processStrReplace(
409+
'test.ts',
410+
replacements,
411+
Promise.resolve(initialContent),
412+
)
413+
414+
expect('content' in result).toBe(true)
415+
expect(applyPatch(initialContent, (result as any).patch)).toBe(
416+
'line 1\nthis is a new line\nnew line 3\n',
417+
)
418+
})
406419
})

backend/src/process-str-replace.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,20 @@ export async function processStrReplace(
1818
| { tool: 'str_replace'; path: string; error: string }
1919
> {
2020
const initialContent = await initialContentPromise
21+
if (initialContent === null) {
22+
return {
23+
tool: 'str_replace',
24+
path,
25+
error:
26+
'The file does not exist, skipping. Please use the write_file tool to create the file.',
27+
}
28+
}
2129

2230
// Process each old/new string pair
2331
let currentContent = initialContent
2432
let allPatches: string[] = []
2533
let messages: string[] = []
34+
const lineEnding = currentContent.includes('\r\n') ? '\r\n' : '\n'
2635

2736
for (const { old: oldStr, new: newStr, allowMultiple } of replacements) {
2837
// Regular case: require oldStr for replacements
@@ -32,14 +41,7 @@ export async function processStrReplace(
3241
)
3342
continue
3443
}
35-
if (currentContent === null) {
36-
messages.push(
37-
'The file does not exist, skipping. Please use the write_file tool to create the file.',
38-
)
39-
continue
40-
}
4144

42-
const lineEnding = currentContent.includes('\r\n') ? '\r\n' : '\n'
4345
const normalizeLineEndings = (str: string) => str.replace(/\r\n/g, '\n')
4446
const normalizedCurrentContent = normalizeLineEndings(currentContent)
4547
const normalizedOldStr = normalizeLineEndings(oldStr)
@@ -64,20 +66,11 @@ export async function processStrReplace(
6466
? normalizedCurrentContent
6567
: normalizedCurrentContent.replaceAll(updatedOldStr, newStr)
6668

67-
let patch = createPatch(path, normalizedCurrentContent, updatedContent)
68-
const lines = patch.split('\n')
69-
const hunkStartIndex = lines.findIndex((line) => line.startsWith('@@'))
70-
if (hunkStartIndex !== -1) {
71-
patch = lines.slice(hunkStartIndex).join('\n')
72-
patch = patch.replaceAll('\n', lineEnding)
73-
allPatches.push(patch)
74-
}
75-
7669
// Update current content for next iteration
7770
currentContent = updatedContent.replaceAll('\n', lineEnding)
7871
}
7972

80-
if (allPatches.length === 0) {
73+
if (initialContent === currentContent) {
8174
logger.debug(
8275
{
8376
path,
@@ -93,7 +86,15 @@ export async function processStrReplace(
9386
}
9487
}
9588

96-
const finalPatch = allPatches.join('\n')
89+
let patch = createPatch(path, initialContent, currentContent)
90+
const lines = patch.split('\n')
91+
const hunkStartIndex = lines.findIndex((line) => line.startsWith('@@'))
92+
if (hunkStartIndex !== -1) {
93+
patch = lines.slice(hunkStartIndex).join('\n')
94+
patch = patch.replaceAll('\n', lineEnding)
95+
allPatches.push(patch)
96+
}
97+
const finalPatch = patch
9798

9899
logger.debug(
99100
{

0 commit comments

Comments
 (0)