Skip to content

Commit d70eaf6

Browse files
committed
Add Html Merged renderer.
1 parent b5cfbd5 commit d70eaf6

File tree

4 files changed

+306
-9
lines changed

4 files changed

+306
-9
lines changed

example/dark-theme.css

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,23 @@ a, a:visited {
5252

5353
.Differences .Skipped {
5454
background: #F7F7F7;
55+
color: #000000;
56+
}
57+
58+
.Differences ins,
59+
.Differences del {
60+
text-decoration: none;
5561
}
5662

5763
/*
5864
* HTML Side by Side Diff
5965
*/
6066
.DifferencesSideBySide .ChangeInsert td.Left {
61-
background: green;
67+
background: #008000;
6268
}
6369

6470
.DifferencesSideBySide .ChangeInsert td.Right {
65-
background: green;
71+
background: #008000;
6672
}
6773

6874
.DifferencesSideBySide .ChangeDelete td.Left {
@@ -85,11 +91,6 @@ a, a:visited {
8591
color: #272822;
8692
}
8793

88-
.Differences ins,
89-
.Differences del {
90-
text-decoration: none;
91-
}
92-
9394
.DifferencesSideBySide .ChangeReplace ins,
9495
.DifferencesSideBySide .ChangeReplace del {
9596
background: #EEBB00;
@@ -115,14 +116,39 @@ a, a:visited {
115116
}
116117

117118
.DifferencesInline .ChangeReplace ins {
118-
background: green;
119+
background: #008000;
119120
}
120121

121122
.DifferencesInline .ChangeReplace del {
122123
background: #EE9999;
123124
color: #272822;
124125
}
125126

127+
/*
128+
* HTML Merged Diff
129+
*/
130+
.DifferencesMerged .ChangeReplace .Left,
131+
.DifferencesMerged .ChangeDelete {
132+
background: #FFDDDD;
133+
color: #272822;
134+
}
135+
136+
.DifferencesMerged .ChangeReplace .Right,
137+
.DifferencesMerged .ChangeInsert {
138+
background: #DDFFDD;
139+
color: #272822;
140+
}
141+
142+
.DifferencesMerged .ChangeReplace ins {
143+
background: #008000;
144+
color: #272822;
145+
}
146+
147+
.DifferencesMerged .ChangeReplace del {
148+
background: #EE9999;
149+
color: #272822;
150+
}
151+
126152
/*
127153
* HTML Unified Diff
128154
*/

example/example.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use jblond\Diff;
44
use jblond\Diff\Renderer\Html\Inline;
5+
use jblond\Diff\Renderer\Html\Merged;
56
use jblond\Diff\Renderer\Html\SideBySide;
67
use jblond\Diff\Renderer\Html\Unified as HtmlUnified;
78
use jblond\Diff\Renderer\Text\Context;
@@ -23,7 +24,7 @@
2324
];
2425

2526
// Choose one of the initializations.
26-
$diff = new Diff($sampleA, $sampleB); // Initialize the diff class with default options.
27+
$diff = new Diff($sampleA, $sampleB); // Initialize the diff class with default options.
2728
//$diff = new Diff($sampleA, $sampleB, $diffOptions); // Initialize the diff class with custom options.
2829

2930
// Options for rendering the diff.
@@ -83,6 +84,13 @@ function changeCSS(cssFile, cssLinkIndex) {
8384
echo $diff->isIdentical() ? 'No differences found.' : $diff->Render($renderer);
8485
?>
8586

87+
<h2>HTML Merged Diff</h2>
88+
<?php
89+
// Generate an inline diff.
90+
$renderer = new Merged();
91+
echo $diff->isIdentical() ? 'No differences found.' : $diff->Render($renderer);
92+
?>
93+
8694
<h2>HTML Unified Diff</h2>
8795
<?php
8896
// Generate a unified diff.

example/styles.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
body {
22
background: #FFFFFF;
3+
color: #000000;
34
font-family: Arial, serif;
45
font-size: 12px;
56
}
@@ -106,6 +107,27 @@ pre {
106107
background: #EE9999;
107108
}
108109

110+
/*
111+
* HTML Merged Diff
112+
*/
113+
.DifferencesMerged .ChangeReplace .Left,
114+
.DifferencesMerged .ChangeDelete {
115+
background: #FFDDDD;
116+
}
117+
118+
.DifferencesMerged .ChangeReplace .Right,
119+
.DifferencesMerged .ChangeInsert {
120+
background: #DDFFDD;
121+
}
122+
123+
.DifferencesMerged .ChangeReplace ins {
124+
background: #99EE99;
125+
}
126+
127+
.DifferencesMerged .ChangeReplace del {
128+
background: #EE9999;
129+
}
130+
109131
/*
110132
* HTML Unified Diff
111133
*/
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<?php
2+
3+
namespace jblond\Diff\Renderer\Html;
4+
5+
use jblond\Diff\Renderer\MainRenderer;
6+
use jblond\Diff\Renderer\SubRendererInterface;
7+
8+
/**
9+
* Merged diff generator for PHP DiffLib.
10+
*
11+
* PHP version 7.2 or greater
12+
*
13+
* @package jblond\Diff\Renderer\Text
14+
* @author Ferry Cools <info@DigiLive.nl>
15+
* @copyright (c) 2020 Ferry Cools
16+
* @license New BSD License http://www.opensource.org/licenses/bsd-license.php
17+
* @version 2.2.1
18+
* @link https://github.com/JBlond/php-diff
19+
*/
20+
class Merged extends MainRenderer implements SubRendererInterface
21+
{
22+
/**
23+
* @var array Associative array containing the default options available for this renderer and their default
24+
* value.
25+
* - format Format of the texts.
26+
* - insertMarkers Markers for inserted text.
27+
* - deleteMarkers Markers for removed text.
28+
* - title1 Title of the 1st version of text.
29+
* - title2 Title of the 2nd version of text.
30+
*/
31+
protected $subOptions = [
32+
'format' => 'html',
33+
'insertMarkers' => ['<ins>', '</ins>'],
34+
'deleteMarkers' => ['<del>', '</del>'],
35+
'title1' => 'Version1',
36+
'title2' => 'Version2',
37+
];
38+
protected $lineOffset = 0;
39+
/**
40+
* @var string
41+
*/
42+
protected $lastDeleted;
43+
44+
/**
45+
* Merged constructor.
46+
*
47+
* @param array $options Custom defined options for the inline diff renderer.
48+
*
49+
* @see Inline::$subOptions
50+
*/
51+
public function __construct(array $options = [])
52+
{
53+
parent::__construct($this->subOptions);
54+
$this->setOptions($options);
55+
}
56+
57+
/**
58+
* @inheritDoc
59+
*/
60+
public function render()
61+
{
62+
$changes = parent::renderSequences();
63+
64+
return parent::renderOutput($changes, $this);
65+
}
66+
67+
/**
68+
* @inheritDoc
69+
*/
70+
public function generateDiffHeader(): string
71+
{
72+
return <<<HTML
73+
<table class="Differences DifferencesMerged">
74+
<thead>
75+
<tr>
76+
<th colspan="2">Merge of {$this->options['title1']} &amp; {$this->options['title2']}</th>
77+
</tr>
78+
</thead>
79+
HTML;
80+
}
81+
82+
/**
83+
* @inheritDoc
84+
*/
85+
public function generateBlockHeader(array $changes): string
86+
{
87+
return '<tbody class="Change' . ucfirst($changes['tag']) . '">';
88+
}
89+
90+
/**
91+
* @inheritDoc
92+
*/
93+
public function generateSkippedLines(): string
94+
{
95+
$marker = '&hellip;';
96+
if ($this->lastDeleted !== null) {
97+
$marker = "*$marker";
98+
}
99+
100+
$this->lastDeleted = null;
101+
102+
return <<<HTML
103+
<tr>
104+
<th title="{$this->lastDeleted}">$marker</th>
105+
<td class="Skipped">&hellip;</td>
106+
</tr>
107+
HTML;
108+
}
109+
110+
/**
111+
* @inheritDoc
112+
*/
113+
public function generateLinesEqual(array $changes): string
114+
{
115+
$html = '';
116+
117+
foreach ($changes['base']['lines'] as $lineNo => $line) {
118+
$fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset;
119+
if (!$lineNo && $this->lastDeleted !== null) {
120+
$fromLine = "*$fromLine";
121+
}
122+
123+
$html .= <<<HTML
124+
<tr>
125+
<th title="{$this->lastDeleted}">$fromLine</th>
126+
<td>$line</td>
127+
</tr>
128+
HTML;
129+
$this->lastDeleted = null;
130+
}
131+
132+
return $html;
133+
}
134+
135+
/**
136+
* @inheritDoc
137+
*/
138+
public function generateLinesInsert(array $changes): string
139+
{
140+
$html = '';
141+
142+
foreach ($changes['changed']['lines'] as $lineNo => $line) {
143+
$this->lineOffset++;
144+
$toLine = $changes['base']['offset'] + $this->lineOffset;
145+
if (!$lineNo && $this->lastDeleted !== null) {
146+
$toLine = "*$toLine";
147+
}
148+
149+
$html .= <<<HTML
150+
<tr>
151+
<th title="{$this->lastDeleted}">$toLine</th>
152+
<td><ins>$line</ins></td>
153+
</tr>
154+
HTML;
155+
$this->lastDeleted = null;
156+
}
157+
158+
159+
return $html;
160+
}
161+
162+
/**
163+
* @inheritDoc
164+
*/
165+
public function generateLinesDelete(array $changes): string
166+
{
167+
$this->lineOffset -= count($changes['base']['lines']);
168+
169+
$title = "Lines deleted at {$this->options['title2']}:\n";
170+
171+
foreach ($changes['base']['lines'] as $lineNo => $line) {
172+
$fromLine = $changes['base']['offset'] + $lineNo + 1;
173+
174+
$title .= <<<TEXT
175+
$fromLine: $line
176+
177+
TEXT;
178+
}
179+
180+
$this->lastDeleted = $title;
181+
182+
return '';
183+
}
184+
185+
/**
186+
* @inheritDoc
187+
*/
188+
public function generateLinesReplace(array $changes): string
189+
{
190+
$html = '';
191+
192+
foreach ($changes['base']['lines'] as $lineNo => $line) {
193+
$fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset;
194+
if (!$lineNo && $this->lastDeleted !== null) {
195+
$fromLine = "*$fromLine";
196+
}
197+
198+
// Capture added parts.
199+
preg_match_all('/\x0.*?\x1/', $changes['changed']['lines'][$lineNo], $addedParts, PREG_PATTERN_ORDER);
200+
array_unshift($addedParts[0], '');
201+
202+
// Concatenate removed parts with added parts.
203+
$line = preg_replace_callback(
204+
'/\x0.*?\x1/',
205+
function ($removedParts) use ($addedParts) {
206+
$addedPart = str_replace(["\0", "\1"], $this->options['insertMarkers'], next($addedParts[0]));
207+
$removedPart = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $removedParts[0]);
208+
209+
return "$removedPart$addedPart";
210+
},
211+
$line
212+
);
213+
214+
$html .= <<<HTML
215+
<tr>
216+
<th title="{$this->lastDeleted}">$fromLine</th>
217+
<td>$line</td>
218+
</tr>
219+
HTML;
220+
$this->lastDeleted = null;
221+
}
222+
223+
return $html;
224+
}
225+
226+
/**
227+
* @inheritDoc
228+
*/
229+
public function generateBlockFooter(array $changes): string
230+
{
231+
return '</tbody>';
232+
}
233+
234+
/**
235+
* @inheritDoc
236+
*/
237+
public function generateDiffFooter(): string
238+
{
239+
return '</table>';
240+
}
241+
}

0 commit comments

Comments
 (0)