@@ -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