Skip to content

Commit 06882f5

Browse files
authored
Don't break if an end_line is passed into a diff (#2743)
1 parent 87af3b3 commit 06882f5

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,25 @@ function helloWorld() {
159159
}
160160
})
161161

162+
it("should replace matching content when end_line is passed in", async () => {
163+
const originalContent = 'function hello() {\n console.log("hello")\n}\n'
164+
const diffContent = `test.ts
165+
<<<<<<< SEARCH
166+
:start_line:1
167+
:end_line:1
168+
-------
169+
function hello() {
170+
=======
171+
function helloWorld() {
172+
>>>>>>> REPLACE`
173+
174+
const result = await strategy.applyDiff(originalContent, diffContent)
175+
expect(result.success).toBe(true)
176+
if (result.success) {
177+
expect(result.content).toBe('function helloWorld() {\n console.log("hello")\n}\n')
178+
}
179+
})
180+
162181
it("should match content with different surrounding whitespace", async () => {
163182
const originalContent = "\nfunction example() {\n return 42;\n}\n\n"
164183
const diffContent = `test.ts

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ Only use a single line of '=======' between search and replacement content, beca
186186
.replace(/^\\=======/gm, "=======")
187187
.replace(/^\\>>>>>>>/gm, ">>>>>>>")
188188
.replace(/^\\-------/gm, "-------")
189+
.replace(/^\\:end_line:/gm, ":end_line:")
189190
.replace(/^\\:start_line:/gm, ":start_line:")
190191
}
191192

@@ -322,25 +323,28 @@ Only use a single line of '=======' between search and replacement content, beca
322323
3. ((?:\:start_line:\s*(\d+)\s*\n))?
323324
  Optionally matches a “:start_line:” line. The outer capturing group is group 1 and the inner (\d+) is group 2.
324325
325-
4. ((?<!\\)-------\s*\n)?
326+
4. ((?:\:end_line:\s*(\d+)\s*\n))?
327+
  Optionally matches a “:end_line:” line. Group 3 is the whole match and group 4 is the digits.
328+
329+
5. ((?<!\\)-------\s*\n)?
326330
  Optionally matches the “-------” marker line (group 5).
327331
328-
5. ([\s\S]*?)(?:\n)?
332+
6. ([\s\S]*?)(?:\n)?
329333
  Non‐greedy match for the “search content” (group 6) up to the next marker.
330334
331-
6. (?:(?<=\n)(?<!\\)=======\s*\n)
335+
7. (?:(?<=\n)(?<!\\)=======\s*\n)
332336
  Matches the “=======” marker on its own line.
333337
334-
7. ([\s\S]*?)(?:\n)?
338+
8. ([\s\S]*?)(?:\n)?
335339
  Non‐greedy match for the “replace content” (group 7).
336340
337-
8. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
341+
9. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
338342
  Matches the final “>>>>>>> REPLACE” marker on its own line (and requires a following newline or the end of file).
339343
*/
340344

341345
let matches = [
342346
...diffContent.matchAll(
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,
347+
/(?:^|\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,
344348
),
345349
]
346350

@@ -359,8 +363,8 @@ Only use a single line of '=======' between search and replacement content, beca
359363
const replacements = matches
360364
.map((match) => ({
361365
startLine: Number(match[2] ?? 0),
362-
searchContent: match[4],
363-
replaceContent: match[5],
366+
searchContent: match[6],
367+
replaceContent: match[7],
364368
}))
365369
.sort((a, b) => a.startLine - b.startLine)
366370

0 commit comments

Comments
 (0)