@@ -10,6 +10,8 @@ class SearchBlock {
1010 public search_lines : string [ ]
1111 public replace_lines : ReplaceLine [ ]
1212 public search_block_start_index : number
13+ public actual_original_line_count : number = 0
14+ public search_to_original_map : Map < number , number > = new Map ( )
1315
1416 public constructor (
1517 search_lines : string [ ] ,
@@ -25,7 +27,7 @@ class SearchBlock {
2527 return this . search_block_start_index
2628 }
2729 get_search_count ( ) {
28- return this . search_lines . length
30+ return this . actual_original_line_count || this . search_lines . length
2931 }
3032}
3133
@@ -61,12 +63,12 @@ export const apply_diff = (params: {
6163} ) : string => {
6264 const original_code_normalized = params . original_code . replace ( / \r \n / g, '\n' )
6365 const original_code_lines = original_code_normalized . split ( / ^ / m)
64- const original_code_lines_normalized = [ ]
66+ const original_code_lines_normalized : { key : number ; value : string } [ ] = [ ]
6567
6668 const patch_normalized = params . diff_patch . replace ( / \r \n / g, '\n' )
6769 const patch_lines = patch_normalized . split ( '\n' )
68- const patch_lines_original = [ ]
69- const patch_lines_normalized = [ ]
70+ const patch_lines_original : string [ ] = [ ]
71+ const patch_lines_normalized : string [ ] = [ ]
7072
7173 let line_count = 0
7274 for ( let i = 0 ; i < original_code_lines . length ; i ++ ) {
@@ -107,11 +109,11 @@ export const apply_diff = (params: {
107109 continue
108110 }
109111
110- if ( line . trim ( ) == '' ) {
112+ if ( line . trim ( ) === '' ) {
111113 line = '~nnn'
112- } else if ( line . trim ( ) == '+' ) {
114+ } else if ( line . trim ( ) === '+' ) {
113115 line = '+~nnn'
114- } else if ( line . trim ( ) == '-' ) {
116+ } else if ( line . trim ( ) === '-' ) {
115117 line = '-~nnn'
116118 }
117119
@@ -149,39 +151,32 @@ export const apply_diff = (params: {
149151 let inside_replace_block = false
150152 let current_block_has_changes = false
151153
154+ const push_block = ( ) => {
155+ if ( search_chunks . length > 0 || replace_chunks . length > 0 ) {
156+ if ( current_block_has_changes ) {
157+ search_replace_blocks . push (
158+ new SearchBlock ( search_chunks , replace_chunks , - 1 )
159+ )
160+ }
161+ }
162+ search_chunks = [ ]
163+ replace_chunks = [ ]
164+ inside_replace_block = false
165+ current_block_has_changes = false
166+ }
167+
152168 for ( let i = 0 ; i < patch_lines_normalized . length ; i ++ ) {
153169 const line = patch_lines_normalized [ i ]
154170 const line_original = patch_lines_original [ i ]
155171
156172 if ( line . startsWith ( '@@' ) ) {
157- if ( search_chunks . length > 0 || replace_chunks . length > 0 ) {
158- if ( current_block_has_changes ) {
159- search_replace_blocks . push (
160- new SearchBlock ( search_chunks , replace_chunks , - 1 )
161- )
162- }
163- }
164- search_chunks = [ ]
165- replace_chunks = [ ]
166- inside_replace_block = false
167- current_block_has_changes = false
173+ push_block ( )
174+ continue
168175 }
169176
170177 if ( line . startsWith ( '-' ) || line . startsWith ( '~' ) ) {
171178 if ( inside_replace_block ) {
172- inside_replace_block = false
173-
174- if ( search_chunks . length > 0 || replace_chunks . length > 0 ) {
175- if ( current_block_has_changes ) {
176- search_replace_blocks . push (
177- new SearchBlock ( search_chunks , replace_chunks , - 1 )
178- )
179- }
180- }
181-
182- search_chunks = [ ]
183- replace_chunks = [ ]
184- current_block_has_changes = false
179+ push_block ( )
185180 }
186181
187182 if ( line . startsWith ( '-' ) ) {
@@ -227,64 +222,72 @@ export const apply_diff = (params: {
227222 }
228223 }
229224
230- if ( search_chunks . length > 0 ) {
231- if ( search_chunks [ search_chunks . length - 1 ] == '~nnn' ) {
225+ if ( search_chunks . length > 0 || replace_chunks . length > 0 ) {
226+ if (
227+ search_chunks [ search_chunks . length - 1 ] === '~nnn' &&
228+ ! inside_replace_block
229+ ) {
232230 search_chunks . pop ( )
233231 replace_chunks . pop ( )
234232 }
235-
236- if ( search_chunks . length != 0 || replace_chunks . length != 0 ) {
237- if ( current_block_has_changes ) {
238- search_replace_blocks . push (
239- new SearchBlock ( search_chunks , replace_chunks , - 1 )
240- )
241- }
242- }
233+ push_block ( )
243234 }
244235
245236 let previous_found_index = 0
246237 for ( let i = 0 ; i < search_replace_blocks . length ; i ++ ) {
247- const search_replace_block = search_replace_blocks [ i ]
248- const search_string = search_replace_block . search_lines . join ( '' )
238+ const block = search_replace_blocks [ i ]
239+
240+ if ( block . search_lines . length === 0 && previous_found_index === 0 ) {
241+ block . search_block_start_index = - 1
242+ continue
243+ }
249244
250245 let found = false
251246 for (
252247 let j = previous_found_index ;
253248 j < original_code_lines_normalized . length ;
254249 j ++
255250 ) {
256- if (
257- search_replace_block . search_lines . length == 0 &&
258- previous_found_index == 0
251+ let search_ptr = 0
252+ let original_ptr = j
253+ const matched_indices : number [ ] = [ ]
254+ const current_search_to_original = new Map < number , number > ( )
255+
256+ while (
257+ search_ptr < block . search_lines . length &&
258+ original_ptr < original_code_lines_normalized . length
259259 ) {
260- search_replace_block . search_block_start_index = - 1
261- found = true
262- break
263- } else {
264- const chunk = original_code_lines_normalized . slice (
265- j ,
266- j + search_replace_block . search_lines . length
267- )
268-
269- const chunk_string = chunk . map ( ( line ) => line . value ) . join ( '' )
260+ const s_val = block . search_lines [ search_ptr ]
261+ const o_val = original_code_lines_normalized [ original_ptr ] . value
270262
271- if ( chunk_string == search_string ) {
272- if ( previous_found_index > chunk [ 0 ] . key ) {
273- throw new Error ( 'Found index is less than previous found index' )
274- }
275-
276- search_replace_block . search_block_start_index = chunk [ 0 ] . key
277- found = true
278-
279- previous_found_index =
280- chunk [ 0 ] . key + search_replace_block . search_lines . length
263+ if ( s_val === o_val ) {
264+ current_search_to_original . set (
265+ search_ptr ,
266+ original_code_lines_normalized [ original_ptr ] . key
267+ )
268+ matched_indices . push ( original_code_lines_normalized [ original_ptr ] . key )
269+ search_ptr ++
270+ original_ptr ++
271+ } else if ( o_val === '~nnn' ) {
272+ matched_indices . push ( original_code_lines_normalized [ original_ptr ] . key )
273+ original_ptr ++
274+ } else {
281275 break
282276 }
283277 }
278+
279+ if ( search_ptr === block . search_lines . length ) {
280+ block . search_block_start_index = matched_indices [ 0 ]
281+ block . actual_original_line_count = matched_indices . length
282+ block . search_to_original_map = current_search_to_original
283+ found = true
284+ previous_found_index = matched_indices [ matched_indices . length - 1 ] + 1
285+ break
286+ }
284287 }
285288
286289 if ( ! found ) {
287- search_replace_block . search_block_start_index = - 2
290+ block . search_block_start_index = - 2
288291 }
289292 }
290293
@@ -297,37 +300,47 @@ export const apply_diff = (params: {
297300
298301 for ( const block of valid_blocks ) {
299302 const start_index =
300- block . get_start_index ( ) == - 1 ? 0 : block . get_start_index ( )
301- const search_count = block . get_search_count ( )
302- const replacement_content = block . replace_lines . map ( ( line ) => {
303- if (
304- line . search_index !== null &&
305- start_index + line . search_index < original_code_lines . length
306- ) {
307- let originalContent =
308- original_code_lines [ start_index + line . search_index ]
309- if ( ! originalContent . endsWith ( '\n' ) ) {
310- originalContent += '\n'
303+ block . get_start_index ( ) === - 1 ? 0 : block . get_start_index ( )
304+ const replacement_content : string [ ] = [ ]
305+ let last_original_idx = start_index - 1
306+
307+ for ( const line of block . replace_lines ) {
308+ if ( line . search_index !== null ) {
309+ const original_idx = block . search_to_original_map . get ( line . search_index )
310+ if ( original_idx !== undefined ) {
311+ for ( let skip = last_original_idx + 1 ; skip < original_idx ; skip ++ ) {
312+ if ( original_code_lines_normalized [ skip ] . value === '~nnn' ) {
313+ replacement_content . push ( original_code_lines [ skip ] )
314+ }
315+ }
316+ let content = original_code_lines [ original_idx ]
317+ if (
318+ ! content . endsWith ( '\n' ) &&
319+ original_idx < original_code_lines . length - 1
320+ )
321+ content += '\n'
322+ replacement_content . push ( content )
323+ last_original_idx = original_idx
311324 }
312- return originalContent
325+ } else {
326+ replacement_content . push ( line . content )
313327 }
314- return line . content
315- } )
316-
317- if ( start_index < 0 || start_index > result_lines . length ) {
318- continue
319328 }
320329
321- const actual_search_count = Math . min (
322- search_count ,
323- result_lines . length - start_index
324- )
330+ const end_original_idx = start_index + block . get_search_count ( ) - 1
331+ for ( let skip = last_original_idx + 1 ; skip <= end_original_idx ; skip ++ ) {
332+ if ( original_code_lines_normalized [ skip ] . value === '~nnn' ) {
333+ replacement_content . push ( original_code_lines [ skip ] )
334+ }
335+ }
325336
326- result_lines . splice (
327- start_index ,
328- actual_search_count ,
329- ...replacement_content
330- )
337+ if ( start_index >= 0 && start_index <= result_lines . length ) {
338+ result_lines . splice (
339+ start_index ,
340+ block . get_search_count ( ) ,
341+ ...replacement_content
342+ )
343+ }
331344 }
332345
333346 return result_lines . join ( '' )
0 commit comments