| | |
| | private function calculateDiff($old, $new) { |
| | - // Normalize line endings to avoid "entire file changed" on CRLF vs LF |
| | + // Normalize line endings |
| | $old = str_replace("\r\n", "\n", $old); |
| | $new = str_replace("\r\n", "\n", $new); |
 |
| | $ops = $this->computeLCS($oldSlice, $newSlice); |
| | |
| | - // Convert Ops to Hunks with Context |
| | - $hunks = []; |
| | - $contextLines = 3; |
| | + // Grouping Optimization: Reorder interleaved +/- to be - then + |
| | + $groupedOps = []; |
| | + $bufferDel = []; |
| | + $bufferAdd = []; |
| | |
| | - // Add full context if file is small, otherwise use hunks |
| | - if (count($ops) === 0) { |
| | - // Identical? Return empty or single hunk of text? |
| | - // If createChange was called, SHAs differed. If content same, maybe just whitespace/normalization? |
| | - // Return empty implies no visual diff. |
| | - return []; |
| | + foreach ($ops as $op) { |
| | + if ($op['t'] === ' ') { |
| | + foreach ($bufferDel as $o) $groupedOps[] = $o; |
| | + foreach ($bufferAdd as $o) $groupedOps[] = $o; |
| | + $bufferDel = []; |
| | + $bufferAdd = []; |
| | + $groupedOps[] = $op; |
| | + } elseif ($op['t'] === '-') { |
| | + $bufferDel[] = $op; |
| | + } elseif ($op['t'] === '+') { |
| | + $bufferAdd[] = $op; |
| | + } |
| | } |
| | + foreach ($bufferDel as $o) $groupedOps[] = $o; |
| | + foreach ($bufferAdd as $o) $groupedOps[] = $o; |
| | + $ops = $groupedOps; |
| | |
| | - // Flatten ops into a stream of changes with line numbers |
| | + // Generate Stream with Context |
| | $stream = []; |
| | |
 |
| | } |
| | |
| | - // Filter stream to create hunks |
| | + // Filter to Hunks |
| | $finalLines = []; |
| | $lastVisibleIndex = -1; |
| | $streamLen = count($stream); |
| | + $contextLines = 3; |
| | |
| | for ($i = 0; $i < $streamLen; $i++) { |
| | $show = false; |
| | |
| | - // Is this line a change? |
| | if ($stream[$i]['t'] !== ' ') { |
| | $show = true; |
| | } else { |
| | - // Check proximity to a change |
| | - // Look ahead |
| | + // Check ahead |
| | for ($j = 1; $j <= $contextLines; $j++) { |
| | if (($i + $j) < $streamLen && $stream[$i + $j]['t'] !== ' ') { |
| | $show = true; |
| | break; |
| | } |
| | } |
| | - // Look behind |
| | + // Check behind |
| | if (!$show) { |
| | for ($j = 1; $j <= $contextLines; $j++) { |
 |
| | if ($show) { |
| | if ($lastVisibleIndex !== -1 && $i > $lastVisibleIndex + 1) { |
| | - // Gap detected |
| | $finalLines[] = ['t' => 'gap']; |
| | } |