@@ -287,37 +287,6 @@ function findAllMatches(
287287 return positions ;
288288}
289289
290- /** Get the longest line from an array (most likely to be unique) */
291- function getLongestLine ( lines : string [ ] ) : string | null {
292- if ( lines . length === 0 ) {
293- return null ;
294- }
295-
296- let longest = lines [ 0 ] ;
297- for ( const line of lines ) {
298- if ( line . trim ( ) . length > longest . trim ( ) . length ) {
299- longest = line ;
300- }
301- }
302- return longest . trim ( ) . length > 0 ? longest : null ;
303- }
304-
305- /** Find line position in source */
306- function findLinePosition (
307- sourceLines : string [ ] ,
308- line : string ,
309- startFrom : number = 0
310- ) : number [ ] {
311- const positions : number [ ] = [ ] ;
312- const trimmedLine = line . trim ( ) ;
313- for ( let i = startFrom ; i < sourceLines . length ; i ++ ) {
314- if ( sourceLines [ i ] . trim ( ) === trimmedLine ) {
315- positions . push ( i ) ;
316- }
317- }
318- return positions ;
319- }
320-
321290/**
322291 * Find block position using three-layer fallback strategy
323292 */
@@ -388,7 +357,10 @@ function findBlockPosition(
388357 }
389358
390359 // Last resort: removals only
391- if ( removals . length >= 2 ) {
360+ if (
361+ removals . length >= 2 ||
362+ ( removals . length === 1 && removals [ 0 ] . trim ( ) . length >= 5 )
363+ ) {
392364 pos = trimmedMatch ( sourceLines , removals , startFrom ) ;
393365 if ( pos !== - 1 ) {
394366 return pos ;
@@ -576,44 +548,31 @@ export function applyDiff(
576548 } else if ( allPositions . length > 1 ) {
577549 // Multiple matches - try to disambiguate using middle context
578550 if ( firstMiddleContext . length > 0 ) {
579- const longestMiddleLine = getLongestLine ( firstMiddleContext ) ;
551+ // Sort candidates to prefer those after lastBlockEnd
552+ const sortedCandidates = [ ...allPositions ] . sort ( ( a , b ) => {
553+ const aAfter =
554+ a + effectiveContext . length >= lastBlockEnd ? 0 : 1 ;
555+ const bAfter =
556+ b + effectiveContext . length >= lastBlockEnd ? 0 : 1 ;
557+ return aAfter - bAfter || a - b ;
558+ } ) ;
559+
560+ for ( const candidatePos of sortedCandidates ) {
561+ const changePos = candidatePos + effectiveContext . length ;
562+ const searchAfter = changePos + removals . length ;
580563
581- if ( longestMiddleLine ) {
582- // Find positions of the longest middle line
583- const middleLinePositions = findLinePosition (
564+ // Check if middle context matches after this candidate
565+ const middlePositions = findAllMatches (
584566 sourceLines ,
585- longestMiddleLine ,
586- 0
567+ firstMiddleContext ,
568+ searchAfter
587569 ) ;
588-
589- // For each candidate position, check if middle line appears after it
590- // Sort candidates to prefer those after lastBlockEnd
591- const sortedCandidates = [ ...allPositions ] . sort ( ( a , b ) => {
592- const aAfter =
593- a + effectiveContext . length >= lastBlockEnd ? 0 : 1 ;
594- const bAfter =
595- b + effectiveContext . length >= lastBlockEnd ? 0 : 1 ;
596- return aAfter - bAfter || a - b ;
597- } ) ;
598-
599- for ( const candidatePos of sortedCandidates ) {
600- const changePos = candidatePos + effectiveContext . length ;
601- const searchAfter = changePos + removals . length ;
602-
603- // Check if any middle line position is reasonably close after this candidate
604- for ( const middlePos of middleLinePositions ) {
605- if (
606- middlePos >= searchAfter &&
607- middlePos < searchAfter + 100
608- ) {
609- // Found: this candidate has the middle context after it
610- position = changePos ;
611- break ;
612- }
613- }
614- if ( position !== - 1 ) {
615- break ;
616- }
570+ if (
571+ middlePositions . length > 0 &&
572+ middlePositions [ 0 ] < searchAfter + 100
573+ ) {
574+ position = changePos ;
575+ break ;
617576 }
618577 }
619578 }
@@ -641,6 +600,20 @@ export function applyDiff(
641600 position = findBlockPosition ( sourceLines , block , lastBlockEnd ) ;
642601 }
643602
603+ // Fallback: use firstMiddleContext to reverse-locate position
604+ // When context is wrong but middle context exists, find middle context position
605+ // and calculate insertion point by going back removals.length lines
606+ if ( position === - 1 && firstMiddleContext . length > 0 ) {
607+ const middlePositions = findAllMatches (
608+ sourceLines ,
609+ firstMiddleContext ,
610+ lastBlockEnd
611+ ) ;
612+ if ( middlePositions . length >= 1 ) {
613+ position = middlePositions [ 0 ] - removals . length ;
614+ }
615+ }
616+
644617 if ( position === - 1 ) {
645618 const context = block . contextBefore . slice ( 0 , 2 ) . join ( '\n' ) ;
646619 const removal = block . removals . slice ( 0 , 2 ) . join ( '\n' ) ;
0 commit comments