-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDiffOpOutputBuilder.php
130 lines (112 loc) · 3.6 KB
/
DiffOpOutputBuilder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php declare(strict_types=1);
namespace Drupal\Component\Diff;
use Drupal\Component\Diff\Engine\DiffOp;
use Drupal\Component\Diff\Engine\DiffOpAdd;
use Drupal\Component\Diff\Engine\DiffOpChange;
use Drupal\Component\Diff\Engine\DiffOpCopy;
use Drupal\Component\Diff\Engine\DiffOpDelete;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;
/**
* Returns a diff as an array of DiffOp operations.
*/
final class DiffOpOutputBuilder implements DiffOutputBuilderInterface {
/**
* A constant to manage removal+addition as a single operation.
*/
private const CHANGED = 999;
/**
* {@inheritdoc}
*/
public function getDiff(array $diff): string {
return serialize($this->toOpsArray($diff));
}
/**
* Converts the output of Differ to an array of DiffOp* value objects.
*
* @param array $diff
* The array output of Differ::diffToArray().
*
* @return \Drupal\Component\Diff\Engine\DiffOp[]
* An array of DiffOp* value objects.
*/
public function toOpsArray(array $diff): array {
$ops = [];
$hunkMode = NULL;
$hunkSource = [];
$hunkTarget = [];
for ($i = 0; $i < count($diff); $i++) {
// Handle a sequence of removals + additions as a sequence of changes, and
// manages the tail if required.
if ($diff[$i][1] === Differ::REMOVED) {
if ($hunkMode !== NULL) {
$ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
$hunkSource = [];
$hunkTarget = [];
}
for ($n = $i; $n < count($diff) && $diff[$n][1] === Differ::REMOVED; $n++) {
$hunkSource[] = $diff[$n][0];
}
for (; $n < count($diff) && $diff[$n][1] === Differ::ADDED; $n++) {
$hunkTarget[] = $diff[$n][0];
}
if (count($hunkTarget) === 0) {
$ops[] = $this->hunkOp(Differ::REMOVED, $hunkSource, $hunkTarget);
}
else {
$ops[] = $this->hunkOp(self::CHANGED, $hunkSource, $hunkTarget);
}
$hunkMode = NULL;
$hunkSource = [];
$hunkTarget = [];
$i = $n - 1;
continue;
}
// When here, we are adding or copying the item. Removing or changing is
// managed above.
if ($hunkMode === NULL) {
$hunkMode = $diff[$i][1];
}
elseif ($hunkMode !== $diff[$i][1]) {
$ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
$hunkMode = $diff[$i][1];
$hunkSource = [];
$hunkTarget = [];
}
$hunkSource[] = $diff[$i][0];
}
if ($hunkMode !== NULL) {
$ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
}
return $ops;
}
/**
* Returns the proper DiffOp object based on the hunk mode.
*
* @param int $mode
* A Differ constant or self::CHANGED.
* @param string[] $source
* An array of strings to be changed/added/removed/copied.
* @param string[] $source
* The array of strings to be changed to when self::CHANGED is specified.
*
* @return \Drupal\Component\Diff\Engine\DiffOp
* A DiffOp* value object.
*
* @throw \InvalidArgumentException
* When $mode is not valid.
*/
private function hunkOp(int $mode, array $source, array $target): DiffOp {
switch ($mode) {
case Differ::OLD:
return new DiffOpCopy($source);
case self::CHANGED:
return new DiffOpChange($source, $target);
case Differ::ADDED:
return new DiffOpAdd($source);
case Differ::REMOVED:
return new DiffOpDelete($source);
}
throw new \InvalidArgumentException("Invalid \$mode {$mode} specified");
}
}