Skip to content

Commit 7b2ab79

Browse files
DigiLiveJBlond
authored andcommitted
Start option to ignore blank lines
* Add option `ignoreLines` to control the ability to ignore blank or empty lines. * Add styles of ignored lines to example css. * Add generator for ignored lines to HTML SideBySide renderer. * Add `options` property to the Sequence Mather. * Refactor option `ignoreNewLines` of the Sequence Matcher. * Add flags to define the level of ignoring blank lines. * Add tag `ignored` to the opCodes. * Add method stripLines to Similarity. * Add default option `ignoreLines` to Diff. * Add tests for testing option `ignoreLines` of the Sequence Matcher. * Reformat code.
1 parent 576830c commit 7b2ab79

File tree

9 files changed

+259
-20
lines changed

9 files changed

+259
-20
lines changed

example/dark-theme.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ a, a:visited {
9797
background: #EEBB00;
9898
}
9999

100+
.DifferencesSideBySide .ChangeIgnore .Left,
101+
.DifferencesSideBySide .ChangeIgnore .Right {
102+
background: #FBF2BF;
103+
}
104+
105+
.DifferencesSideBySide .ChangeIgnore .Left.Ignore {
106+
background: #4B4C57;
107+
}
108+
109+
.DifferencesSideBySide .ChangeIgnore .Right.Ignore {
110+
background: #4B4C57;
111+
}
112+
100113
/*
101114
* HTML Unified Diff
102115
*/

example/example.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'trimEqual' => false,
2121
'ignoreWhitespace' => true,
2222
'ignoreCase' => true,
23+
'ignoreLines' => Diff::DIFF_IGNORE_LINE_EMPTY,
2324
];
2425

2526
// Choose one of the initializations.

example/styles.css

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,43 @@ body {
66
}
77

88
pre {
9-
width: 100%;
109
overflow: auto;
10+
width: 100%;
1111
}
1212

1313
/*
1414
* HTML Renderers - General
1515
*/
1616

1717
.Differences {
18-
width: 100%;
1918
border-collapse: collapse;
2019
border-spacing: 0;
2120
empty-cells: show;
21+
width: 100%;
2222
}
2323

2424
.Differences thead th {
25-
text-align: left;
26-
border-bottom: 1px solid #000000;
2725
background: #AAAAAA;
26+
border-bottom: 1px solid #000000;
2827
color: #000000;
2928
padding: 4px;
29+
text-align: left;
3030
}
3131

3232
.Differences tbody th {
33-
text-align: right;
3433
background: #CCCCCC;
35-
width: 4em;
36-
padding: 1px 2px;
3734
border-right: 1px solid #000000;
38-
vertical-align: top;
3935
font-size: 13px;
36+
padding: 1px 2px;
37+
text-align: right;
38+
vertical-align: top;
39+
width: 4em;
4040
}
4141

4242
.Differences td {
43-
padding: 1px 2px;
4443
font-family: Consolas, monospace;
4544
font-size: 13px;
45+
padding: 1px 2px;
4646
}
4747

