Skip to content

Commit 8db5890

Browse files
committed
More tolerant search/replace match
1 parent 836371c commit 8db5890

File tree

3 files changed

+17
-10
lines changed

3 files changed

+17
-10
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ describe("MultiSearchReplaceDiffStrategy", () => {
1313
expect(strategy["validateMarkerSequencing"](diff).success).toBe(true)
1414
})
1515

16+
it("validates correct marker sequence with extra > in SEARCH", () => {
17+
const diff = "<<<<<<< SEARCH>\n" + "some content\n" + "=======\n" + "new content\n" + ">>>>>>> REPLACE"
18+
expect(strategy["validateMarkerSequencing"](diff).success).toBe(true)
19+
})
20+
1621
it("validates multiple correct marker sequences", () => {
1722
const diff =
1823
"<<<<<<< SEARCH\n" +

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ Each file requires its own path, start_line, and diff elements.
260260
const state = { current: State.START, line: 0 }
261261

262262
const SEARCH = "<<<<<<< SEARCH"
263+
const SEARCH_PATTERN = /^<<<<<<< SEARCH>?$/
263264
const SEP = "======="
264265
const REPLACE = ">>>>>>> REPLACE"
265266
const SEARCH_PREFIX = "<<<<<<< "
@@ -329,7 +330,7 @@ Each file requires its own path, start_line, and diff elements.
329330
})
330331

331332
const lines = diffContent.split("\n")
332-
const searchCount = lines.filter((l) => l.trim() === SEARCH).length
333+
const searchCount = lines.filter((l) => SEARCH_PATTERN.test(l.trim())).length
333334
const sepCount = lines.filter((l) => l.trim() === SEP).length
334335
const replaceCount = lines.filter((l) => l.trim() === REPLACE).length
335336

@@ -357,20 +358,20 @@ Each file requires its own path, start_line, and diff elements.
357358
: reportMergeConflictError(SEP, SEARCH)
358359
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEARCH)
359360
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
360-
if (marker === SEARCH) state.current = State.AFTER_SEARCH
361+
if (SEARCH_PATTERN.test(marker)) state.current = State.AFTER_SEARCH
361362
else if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
362363
break
363364

364365
case State.AFTER_SEARCH:
365-
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, SEP)
366+
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, SEP)
366367
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
367368
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEP)
368369
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
369370
if (marker === SEP) state.current = State.AFTER_SEPARATOR
370371
break
371372

372373
case State.AFTER_SEPARATOR:
373-
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, REPLACE)
374+
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, REPLACE)
374375
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, REPLACE)
375376
if (marker === SEP)
376377
return likelyBadStructure
@@ -467,7 +468,7 @@ Each file requires its own path, start_line, and diff elements.
467468
*/
468469
let matches = [
469470
...diffContent.matchAll(
470-
/(?:^|\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,
471+
/(?:^|\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,
471472
),
472473
]
473474

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ Only use a single line of '=======' between search and replacement content, beca
199199
const state = { current: State.START, line: 0 }
200200

201201
const SEARCH = "<<<<<<< SEARCH"
202+
const SEARCH_PATTERN = /^<<<<<<< SEARCH>?$/
202203
const SEP = "======="
203204
const REPLACE = ">>>>>>> REPLACE"
204205
const SEARCH_PREFIX = "<<<<<<<"
@@ -268,7 +269,7 @@ Only use a single line of '=======' between search and replacement content, beca
268269
})
269270

270271
const lines = diffContent.split("\n")
271-
const searchCount = lines.filter((l) => l.trim() === SEARCH).length
272+
const searchCount = lines.filter((l) => SEARCH_PATTERN.test(l.trim())).length
272273
const sepCount = lines.filter((l) => l.trim() === SEP).length
273274
const replaceCount = lines.filter((l) => l.trim() === REPLACE).length
274275

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

303304
case State.AFTER_SEARCH:
304-
if (marker === SEARCH) return reportInvalidDiffError(SEARCH, SEP)
305+
if (SEARCH_PATTERN.test(marker)) return reportInvalidDiffError(SEARCH, SEP)
305306
if (marker.startsWith(SEARCH_PREFIX)) return reportMergeConflictError(marker, SEARCH)
306307
if (marker === REPLACE) return reportInvalidDiffError(REPLACE, SEP)
307308
if (marker.startsWith(REPLACE_PREFIX)) return reportMergeConflictError(marker, SEARCH)
308309
if (marker === SEP) state.current = State.AFTER_SEPARATOR
309310
break
310311

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

379380
let matches = [
380381
...diffContent.matchAll(
381-
/(?:^|\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,
382+
/(?:^|\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,
382383
),
383384
]
384385

0 commit comments

Comments
 (0)