Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ describe("MultiSearchReplaceDiffStrategy", () => {
expect(strategy["validateMarkerSequencing"](diff).success).toBe(true)
})

it("validates correct marker sequence with extra > in SEARCH", () => {
const diff = "<<<<<<< SEARCH>\n" + "some content\n" + "=======\n" + "new content\n" + ">>>>>>> REPLACE"
expect(strategy["validateMarkerSequencing"](diff).success).toBe(true)
})

it("validates multiple correct marker sequences", () => {
const diff =
"<<<<<<< SEARCH\n" +
Expand Down
11 changes: 6 additions & 5 deletions src/core/diff/strategies/multi-file-search-replace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Each file requires its own path, start_line, and diff elements.
const state = { current: State.START, line: 0 }

const SEARCH = "<<<<<<< SEARCH"
const SEARCH_PATTERN = /^<<<<<<< SEARCH>?$/
const SEP = "======="
const REPLACE = ">>>>>>> REPLACE"
const SEARCH_PREFIX = "<<<<<<< "
Expand Down Expand Up @@ -329,7 +330,7 @@ Each file requires its own path, start_line, and diff elements.
})

const lines = diffContent.split("\n")
const searchCount = lines.filter((l) => l.trim() === SEARCH).length
const searchCount = lines.filter((l) => SEARCH_PATTERN.test(l.trim())).length
const sepCount = lines.filter((l) => l.trim() === SEP).length
const replaceCount = lines.filter((l) => l.trim() === REPLACE).length

Expand Down Expand Up @@ -357,20 +358,20 @@ Each file requires its own path, start_line, and diff elements.
: reportMergeConflictError(SEP, SEARCH)
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEARCH)
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === SEARCH) state.current = State.AFTER_SEARCH
if (SEARCH_PATTERN.test(marker)) state.current = State.AFTER_SEARCH
else if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
break

case State.AFTER_SEARCH:
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, SEP)
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, SEP)
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEP)
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === SEP) state.current = State.AFTER_SEPARATOR
break

case State.AFTER_SEPARATOR:
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, REPLACE)
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, REPLACE)
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, REPLACE)
if (marker === SEP)
return likelyBadStructure
Expand Down Expand Up @@ -467,7 +468,7 @@ Each file requires its own path, start_line, and diff elements.
*/
let matches = [
...diffContent.matchAll(
/(?:^|\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,
/(?:^|\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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The detailed regex comment here should be updated to mention the optional > character. Currently it says:

2. (?<!\\)<<<<<<< SEARCH\s*\n Matches the line "<<<<<<< SEARCH" ...

Could we update it to:

2. (?<!\\)<<<<<<< SEARCH>?\s*\n Matches the line "<<<<<<< SEARCH" with optional '>' ...

),
]

Expand Down
11 changes: 6 additions & 5 deletions src/core/diff/strategies/multi-search-replace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ Only use a single line of '=======' between search and replacement content, beca
const state = { current: State.START, line: 0 }

const SEARCH = "<<<<<<< SEARCH"
const SEARCH_PATTERN = /^<<<<<<< SEARCH>?$/
const SEP = "======="
const REPLACE = ">>>>>>> REPLACE"
const SEARCH_PREFIX = "<<<<<<<"
Expand Down Expand Up @@ -268,7 +269,7 @@ Only use a single line of '=======' between search and replacement content, beca
})

const lines = diffContent.split("\n")
const searchCount = lines.filter((l) => l.trim() === SEARCH).length
const searchCount = lines.filter((l) => SEARCH_PATTERN.test(l.trim())).length
const sepCount = lines.filter((l) => l.trim() === SEP).length
const replaceCount = lines.filter((l) => l.trim() === REPLACE).length

Expand Down Expand Up @@ -296,20 +297,20 @@ Only use a single line of '=======' between search and replacement content, beca
: reportMergeConflictError(SEP, SEARCH)
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEARCH)
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === SEARCH) state.current = State.AFTER_SEARCH
if (SEARCH_PATTERN.test(marker)) state.current = State.AFTER_SEARCH
else if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
break

case State.AFTER_SEARCH:
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, SEP)
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, SEP)
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEP)
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
if (marker === SEP) state.current = State.AFTER_SEPARATOR
break

case State.AFTER_SEPARATOR:
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, REPLACE)
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, REPLACE)
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, REPLACE)
if (marker === SEP)
return likelyBadStructure
Expand Down Expand Up @@ -378,7 +379,7 @@ Only use a single line of '=======' between search and replacement content, beca

let matches = [
...diffContent.matchAll(
/(?:^|\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,
/(?:^|\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,
),
]

Expand Down