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>
This commit is contained in:
Eric Wheeler 2025-03-21 19:41:41 -07:00
parent c7321b0107
commit 4b3f711746
2 changed files with 33 additions and 4 deletions

View file

@ -980,7 +980,6 @@ function test() {
:end_line:4
-------
=======
// End of file
>>>>>>> REPLACE`
@ -990,7 +989,6 @@ function test() {
expect(result.content).toBe(`function test() {
return true;
}
// End of file`)
}
})

View file

@ -234,16 +234,47 @@ Only use a single line of '=======' between search and replacement content, beca
}
}
/*
Regex parts:
1. (?:^|\n)
Ensures the first marker starts at the beginning of the file or right after a newline.
2. (?<!\\)<<<<<<< SEARCH\s*\n
Matches the line <<<<<<< SEARCH (ignoring any trailing spaces) the negative lookbehind makes sure it isnt escaped.
3. ((?:\:start_line:\s*(\d+)\s*\n))?
Optionally matches a :start_line: line. The outer capturing group is group1 and the inner (\d+) is group2.
4. ((?:\:end_line:\s*(\d+)\s*\n))?
Optionally matches a :end_line: line. Group3 is the whole match and group4 is the digits.
5. ((?<!\\)-------\s*\n)?
Optionally matches the ------- marker line (group5).
6. ([\s\S]*?)(?:\n)?
Nongreedy match for the search content (group6) up to the next marker.
7. (?:(?<=\n)(?<!\\)=======\s*\n)
Matches the ======= marker on its own line.
8. ([\s\S]*?)(?:\n)?
Nongreedy match for the replace content (group7).
9. (?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)
Matches the final >>>>>>> REPLACE marker on its own line (and requires a following newline or the end of file).
*/
let matches = [
...diffContent.matchAll(
/(?<!\\)<<<<<<< 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,
/(?:^|\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,
),
]
if (matches.length === 0) {
return {
success: false,
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`,
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`,
}
}
// Detect line ending from original content