Skip to content

Commit 1eb29be

Browse files
authored
Remove the end_line from the multi_diff instructions and logic (#2615)
1 parent 30c3a65 commit 1eb29be

File tree

4 files changed

+25
-93
lines changed

4 files changed

+25
-93
lines changed

src/core/diff/strategies/__tests__/multi-search-replace.test.ts

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,13 @@ describe("MultiSearchReplaceDiffStrategy", () => {
3232
const diff =
3333
"<<<<<<< SEARCH\n" +
3434
":start_line:10\n" +
35-
":end_line:11\n" +
3635
"-------\n" +
3736
"content1\n" +
3837
"=======\n" +
3938
"new1\n" +
4039
">>>>>>> REPLACE\n\n" +
4140
"<<<<<<< SEARCH\n" +
4241
":start_line:10\n" +
43-
":end_line:11\n" +
4442
"-------\n" +
4543
"content2\n" +
4644
"=======\n" +
@@ -141,15 +139,13 @@ function helloWorld() {
141139
const diffContent = `test.ts
142140
<<<<<<< SEARCH
143141
:start_line:1
144-
:end_line:1
145142
-------
146143
function hello() {
147144
=======
148145
function helloWorld() {
149146
>>>>>>> REPLACE
150147
<<<<<<< SEARCH
151148
:start_line:2
152-
:end_line:2
153149
-------
154150
console.log("hello")
155151
=======
@@ -741,7 +737,7 @@ function five() {
741737
// Search around the middle (function three)
742738
// Even though all functions contain the target text,
743739
// it should match the one closest to line 9 first
744-
const result = await strategy.applyDiff(originalContent, diffContent, 9, 9)
740+
const result = await strategy.applyDiff(originalContent, diffContent, 9)
745741
expect(result.success).toBe(true)
746742
if (result.success) {
747743
expect(result.content).toBe(`function one() {
@@ -843,7 +839,6 @@ function five() {
843839
const diffContent = [
844840
"<<<<<<< SEARCH",
845841
":start_line:1",
846-
":end_line:3",
847842
"-------",
848843
"1 | function test() {",
849844
" return true;", // missing line number
@@ -868,7 +863,6 @@ function five() {
868863
const diffContent = [
869864
"<<<<<<< SEARCH",
870865
":start_line:1",
871-
":end_line:3",
872866
"-------",
873867
"| function test() {",
874868
"| return true;",
@@ -1634,7 +1628,6 @@ function five() {
16341628
const diffContent = `
16351629
<<<<<<< SEARCH
16361630
:start_line:2
1637-
:end_line:2
16381631
-------
16391632
2 | line to delete
16401633
=======
@@ -1768,7 +1761,7 @@ function two() {
17681761
}
17691762
>>>>>>> REPLACE`
17701763

1771-
const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
1764+
const result = await strategy.applyDiff(originalContent, diffContent, 5)
17721765
expect(result.success).toBe(true)
17731766
if (result.success) {
17741767
expect(result.content).toBe(`function one() {
@@ -1812,7 +1805,7 @@ function three() {
18121805

18131806
// Even though we specify lines 5-7, it should still find the match at lines 9-11
18141807
// because it's within the 5-line buffer zone
1815-
const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
1808+
const result = await strategy.applyDiff(originalContent, diffContent, 5)
18161809
expect(result.success).toBe(true)
18171810
if (result.success) {
18181811
expect(result.content).toBe(`function one() {
@@ -1866,7 +1859,6 @@ pointer-events: auto; /* Enable clicks on the promotion dialog */
18661859
const diffContent = `test.ts
18671860
<<<<<<< SEARCH
18681861
:start_line:12
1869-
:end_line:13
18701862
-------
18711863
.overlay {
18721864
=======
@@ -1946,7 +1938,6 @@ function five() {
19461938
const diffContent = `test.ts
19471939
<<<<<<< SEARCH
19481940
:start_line:5
1949-
:end_line:7
19501941
-------
19511942
function five() {
19521943
return 5;
@@ -1984,7 +1975,7 @@ function one() {
19841975
}
19851976
>>>>>>> REPLACE`
19861977

1987-
const result = await strategy.applyDiff(originalContent, diffContent, 1, 3)
1978+
const result = await strategy.applyDiff(originalContent, diffContent, 1)
19881979
expect(result.success).toBe(true)
19891980
if (result.success) {
19901981
expect(result.content).toBe(`function one() {
@@ -2018,7 +2009,7 @@ function two() {
20182009
}
20192010
>>>>>>> REPLACE`
20202011

2021-
const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
2012+
const result = await strategy.applyDiff(originalContent, diffContent, 5)
20222013
expect(result.success).toBe(true)
20232014
if (result.success) {
20242015
expect(result.content).toBe(`function one() {
@@ -2064,7 +2055,7 @@ function processData(data) {
20642055
>>>>>>> REPLACE`
20652056

20662057
// Target the second instance of processData
2067-
const result = await strategy.applyDiff(originalContent, diffContent, 10, 12)
2058+
const result = await strategy.applyDiff(originalContent, diffContent, 10)
20682059
expect(result.success).toBe(true)
20692060
if (result.success) {
20702061
expect(result.content).toBe(`function processData(data) {
@@ -2131,49 +2122,6 @@ function three() {
21312122
}
21322123
})
21332124

2134-
it("should search from start of file to end line when only end_line is provided", async () => {
2135-
const originalContent = `
2136-
function one() {
2137-
return 1;
2138-
}
2139-
2140-
function two() {
2141-
return 2;
2142-
}
2143-
2144-
function three() {
2145-
return 3;
2146-
}
2147-
`.trim()
2148-
const diffContent = `test.ts
2149-
<<<<<<< SEARCH
2150-
function one() {
2151-
return 1;
2152-
}
2153-
=======
2154-
function one() {
2155-
return "one";
2156-
}
2157-
>>>>>>> REPLACE`
2158-
2159-
// Only provide end_line, should search from start of file to there
2160-
const result = await strategy.applyDiff(originalContent, diffContent, undefined, 4)
2161-
expect(result.success).toBe(true)
2162-
if (result.success) {
2163-
expect(result.content).toBe(`function one() {
2164-
return "one";
2165-
}
2166-
2167-
function two() {
2168-
return 2;
2169-
}
2170-
2171-
function three() {
2172-
return 3;
2173-
}`)
2174-
}
2175-
})
2176-
21772125
it("should prioritize exact line match over expanded search", async () => {
21782126
const originalContent = `
21792127
function one() {
@@ -2204,7 +2152,7 @@ function process() {
22042152

22052153
// Should match the second instance exactly at lines 10-12
22062154
// even though the first instance at 6-8 is within the expanded search range
2207-
const result = await strategy.applyDiff(originalContent, diffContent, 10, 12)
2155+
const result = await strategy.applyDiff(originalContent, diffContent, 10)
22082156
expect(result.success).toBe(true)
22092157
if (result.success) {
22102158
expect(result.content).toBe(`
@@ -2252,7 +2200,7 @@ function process() {
22522200

22532201
// Specify wrong line numbers (3-5), but content exists at 6-8
22542202
// Should still find and replace it since it's within the expanded range
2255-
const result = await strategy.applyDiff(originalContent, diffContent, 3, 5)
2203+
const result = await strategy.applyDiff(originalContent, diffContent, 3)
22562204
expect(result.success).toBe(true)
22572205
if (result.success) {
22582206
expect(result.content).toBe(`function one() {

src/core/diff/strategies/multi-search-replace.ts

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ Diff format:
107107
\`\`\`
108108
<<<<<<< SEARCH
109109
:start_line: (required) The line number of original content where the search block starts.
110-
:end_line: (required) The line number of original content where the search block ends.
111110
-------
112111
[exact content to find including whitespace]
113112
=======
@@ -132,7 +131,6 @@ Search/Replace content:
132131
\`\`\`
133132
<<<<<<< SEARCH
134133
:start_line:1
135-
:end_line:5
136134
-------
137135
def calculate_total(items):
138136
total = 0
@@ -151,7 +149,6 @@ Search/Replace content with multi edits:
151149
\`\`\`
152150
<<<<<<< SEARCH
153151
:start_line:1
154-
:end_line:2
155152
-------
156153
def calculate_total(items):
157154
sum = 0
@@ -162,7 +159,6 @@ def calculate_sum(items):
162159
163160
<<<<<<< SEARCH
164161
:start_line:4
165-
:end_line:5
166162
-------
167163
total += item
168164
return total
@@ -190,7 +186,6 @@ Only use a single line of '=======' between search and replacement content, beca
190186
.replace(/^\\=======/gm, "=======")
191187
.replace(/^\\>>>>>>>/gm, ">>>>>>>")
192188
.replace(/^\\-------/gm, "-------")
193-
.replace(/^\\:end_line:/gm, ":end_line:")
194189
.replace(/^\\:start_line:/gm, ":start_line:")
195190
}
196191

@@ -240,7 +235,6 @@ Only use a single line of '=======' between search and replacement content, beca
240235
"CORRECT FORMAT:\n\n" +
241236
"<<<<<<< SEARCH\n" +
242237
":start_line: (required) The line number of original content where the search block starts.\n" +
243-
":end_line: (required) The line number of original content where the search block ends.\n" +
244238
"-------\n" +
245239
"[exact content to find including whitespace]\n" +
246240
"=======\n" +
@@ -328,35 +322,32 @@ Only use a single line of '=======' between search and replacement content, beca
328322
3. ((?:\:start_line:\s*(\d+)\s*\n))?
329323
  Optionally matches a “:start_line:” line. The outer capturing group is group 1 and the inner (\d+) is group 2.
330324
331-
4. ((?:\:end_line:\s*(\d+)\s*\n))?
332-
  Optionally matches a “:end_line:” line. Group 3 is the whole match and group 4 is the digits.
333-
334-
5. ((?<!\\)-------\s*\n)?
325+
4. ((?<!\\)-------\s*\n)?
335326
  Optionally matches the “-------” marker line (group 5).
336327
337-
6. ([\s\S]*?)(?:\n)?
328+
5. ([\s\S]*?)(?:\n)?
338329
  Non‐greedy match for the “search content” (group 6) up to the next marker.
339330
340-
7. (?:(?<=\n)(?<!\\)=======\s*\n)
331+
6. (?:(?<=\n)(?<!\\)=======\s*\n)
341332
  Matches the “=======” marker on its own line.
342333
343-
8. ([\s\S]*?)(?:\n)?
334+
7. ([\s\S]*?)(?:\n)?
344335
  Non‐greedy match for the “replace content” (group 7).
345336
346-
9. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
337+
8. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
347338
  Matches the final “>>>>>>> REPLACE” marker on its own line (and requires a following newline or the end of file).
348339
*/
349340

350341
let matches = [
351342
...diffContent.matchAll(
352-
/(?:^|\n)(?<!\\)<<<<<<< SEARCH\s*\n((?:\:start_line:\s*(\d+)\s*\n))?((?:\:end_line:\s*(\d+)\s*\n))?((?<!\\)-------\s*\n)?([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)=======\s*\n)([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)/g,
343+
/(?:^|\n)(?<!\\)<<<<<<< SEARCH\s*\n((?:\:start_line:\s*(\d+)\s*\n))?((?<!\\)-------\s*\n)?([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)=======\s*\n)([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)/g,
353344
),
354345
]
355346

356347
if (matches.length === 0) {
357348
return {
358349
success: false,
359-
error: `Invalid diff format - missing required sections\n\nDebug Info:\n- Expected Format: <<<<<<< SEARCH\\n:start_line: start line\\n:end_line: end line\\n-------\\n[search content]\\n=======\\n[replace content]\\n>>>>>>> REPLACE\n- Tip: Make sure to include start_line/end_line/SEARCH/=======/REPLACE sections with correct markers on new lines`,
350+
error: `Invalid diff format - missing required sections\n\nDebug Info:\n- Expected Format: <<<<<<< SEARCH\\n:start_line: start line\\n-------\\n[search content]\\n=======\\n[replace content]\\n>>>>>>> REPLACE\n- Tip: Make sure to include start_line/SEARCH/=======/REPLACE sections with correct markers on new lines`,
360351
}
361352
}
362353
// Detect line ending from original content
@@ -368,8 +359,8 @@ Only use a single line of '=======' between search and replacement content, beca
368359
const replacements = matches
369360
.map((match) => ({
370361
startLine: Number(match[2] ?? 0),
371-
searchContent: match[6],
372-
replaceContent: match[7],
362+
searchContent: match[4],
363+
replaceContent: match[5],
373364
}))
374365
.sort((a, b) => a.startLine - b.startLine)
375366

@@ -430,15 +421,16 @@ Only use a single line of '=======' between search and replacement content, beca
430421
let searchEndIndex = resultLines.length
431422

432423
// Validate and handle line range if provided
433-
if (startLine && endLine) {
424+
if (startLine) {
434425
// Convert to 0-based index
435426
const exactStartIndex = startLine - 1
436-
const exactEndIndex = endLine - 1
427+
const searchLen = searchLines.length
428+
const exactEndIndex = exactStartIndex + searchLen - 1
437429

438-
if (exactStartIndex < 0 || exactEndIndex > resultLines.length || exactStartIndex > exactEndIndex) {
430+
if (exactStartIndex < 0 || exactEndIndex >= resultLines.length) {
439431
diffResults.push({
440432
success: false,
441-
error: `Line range ${startLine}-${endLine} is invalid (file has ${resultLines.length} lines)\n\nDebug Info:\n- Requested Range: lines ${startLine}-${endLine}\n- File Bounds: lines 1-${resultLines.length}`,
433+
error: `Line range ${startLine}-${startLine + searchLen - 1} is invalid (file has ${resultLines.length} lines)\n\nDebug Info:\n- Requested Range: lines ${startLine}-${startLine + searchLen - 1}\n- File Bounds: lines 1-${resultLines.length}`,
442434
})
443435
continue
444436
}
@@ -453,7 +445,7 @@ Only use a single line of '=======' between search and replacement content, beca
453445
} else {
454446
// Set bounds for buffered search
455447
searchStartIndex = Math.max(0, startLine - (this.bufferLines + 1))
456-
searchEndIndex = Math.min(resultLines.length, endLine + this.bufferLines)
448+
searchEndIndex = Math.min(resultLines.length, startLine + searchLines.length + this.bufferLines)
457449
}
458450
}
459451

@@ -512,14 +504,11 @@ Only use a single line of '=======' between search and replacement content, beca
512504
? `\n\nBest Match Found:\n${addLineNumbers(bestMatchContent, matchIndex + 1)}`
513505
: `\n\nBest Match Found:\n(no match)`
514506

515-
const lineRange =
516-
startLine || endLine
517-
? ` at ${startLine ? `start: ${startLine}` : "start"} to ${endLine ? `end: ${endLine}` : "end"}`
518-
: ""
507+
const lineRange = startLine ? ` at line: ${startLine}` : ""
519508

520509
diffResults.push({
521510
success: false,
522-
error: `No sufficiently similar match found${lineRange} (${Math.floor(bestMatchScore * 100)}% similar, needs ${Math.floor(this.fuzzyThreshold * 100)}%)\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Search Range: ${startLine && endLine ? `lines ${startLine}-${endLine}` : "start to end"}\n- Tried both standard and aggressive line number stripping\n- Tip: Use the read_file tool to get the latest content of the file before attempting to use the apply_diff tool again, as the file content may have changed\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}`,
511+
error: `No sufficiently similar match found${lineRange} (${Math.floor(bestMatchScore * 100)}% similar, needs ${Math.floor(this.fuzzyThreshold * 100)}%)\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Search Range: ${startLine ? `starting at line ${startLine}` : "start to end"}\n- Tried both standard and aggressive line number stripping\n- Tip: Use the read_file tool to get the latest content of the file before attempting to use the apply_diff tool again, as the file content may have changed\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}`,
523512
})
524513
continue
525514
}

src/core/prompts/__tests__/__snapshots__/system.test.ts.snap

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4055,7 +4055,6 @@ Diff format:
40554055
\`\`\`
40564056
<<<<<<< SEARCH
40574057
:start_line: (required) The line number of original content where the search block starts.
4058-
:end_line: (required) The line number of original content where the search block ends.
40594058
-------
40604059
[exact content to find including whitespace]
40614060
=======
@@ -4080,7 +4079,6 @@ Search/Replace content:
40804079
\`\`\`
40814080
<<<<<<< SEARCH
40824081
:start_line:1
4083-
:end_line:5
40844082
-------
40854083
def calculate_total(items):
40864084
total = 0
@@ -4099,7 +4097,6 @@ Search/Replace content with multi edits:
40994097
\`\`\`
41004098
<<<<<<< SEARCH
41014099
:start_line:1
4102-
:end_line:2
41034100
-------
41044101
def calculate_total(items):
41054102
sum = 0
@@ -4110,7 +4107,6 @@ def calculate_sum(items):
41104107

41114108
<<<<<<< SEARCH
41124109
:start_line:4
4113-
:end_line:5
41144110
-------
41154111
total += item
41164112
return total

src/core/tools/applyDiffTool.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ export async function applyDiffTool(
7777
originalContent,
7878
diffContent,
7979
parseInt(block.params.start_line ?? ""),
80-
parseInt(block.params.end_line ?? ""),
8180
)) ?? {
8281
success: false,
8382
error: "No diff strategy available",

0 commit comments

Comments
 (0)