@@ -35,17 +35,21 @@ function getSimilarity(original: string, search: string): number {
3535/**
3636 * Performs a "middle-out" search of `lines` (between [startIndex, endIndex]) to find
3737 * the slice that is most similar to `searchChunk`. Returns the best score, index, and matched text.
38+ * If targetLine is provided, the search starts from that line instead of the midpoint.
3839 */
39- function fuzzySearch ( lines : string [ ] , searchChunk : string , startIndex : number , endIndex : number ) {
40+ function fuzzySearch ( lines : string [ ] , searchChunk : string , startIndex : number , endIndex : number , targetLine ?: number ) {
4041 let bestScore = 0
4142 let bestMatchIndex = - 1
4243 let bestMatchContent = ""
4344 const searchLen = searchChunk . split ( / \r ? \n / ) . length
4445
45- // Middle-out from the midpoint
46- const midPoint = Math . floor ( ( startIndex + endIndex ) / 2 )
47- let leftIndex = midPoint
48- let rightIndex = midPoint + 1
46+ // If targetLine is provided, start from there; otherwise use midpoint
47+ const searchStart =
48+ targetLine !== undefined
49+ ? Math . max ( startIndex , Math . min ( targetLine , endIndex - searchLen ) )
50+ : Math . floor ( ( startIndex + endIndex ) / 2 )
51+ let leftIndex = searchStart
52+ let rightIndex = searchStart + 1
4953
5054 while ( leftIndex >= startIndex || rightIndex <= endIndex - searchLen ) {
5155 if ( leftIndex >= startIndex ) {
@@ -378,7 +382,7 @@ Only use a single line of '=======' between search and replacement content, beca
378382
379383 let matches = [
380384 ...diffContent . matchAll (
381- / (?: ^ | \n ) (?< ! \\ ) < < < < < < < S E A R C H \s * \n ( (?: \: s t a r t _ l i n e : \s * ( \d + ) \s * \n ) ) ? ( (?: \: e n d _ l i n e : \s * ( \d + ) \s * \n ) ) ? ( (?< ! \\ ) - - - - - - - \s * \n ) ? ( [ \s \S ] * ?) (?: \n ) ? (?: (?< = \n ) (?< ! \\ ) = = = = = = = \s * \n ) ( [ \s \S ] * ?) (?: \n ) ? (?: (?< = \n ) (?< ! \\ ) > > > > > > > R E P L A C E ) (? = \n | $ ) / g,
385+ / (?: ^ | \n ) (?< ! \\ ) < < < < < < < S E A R C H \s * \n ( (?: \: s t a r t _ l i n e : \s * ( - ? \d + ) \s * \n ) ) ? ( (?: \: e n d _ l i n e : \s * ( - ? \d + ) \s * \n ) ) ? ( (?< ! \\ ) - - - - - - - \s * \n ) ? ( [ \s \S ] * ?) (?: \n ) ? (?: (?< = \n ) (?< ! \\ ) = = = = = = = \s * \n ) ( [ \s \S ] * ?) (?: \n ) ? (?: (?< = \n ) (?< ! \\ ) > > > > > > > R E P L A C E ) (? = \n | $ ) / g,
382386 ) ,
383387 ]
384388
@@ -464,8 +468,11 @@ Only use a single line of '=======' between search and replacement content, beca
464468
465469 // Validate and handle line range if provided
466470 if ( startLine ) {
471+ // Clamp startLine to valid range (1 to resultLines.length)
472+ const clampedStartLine = Math . max ( 1 , Math . min ( startLine , resultLines . length ) )
473+
467474 // Convert to 0-based index
468- const exactStartIndex = startLine - 1
475+ const exactStartIndex = clampedStartLine - 1
469476 const searchLen = searchLines . length
470477 const exactEndIndex = exactStartIndex + searchLen - 1
471478
@@ -478,8 +485,11 @@ Only use a single line of '=======' between search and replacement content, beca
478485 bestMatchContent = originalChunk
479486 } else {
480487 // Set bounds for buffered search
481- searchStartIndex = Math . max ( 0 , startLine - ( this . bufferLines + 1 ) )
482- searchEndIndex = Math . min ( resultLines . length , startLine + searchLines . length + this . bufferLines )
488+ searchStartIndex = Math . max ( 0 , clampedStartLine - ( this . bufferLines + 1 ) )
489+ searchEndIndex = Math . min (
490+ resultLines . length ,
491+ clampedStartLine + searchLines . length + this . bufferLines ,
492+ )
483493 }
484494 }
485495
@@ -489,7 +499,13 @@ Only use a single line of '=======' between search and replacement content, beca
489499 bestScore,
490500 bestMatchIndex,
491501 bestMatchContent : midContent ,
492- } = fuzzySearch ( resultLines , searchChunk , searchStartIndex , searchEndIndex )
502+ } = fuzzySearch (
503+ resultLines ,
504+ searchChunk ,
505+ searchStartIndex ,
506+ searchEndIndex ,
507+ startLine ? Math . max ( 0 , Math . min ( startLine - 1 , resultLines . length - 1 ) ) : undefined ,
508+ )
493509 matchIndex = bestMatchIndex
494510 bestMatchScore = bestScore
495511 bestMatchContent = midContent
@@ -509,7 +525,13 @@ Only use a single line of '=======' between search and replacement content, beca
509525 bestScore,
510526 bestMatchIndex,
511527 bestMatchContent : aggContent ,
512- } = fuzzySearch ( resultLines , aggressiveSearchChunk , searchStartIndex , searchEndIndex )
528+ } = fuzzySearch (
529+ resultLines ,
530+ aggressiveSearchChunk ,
531+ searchStartIndex ,
532+ searchEndIndex ,
533+ startLine ? startLine - 1 : undefined ,
534+ )
513535 if ( bestMatchIndex !== - 1 && bestScore >= this . fuzzyThreshold ) {
514536 matchIndex = bestMatchIndex
515537 bestMatchScore = bestScore
0 commit comments