@@ -168,103 +168,111 @@ private function enhanceWithWordLevelDiff(string $html, array $operations, array
168168			}
169169		}
170170
171- 	// Second pass: detect moves first (exact content matches at different line positions) 
172- 	$ enhancedOps  = [];
173- 	$ moveDetectedAdds  = [];
174- 	$ moveDetectedRemoves  = [];
171+ 		 // Second pass: detect moves first (exact content matches at different line positions) 
172+ 		 $ enhancedOps  = [];
173+ 		 $ moveDetectedAdds  = [];
174+ 		 $ moveDetectedRemoves  = [];
175175
176- 	// Find exact content matches between removes and adds (moves) 
177- 	foreach  ($ removes  as  $ removeIndex  => $ removeOp ) {
178- 		$ oldLine  = $ oldLines [$ removeOp ['old_line ' ]] ?? '' ;
179- 		$ oldLineNum  = $ removeOp ['old_line ' ] + 1 ;
176+ 		 // Find exact content matches between removes and adds (moves) 
177+ 		 foreach  ($ removes  as  $ removeIndex  => $ removeOp ) {
178+ 			 $ oldLine  = $ oldLines [$ removeOp ['old_line ' ]] ?? '' ;
179+ 			 $ oldLineNum  = $ removeOp ['old_line ' ] + 1 ;
180180
181- 		// Skip empty lines for move detection 
182- 		if  (empty (trim ($ oldLine ))) continue ;
181+ 			// Skip empty lines for move detection 
182+ 			if  (empty (trim ($ oldLine ))) {
183+ 				continue ;
184+ 			}
183185
184- 		// Look for exact match in adds 
185- 		foreach  ($ adds  as  $ addIndex  => $ addOp ) {
186- 			if  (in_array ($ addIndex , $ moveDetectedAdds )) continue ;
186+ 			// Look for exact match in adds 
187+ 			foreach  ($ adds  as  $ addIndex  => $ addOp ) {
188+ 				if  (in_array ($ addIndex , $ moveDetectedAdds )) {
189+ 					continue ;
190+ 				}
187191
188- 			$ newLine  = $ newLines [$ addOp ['new_line ' ]] ?? '' ;
189- 			$ newLineNum  = $ addOp ['new_line ' ] + 1 ;
192+ 				 $ newLine  = $ newLines [$ addOp ['new_line ' ]] ?? '' ;
193+ 				 $ newLineNum  = $ addOp ['new_line ' ] + 1 ;
190194
191- 			// Exact content match but different line positions = move 
192- 			if  (trim ($ oldLine ) === trim ($ newLine ) && $ oldLineNum  !== $ newLineNum ) {
193- 				$ enhancedOps [] = [
194- 					'type '  => 'move ' ,
195- 					'old_line '  => $ removeOp ['old_line ' ],
196- 					'new_line '  => $ addOp ['new_line ' ],
197- 					'content '  => $ newLine
198- 				];
199- 				$ moveDetectedAdds [] = $ addIndex ;
200- 				$ moveDetectedRemoves [] = $ removeIndex ;
201- 				break ; // Found a move for this remove, stop looking 
195+ 				// Exact content match but different line positions = move 
196+ 				if  (trim ($ oldLine ) === trim ($ newLine ) && $ oldLineNum  !== $ newLineNum ) {
197+ 					$ enhancedOps [] = [
198+ 						'type '  => 'move ' ,
199+ 						'old_line '  => $ removeOp ['old_line ' ],
200+ 						'new_line '  => $ addOp ['new_line ' ],
201+ 						'content '  => $ newLine
202+ 					];
203+ 					$ moveDetectedAdds [] = $ addIndex ;
204+ 					$ moveDetectedRemoves [] = $ removeIndex ;
205+ 					break ; // Found a move for this remove, stop looking 
206+ 				}
202207			}
203208		}
204- 	}
205209
206- 	// Third pass: detect modifications from remaining removes/adds 
207- 	$ usedAdds  = $ moveDetectedAdds ; // Start with adds already used in moves 
210+ 		 // Third pass: detect modifications from remaining removes/adds 
211+ 		 $ usedAdds  = $ moveDetectedAdds ; // Start with adds already used in moves 
208212
209- 	// Process remaining removes and try to find matching adds for modifications 
210- 	foreach  ($ removes  as  $ removeIndex  => $ removeOp ) {
211- 		// Skip removes already used in moves 
212- 		if  (in_array ($ removeIndex , $ moveDetectedRemoves )) continue ;
213+ 		// Process remaining removes and try to find matching adds for modifications 
214+ 		foreach  ($ removes  as  $ removeIndex  => $ removeOp ) {
215+ 			// Skip removes already used in moves 
216+ 			if  (in_array ($ removeIndex , $ moveDetectedRemoves )) {
217+ 				continue ;
218+ 			}
213219
214- 		$ bestMatch  = null ;
215- 		$ bestScore  = -1 ;
216- 		$ bestAddIndex  = -1 ;
220+ 			 $ bestMatch  = null ;
221+ 			 $ bestScore  = -1 ;
222+ 			 $ bestAddIndex  = -1 ;
217223
218- 		$ oldLine  = $ oldLines [$ removeOp ['old_line ' ]] ?? '' ;
219- 		$ oldLineNum  = $ removeOp ['old_line ' ] + 1 ;
224+ 			 $ oldLine  = $ oldLines [$ removeOp ['old_line ' ]] ?? '' ;
225+ 			 $ oldLineNum  = $ removeOp ['old_line ' ] + 1 ;
220226
221- 		// Look for best matching add operation 
222- 		foreach  ($ adds  as  $ addIndex  => $ addOp ) {
223- 			if  (in_array ($ addIndex , $ usedAdds )) continue ;
227+ 			// Look for best matching add operation 
228+ 			foreach  ($ adds  as  $ addIndex  => $ addOp ) {
229+ 				if  (in_array ($ addIndex , $ usedAdds )) {
230+ 					continue ;
231+ 				}
224232
225- 			$ newLine  = $ newLines [$ addOp ['new_line ' ]] ?? '' ;
226- 			$ newLineNum  = $ addOp ['new_line ' ] + 1 ;
233+ 				 $ newLine  = $ newLines [$ addOp ['new_line ' ]] ?? '' ;
234+ 				 $ newLineNum  = $ addOp ['new_line ' ] + 1 ;
227235
228- 			// Calculate matching score 
229- 			$ score  = 0 ;
236+ 				 // Calculate matching score 
237+ 				 $ score  = 0 ;
230238
231- 			// Same line number gets highest priority 
232- 			if  ($ oldLineNum  === $ newLineNum ) {
233- 				$ score  += 100 ;
234- 			}
239+ 				 // Same line number gets highest priority 
240+ 				 if  ($ oldLineNum  === $ newLineNum ) {
241+ 					 $ score  += 100 ;
242+ 				 }
235243
236- 			// Similar content gets secondary priority 
237- 			if  ($ this  ->shouldUseWordLevelDiff ($ oldLine , $ newLine )) {
238- 				$ maxLen  = max (strlen ($ oldLine ), strlen ($ newLine ));
239- 				$ distance  = levenshtein ($ oldLine , $ newLine );
240- 				$ similarity  = 1  - ($ distance  / $ maxLen );
241- 				$ score  += $ similarity  * 50 ; // Up to 50 points for similarity 
242- 			}
244+ 				 // Similar content gets secondary priority 
245+ 				 if  ($ this  ->shouldUseWordLevelDiff ($ oldLine , $ newLine )) {
246+ 					 $ maxLen  = max (strlen ($ oldLine ), strlen ($ newLine ));
247+ 					 $ distance  = levenshtein ($ oldLine , $ newLine );
248+ 					 $ similarity  = 1  - ($ distance  / $ maxLen );
249+ 					 $ score  += $ similarity  * 50 ; // Up to 50 points for similarity 
250+ 				 }
243251
244- 			// Proximity bonus (closer line numbers get bonus) 
245- 			$ proximityBonus  = max (0 , 10  - abs ($ oldLineNum  - $ newLineNum ));
246- 			$ score  += $ proximityBonus ;
252+ 				 // Proximity bonus (closer line numbers get bonus) 
253+ 				 $ proximityBonus  = max (0 , 10  - abs ($ oldLineNum  - $ newLineNum ));
254+ 				 $ score  += $ proximityBonus ;
247255
248- 			if  ($ score  > $ bestScore ) {
249- 				$ bestScore  = $ score ;
250- 				$ bestMatch  = $ addOp ;
251- 				$ bestAddIndex  = $ addIndex ;
256+ 				if  ($ score  > $ bestScore ) {
257+ 					$ bestScore  = $ score ;
258+ 					$ bestMatch  = $ addOp ;
259+ 					$ bestAddIndex  = $ addIndex ;
260+ 				}
252261			}
253- 		}
254262
255- 		// If we found a good match, create a modify operation 
256- 		if  ($ bestMatch  && $ bestScore  > 10 ) { // Minimum threshold 
257- 			$ enhancedOps [] = [
258- 				'type '  => 'modify ' ,
259- 				'old_line '  => $ removeOp ['old_line ' ],
260- 				'new_line '  => $ bestMatch ['new_line ' ]
261- 			];
262- 			$ usedAdds [] = $ bestAddIndex ;
263- 		} else  {
264- 			// No good match, keep as remove 
265- 			$ enhancedOps [] = $ removeOp ;
263+ 			// If we found a good match, create a modify operation 
264+ 			if  ($ bestMatch  && $ bestScore  > 10 ) { // Minimum threshold 
265+ 				$ enhancedOps [] = [
266+ 					'type '  => 'modify ' ,
267+ 					'old_line '  => $ removeOp ['old_line ' ],
268+ 					'new_line '  => $ bestMatch ['new_line ' ]
269+ 				];
270+ 				$ usedAdds [] = $ bestAddIndex ;
271+ 			} else  {
272+ 				// No good match, keep as remove 
273+ 				$ enhancedOps [] = $ removeOp ;
274+ 			}
266275		}
267- 	}
268276
269277		// Fourth pass: add remaining unused operations 
270278		// Add remaining unused add operations (not involved in moves or modifications) 
0 commit comments