4848
.Differences .Skipped {
@@ -77,6 +77,19 @@ pre {
7777
background: #FFDD88;
7878
}
7979

80+
.DifferencesSideBySide .ChangeIgnore .Left,
81+
.DifferencesSideBySide .ChangeIgnore .Right {
82+
background: #FBF2BF;
83+
}
84+
85+
.DifferencesSideBySide .ChangeIgnore .Left.Ignore {
86+
background: #F7F7F7;
87+
}
88+
89+
.DifferencesSideBySide .ChangeIgnore .Right.Ignore {
90+
background: #F7F7F7;
91+
}
92+
8093
.Differences ins,
8194
.Differences del {
8295
text-decoration: none;

lib/jblond/Diff.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
*/
2929
class Diff
3030
{
31+
/**
32+
* Flag to disable ignore of successive empty/blank lines.
33+
*/
34+
public const DIFF_IGNORE_LINE_NONE = 0;
35+
/**
36+
* Flag to ignore successive empty lines.
37+
*/
38+
public const DIFF_IGNORE_LINE_EMPTY = 1;
39+
/**
40+
* Flag to ignore successive blank lines. (Lines which contain no or only non printable characters.)
41+
*/
42+
public const DIFF_IGNORE_LINE_BLANK = 2;
3143
/**
3244
* @var array The first version to compare.
3345
* Each element contains a line of this string.
@@ -48,18 +60,20 @@ class Diff
4860
/**
4961
* @var array Associative array containing the default options available for the diff class and their default value.
5062
*
51-
* - context The amount of lines to include around blocks that differ.
52-
* - trimEqual Strip blocks of equal lines from the start and end of the text.
53-
* - ignoreWhitespace When true, tabs and spaces are ignored while comparing.
54-
* The spacing of version1 is leading.
55-
* - ignoreCase When true, character casing is ignored while comparing.
56-
* The casing of version1 is leading.
63+
* - context The amount of lines to include around blocks that differ.
64+
* - trimEqual Strip blocks of equal lines from the start and end of the text.
65+
* - ignoreWhitespace True to ignore differences in tabs and spaces.
66+
* - ignoreCase True to ignore differences in character casing.
67+
* - ignoreLines 0: None.
68+
* 1: Ignore empty lines.
69+
* 2: Ignore blank lines.
5770
*/
5871
private $defaultOptions = [
5972
'context' => 3,
6073
'trimEqual' => true,
6174
'ignoreWhitespace' => false,
6275
'ignoreCase' => false,
76+
'ignoreLines' => self::DIFF_IGNORE_LINE_NONE,
6377
];
6478

6579
/**

lib/jblond/Diff/Renderer/Html/SideBySide.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,70 @@ public function generateDiffFooter(): string
282282
{
283283
return '</table>';
284284
}
285+
286+
/**
287+
* @inheritDoc
288+
*
289+
* @return string Html code representing table rows showing ignored text.
290+
*/
291+
public function generateLinesIgnore(array $changes): string
292+
{
293+
$html = '';
294+
295+
// Is below comparison result ever false?
296+
if (count($changes['base']['lines']) >= count($changes['changed']['lines'])) {
297+
foreach ($changes['base']['lines'] as $lineNo => $line) {
298+
$fromLine = $changes['base']['offset'] + $lineNo + 1;
299+
$toLine = '&nbsp;';
300+
$changedLine = '&nbsp;';
301+
if (isset($changes['changed']['lines'][$lineNo])) {
302+
$toLine = $changes['changed']['offset'] + $lineNo + 1;
303+
$changedLine = $changes['changed']['lines'][$lineNo];
304+
}
305+
306+
$html .= <<<HTML
307+
<tr>
308+
<th>$fromLine</th>
309+
<td class="Left">
310+
<span>$line</span>
311+
</td>
312+
<th>$toLine</th>
313+
<td class="Right Ignore">
314+
<span>$changedLine</span>
315+
</td>
316+
</tr>
317+
HTML;
318+
}
319+
320+
return $html;
321+
}
322+
323+
foreach ($changes['changed']['lines'] as $lineNo => $changedLine) {
324+
$toLine = $changes['changed']['offset'] + $lineNo + 1;
325+
$fromLine = '&nbsp;';
326+
$line = '&nbsp;';
327+
if (isset($changes['base']['lines'][$lineNo])) {
328+
$fromLine = $changes['base']['offset'] + $lineNo + 1;
329+
$line = $changes['base']['lines'][$lineNo];
330+
}
331+
332+
$line = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $line);
333+
$changedLine = str_replace(["\0", "\1"], $this->options['insertMarkers'], $changedLine);
334+
335+
$html .= <<<HTML
336+
<tr>
337+
<th>$fromLine</th>
338+
<td class="Left Ignore">
339+
<span>$line</span>
340+
</td>
341+
<th>$toLine</th>
342+
<td class="Right">
343+
<span>$changedLine</span>
344+
</td>
345+
</tr>
346+
HTML;
347+
}
348+
349+
return $html;
350+
}
285351
}

lib/jblond/Diff/Renderer/MainRenderer.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public function renderOutput(array $changes, object $subRenderer)
8080
case 'replace':
8181
$output .= $subRenderer->generateLinesReplace($change);
8282
break;
83+
case 'ignore':
84+
// TODO: Keep backward compatible with renderers?
85+
$output .= $subRenderer->generateLinesIgnore($change);
86+
break;
8387
}
8488

8589
$output .= $subRenderer->generateBlockFooter($change);
@@ -130,6 +134,8 @@ protected function renderSequences(): array
130134
* insert - The string in $newText from $startNew to $endNew should be inserted at $startOld in
131135
* $oldText.
132136
* equal - The two strings with the specified ranges are equal.
137+
* ignore - The string in $oldText from $startOld to $endOld and
138+
* the string in $newText from $startNew to $endNew are different, but considered to be equal.
133139
*/
134140

135141
$blockSizeOld = $endOld - $startOld;

