Skip to content

Fix #90 - Diff mode Merged shows result only partially #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions example/dark-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ body {
}

pre {
width: 100%;
overflow: auto;
width: 100%;
}

a, a:visited {
Expand All @@ -19,35 +19,35 @@ a, a:visited {
*/

.Differences {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
empty-cells: show;
width: 100%;
}

.Differences thead th {
text-align: left;
border-bottom: 1px solid #000000;
background: #AAAAAA;
border-bottom: 1px solid #000000;
color: #000000;
padding: 4px;
text-align: left;
}

.Differences tbody th {
text-align: right;
background: #AAAAAA;
border-right: 1px solid #000000;
color: #272822;
width: 4em;
font-size: 13px;
padding: 1px 2px;
border-right: 1px solid #000000;
text-align: right;
vertical-align: top;
font-size: 13px;
width: 4em;
}

.Differences td {
padding: 1px 2px;
font-family: Consolas, monospace;
font-size: 13px;
padding: 1px 2px;
}

.Differences .Skipped {
Expand All @@ -65,7 +65,7 @@ a, a:visited {
* HTML Side by Side Diff
*/
.DifferencesSideBySide .ChangeInsert td.Left {
background: #008000;
background: #DDFFDD;
}

.DifferencesSideBySide .ChangeInsert td.Right {
Expand All @@ -83,7 +83,7 @@ a, a:visited {
}

.DifferencesSideBySide .ChangeReplace .Left {
background: #FFEE99;
background: #FFDD88;
color: #272822;
}

Expand Down Expand Up @@ -114,10 +114,12 @@ a, a:visited {
.DifferencesUnified .ChangeReplace .Right,
.DifferencesUnified .ChangeInsert .Right {
background: #DDFFDD;
color: #272822;
}

.DifferencesUnified .ChangeReplace ins {
background: #008000;
color: #272822;
}

.DifferencesUnified .ChangeReplace del {
Expand All @@ -128,13 +130,16 @@ a, a:visited {
/*
* HTML Merged Diff
*/
.DifferencesMerged .ChangeReplace .Left,
.DifferencesMerged td.ChangeReplace {
background: #FFDD88;
color: #272822;
}

.DifferencesMerged .ChangeDelete {
background: #FFDDDD;
color: #272822;
}

.DifferencesMerged .ChangeReplace .Right,
.DifferencesMerged .ChangeInsert {
background: #DDFFDD;
color: #272822;
Expand All @@ -153,3 +158,7 @@ a, a:visited {
.DifferencesMerged th.ChangeDelete {
background-image: linear-gradient(-45deg, #AAAAAA 0%, #EE9999 100%);
}

.DifferencesMerged th.ChangeReplace {
background-image: linear-gradient(-45deg, #CCCCCC 0%, #FFDD88 100%);
}
33 changes: 20 additions & 13 deletions example/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,44 @@ body {
}

pre {
width: 100%;
overflow: auto;
width: 100%;
}

/*
* HTML Renderers - General
*/

.Differences {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
empty-cells: show;
width: 100%;
}

.Differences thead th {
text-align: left;
border-bottom: 1px solid #000000;
background: #AAAAAA;
border-bottom: 1px solid #000000;
color: #000000;
padding: 4px;
text-align: left;
}

.Differences tbody th {
text-align: right;
background: #CCCCCC;
width: 4em;
padding: 1px 2px;
border-right: 1px solid #000000;
vertical-align: top;
font-size: 13px;
padding: 1px 2px;
text-align: right;
vertical-align: top;
width: 4em;
}

.Differences td {
padding: 1px 2px;
font-family: Consolas, monospace;
font-size: 13px;
font-family: Consolas, monospace;
font-size: 13px;
padding: 1px 2px;
vertical-align: top;
}

.Differences .Skipped {
Expand Down Expand Up @@ -111,12 +112,14 @@ pre {
/*
* HTML Merged Diff
*/
.DifferencesMerged .ChangeReplace .Left,
.DifferencesMerged td.ChangeReplace {
background: #FFDD88;
}

.DifferencesMerged .ChangeDelete {
background: #FFDDDD;
}

.DifferencesMerged .ChangeReplace .Right,
.DifferencesMerged .ChangeInsert {
background: #DDFFDD;
}
Expand All @@ -132,3 +135,7 @@ pre {
.DifferencesMerged th.ChangeDelete {
background-image: linear-gradient(-45deg, #CCCCCC 0%, #EE9999 100%);
}

.DifferencesMerged th.ChangeReplace {
background-image: linear-gradient(-45deg, #CCCCCC 0%, #FFDD88 100%);
}
100 changes: 64 additions & 36 deletions lib/jblond/Diff/Renderer/Html/Merged.php
Original file line number Diff line number Diff line change
Expand Up @@ -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">&hellip;</td>
</tr>
HTML;
Expand All @@ -125,18 +125,19 @@ public function generateSkippedLines(): string
*/
public function generateLinesEqual(array $changes): string
{
$html = '';
$headerClass = '';
$html = '';

foreach ($changes['base']['lines'] as $lineNo => $line) {
$fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset;
$fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset;
$headerClass = '';

if (!$lineNo && $this->lastDeleted !== null) {
$headerClass = 'ChangeDelete';
}

$html .= <<<HTML
<tr>
<th class="$headerClass" title="{$this->lastDeleted}">$fromLine</th>
<th class="$headerClass" title="$this->lastDeleted">$fromLine</th>
<td>$line</td>
</tr>
HTML;
Expand All @@ -153,19 +154,19 @@ public function generateLinesEqual(array $changes): string
*/
public function generateLinesInsert(array $changes): string
{
$html = '';
$headerClass = '';
$html = '';

foreach ($changes['changed']['lines'] as $lineNo => $line) {
$this->lineOffset++;
$toLine = $changes['base']['offset'] + $this->lineOffset;
$toLine = $changes['base']['offset'] + $this->lineOffset;
$headerClass = '';
if (!$lineNo && $this->lastDeleted !== null) {
$headerClass = 'ChangeDelete';
}

$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;
Expand All @@ -185,7 +186,7 @@ public function generateLinesDelete(array $changes): string
{
$this->lineOffset -= count($changes['base']['lines']);

$title = "Lines deleted at {$this->options['title2']}:\n";
$title = "Lines of {$this->options['title1']} deleted at {$this->options['title2']}:\n";

foreach ($changes['base']['lines'] as $lineNo => $line) {
$fromLine = $changes['base']['offset'] + $lineNo + 1;
Expand All @@ -196,7 +197,7 @@ public function generateLinesDelete(array $changes): string
TEXT;
}

$this->lastDeleted = $title;
$this->lastDeleted = htmlentities($title);

return '';
}
Expand All @@ -208,41 +209,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;
}

Expand Down
Loading