diff --git a/example/dark-theme.css b/example/dark-theme.css index bbbc31b..175b075 100644 --- a/example/dark-theme.css +++ b/example/dark-theme.css @@ -130,7 +130,7 @@ a, a:visited { /* * HTML Merged Diff */ -.DifferencesMerged td.ChangeInsert { +.DifferencesMerged td.ChangeReplace { background: #FFDD88; color: #272822; } diff --git a/example/styles.css b/example/styles.css index bd61746..5dd6abe 100644 --- a/example/styles.css +++ b/example/styles.css @@ -112,7 +112,7 @@ pre { /* * HTML Merged Diff */ -.DifferencesMerged td.ChangeInsert { +.DifferencesMerged td.ChangeReplace { background: #FFDD88; } diff --git a/lib/jblond/Diff/Renderer/Html/Merged.php b/lib/jblond/Diff/Renderer/Html/Merged.php index ab75577..679b6a7 100644 --- a/lib/jblond/Diff/Renderer/Html/Merged.php +++ b/lib/jblond/Diff/Renderer/Html/Merged.php @@ -112,7 +112,7 @@ public function generateSkippedLines(): string return <<<HTML <tr> - <th class="$headerClass" title="{$this->lastDeleted}">$marker</th> + <th class="$headerClass" title="$this->lastDeleted">$marker</th> <td class="Skipped">…</td> </tr> HTML; @@ -136,7 +136,7 @@ public function generateLinesEqual(array $changes): string $html .= <<<HTML <tr> - <th class="$headerClass" title="{$this->lastDeleted}">$fromLine</th> + <th class="$headerClass" title="$this->lastDeleted">$fromLine</th> <td>$line</td> </tr> HTML; @@ -165,7 +165,7 @@ public function generateLinesInsert(array $changes): string $html .= <<<HTML <tr> - <th class="$headerClass" title="{$this->lastDeleted}">$toLine</th> + <th class="$headerClass" title="$this->lastDeleted">$toLine</th> <td><ins>$line</ins></td> </tr> HTML; @@ -208,41 +208,68 @@ public function generateLinesDelete(array $changes): string */ public function generateLinesReplace(array $changes): string { - $html = ''; - $headerClass = ''; - - foreach ($changes['base']['lines'] as $lineNo => $line) { - $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; - if (!$lineNo && $this->lastDeleted !== null) { - $headerClass = 'ChangeDelete'; + $html = ''; + $baseLineCount = count($changes['base']['lines']); + $changedLineCount = count($changes['changed']['lines']); + + if (count($changes['base']['lines']) == $changedLineCount) { + // Lines of Version 1 are modified at version 2. + foreach ($changes['base']['lines'] as $lineNo => $line) { + $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; + + // Capture line-parts which are added to the same line at version 2. + $addedParts = []; + preg_match_all('/\x0.*?\x1/', $changes['changed']['lines'][$lineNo], $addedParts, PREG_PATTERN_ORDER); + array_unshift($addedParts[0], ''); + + // Inline Replacement: + // Concatenate line-parts which are removed at version2 with line-parts which are added at version 2. + $line = preg_replace_callback( + '/\x0.*?\x1/', + function ($removedParts) use ($addedParts) { + $addedPart = str_replace(["\0", "\1"], $this->options['insertMarkers'], next($addedParts[0])); + $removedPart = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $removedParts[0]); + + return "$removedPart$addedPart"; + }, + $line + ); + + $html .= <<<HTML +<tr> + <th>$fromLine</th> + <td>$line</td> +</tr> +HTML; } - // Capture added parts. - $addedParts = []; - preg_match_all('/\x0.*?\x1/', $changes['changed']['lines'][$lineNo], $addedParts, PREG_PATTERN_ORDER); - array_unshift($addedParts[0], ''); + return $html; + } - // Concatenate removed parts with added parts. - $line = preg_replace_callback( - '/\x0.*?\x1/', - function ($removedParts) use ($addedParts) { - $addedPart = str_replace(["\0", "\1"], $this->options['insertMarkers'], next($addedParts[0])); - $removedPart = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $removedParts[0]); + // More or less lines at version 2. Block of version 1 is replaced by block of version 2. + $title = ''; - return "$removedPart$addedPart"; - }, - $line - ); + foreach ($changes['changed']['lines'] as $lineNo => $line) { + $toLine = $changes['changed']['offset'] + $lineNo + 1; - $html .= <<<HTML + if (!$lineNo) { + $title = "Lines replaced at {$this->options['title1']}:\n"; + foreach ($changes['base']['lines'] as $baseLineNo => $baseLine) { + $title .= $changes['base']['offset'] + $baseLineNo + 1 . ": $baseLine\n"; + } + } + + $title = htmlentities($title); + $html .= <<<HTML <tr> - <th class="$headerClass" title="{$this->lastDeleted}">$fromLine</th> - <td>$line</td> + <th class="ChangeReplace" title="$title">$toLine</th> + <td class="ChangeReplace">$line</td> </tr> HTML; - $this->lastDeleted = null; } + $this->lineOffset = $this->lineOffset + $changedLineCount - $baseLineCount; + return $html; }