lib/jblond/Diff/SequenceMatcher.php

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@
2222
*/
2323
class SequenceMatcher
2424
{
25+
/**
26+
* Flag to disable ignore of successive empty/blank lines.
27+
*/
28+
public const DIFF_IGNORE_LINE_NONE = 0;
29+
/**
30+
* Flag to ignore empty lines.
31+
*/
32+
public const DIFF_IGNORE_LINE_EMPTY = 1;
33+
/**
34+
* Flag to ignore blank lines. (Lines which contain no or only non printable characters.)
35+
*/
36+
public const DIFF_IGNORE_LINE_BLANK = 2;
2537
/**
2638
* @var array The first sequence to compare against.
2739
*/
@@ -30,6 +42,13 @@ class SequenceMatcher
3042
* @var array The second sequence.
3143
*/
3244
protected $new;
45+
/**
46+
* @var array Associative array containing the options that will be applied for generating the diff.
47+
* The key-value pairs are set at the constructor of this class.
48+
*
49+
* @see SequenceMatcher::setOptions()
50+
*/
51+
protected $options = [];
3352
/**
3453
* @var string|array Either a string or an array containing a callback function to determine
3554
* if a line is "junk" or not.
@@ -39,12 +58,10 @@ class SequenceMatcher
3958
* @var array Array of characters that are considered junk from the second sequence. Characters are the array key.
4059
*/
4160
private $junkDict = [];
42-
4361
/**
4462
* @var array Array of indices that do not contain junk elements.
4563
*/
4664
private $b2j = [];
47-
4865
/**
4966
* @var array A list of all of the op-codes for the differences between the compared strings.
5067
*/
@@ -56,14 +73,22 @@ class SequenceMatcher
5673
private $matchingBlocks;
5774

5875
/**
59-
* @var array
76+
* @var array Associative array containing the default options available for the diff class and their default value.
77+
*
78+
* - context The amount of lines to include around blocks that differ.
79+
* - trimEqual Strip blocks of equal lines from the start and end of the text.
80+
* - ignoreWhitespace True to ignore differences in tabs and spaces.
81+
* - ignoreCase True to ignore differences in character casing.
82+
* - ignoreLines 0: None.
83+
* 1: Ignore empty lines.
84+
* 2: Ignore blank lines.
6085
*/
6186
private $defaultOptions = [
6287
'context' => 3,
6388
'trimEqual' => true,
6489
'ignoreWhitespace' => false,
6590
'ignoreCase' => false,
66-
'ignoreNewLines' => false,
91+
'ignoreLines' => self::DIFF_IGNORE_LINE_NONE,
6792
];
6893

6994
/**
@@ -330,6 +355,28 @@ public function getOpCodes(): array
330355
$tag = 'insert';
331356
}
332357

358+
if ($this->options['ignoreLines']) {
359+
$part1 = array_slice($this->old, $i, $ai - $i);
360+
$part2 = array_slice($this->new, $j, $bj - $j);
361+
362+
if ($this->options['ignoreLines'] == 2) {
363+
array_walk($part1, function (&$line) {
364+
$line = trim($line);
365+
});
366+
array_walk($part2, function (&$line) {
367+
$line = trim($line);
368+
});
369+
unset($line);
370+
}
371+
372+
if (
373+
($tag == 'delete' && implode('', $part1) == '') ||
374+
($tag == 'insert' && implode('', $part2) == '')
375+
) {
376+
$tag = 'ignore';
377+
}
378+
}
379+
333380
if ($tag) {
334381
$this->opCodes[] = [
335382
$tag,

lib/jblond/Diff/Similarity.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ public function getSimilarity(int $type = self::CALC_DEFAULT): float
7171
case self::CALC_FASTEST:
7272
return $this->getRatioFastest();
7373
default:
74+
if ($this->options['ignoreLines']) {
75+
$this->stripLines();
76+
}
7477
$matches = array_reduce(
7578
$this->getMatchingBlocks(),
7679
function ($carry, $item) {
@@ -80,6 +83,7 @@ function ($carry, $item) {
8083
);
8184

8285
return $this->calculateRatio($matches, count($this->old) + count($this->new));
86+
// TODO: Restore original (un-stripped) versions?
8387
}
8488
}
8589

@@ -151,6 +155,33 @@ private function getRatioFastest(): float
151155
return $this->calculateRatio(min($aLength, $bLength), $aLength + $bLength);
152156
}
153157

158+
/**
159+
* Strip empty or blank lines from the sequences to compare.
160+
*
161+
*/
162+
private function stripLines(): void
163+
{
164+
foreach (['old', 'new'] as $version) {
165+
if ($this->options['ignoreLines'] == self::DIFF_IGNORE_LINE_BLANK) {
166+
array_walk(
167+
$this->$version,
168+
function (&$line) {
169+
$line = trim($line);
170+
}
171+
);
172+
unset($line);
173+
}
174+
175+
$this->$version = array_filter(
176+
$this->$version,
177+
function ($line) {
178+
return $line != '';
179+
}
180+
);
181+
}
182+
183+
$this->setSequences(array_values($this->old), array_values($this->new));
184+
}
154185

155186
/**
156187
* Helper function to calculate the number of matches for Ratio().

0 commit comments

Comments
 (0)