Skip to content

Commit 4b3f711

Browse files
author
Eric Wheeler
committed
fix: enforce newlines between diff section separators
Require newlines between diff section markers (SEARCH, ======, REPLACE) to prevent content confusion when searching input contains separator markers. Error message mentions required marker newlines. Signed-off-by: Eric Wheeler <roo-code@z.ewheeler.org>
1 parent c7321b0 commit 4b3f711

2 files changed

Lines changed: 33 additions & 4 deletions

File tree

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,6 @@ function test() {
980980
:end_line:4
981981
-------
982982
=======
983-
984983
// End of file
985984
>>>>>>> REPLACE`
986985

@@ -990,7 +989,6 @@ function test() {
990989
expect(result.content).toBe(`function test() {
991990
return true;
992991
}
993-
994992
// End of file`)
995993
}
996994
})

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,47 @@ Only use a single line of '=======' between search and replacement content, beca
234234
}
235235
}
236236

237+
/*
238+
Regex parts:
239+
240+
1. (?:^|\n)
241+
  Ensures the first marker starts at the beginning of the file or right after a newline.
242+
243+
2. (?<!\\)<<<<<<< SEARCH\s*\n
244+
  Matches the line “<<<<<<< SEARCH” (ignoring any trailing spaces) – the negative lookbehind makes sure it isn’t escaped.
245+
246+
3. ((?:\:start_line:\s*(\d+)\s*\n))?
247+
  Optionally matches a “:start_line:” line. The outer capturing group is group 1 and the inner (\d+) is group 2.
248+
249+
4. ((?:\:end_line:\s*(\d+)\s*\n))?
250+
  Optionally matches a “:end_line:” line. Group 3 is the whole match and group 4 is the digits.
251+
252+
5. ((?<!\\)-------\s*\n)?
253+
  Optionally matches the “-------” marker line (group 5).
254+
255+
6. ([\s\S]*?)(?:\n)?
256+
  Non‐greedy match for the “search content” (group 6) up to the next marker.
257+
258+
7. (?:(?<=\n)(?<!\\)=======\s*\n)
259+
  Matches the “=======” marker on its own line.
260+
261+
8. ([\s\S]*?)(?:\n)?
262+
  Non‐greedy match for the “replace content” (group 7).
263+
264+
9. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
265+
  Matches the final “>>>>>>> REPLACE” marker on its own line (and requires a following newline or the end of file).
266+
*/
267+
237268
let matches = [
238269
...diffContent.matchAll(
239-
/(?<!\\)<<<<<<< SEARCH\n(:start_line:\s*(\d+)\n){0,1}(:end_line:\s*(\d+)\n){0,1}((?<!\\)-------\n){0,1}([\s\S]*?)\n?(?<!\\)=======\n([\s\S]*?)\n?(?<!\\)>>>>>>> REPLACE/g,
270+
/(?:^|\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,
240271
),
241272
]
242273

243274
if (matches.length === 0) {
244275
return {
245276
success: false,
246-
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`,
277+
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`,
247278
}
248279
}
249280
// Detect line ending from original content

0 commit comments

Comments
 (0)