PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / 2.8.6
Kubio AI Page Builder v2.8.6
2.8.6 2.8.5 2.8.4 2.8.3 2.8.2 2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / vendor / sebastian / diff / src / Output / UnifiedDiffOutputBuilder.php
kubio / vendor / sebastian / diff / src / Output Last commit date
AbstractChunkOutputBuilder.php 1 year ago DiffOnlyOutputBuilder.php 1 year ago DiffOutputBuilderInterface.php 1 year ago StrictUnifiedDiffOutputBuilder.php 1 year ago UnifiedDiffOutputBuilder.php 1 year ago
UnifiedDiffOutputBuilder.php
273 lines
1 <?php declare(strict_types=1);
2 /*
3 * This file is part of sebastian/diff.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10 namespace SebastianBergmann\Diff\Output;
11
12 use function array_splice;
13 use function count;
14 use function fclose;
15 use function fopen;
16 use function fwrite;
17 use function max;
18 use function min;
19 use function stream_get_contents;
20 use function strlen;
21 use function substr;
22 use SebastianBergmann\Diff\Differ;
23
24 /**
25 * Builds a diff string representation in unified diff format in chunks.
26 */
27 final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder
28 {
29 /**
30 * @var bool
31 */
32 private $collapseRanges = true;
33
34 /**
35 * @var int >= 0
36 */
37 private $commonLineThreshold = 6;
38
39 /**
40 * @var int >= 0
41 */
42 private $contextLines = 3;
43
44 /**
45 * @var string
46 */
47 private $header;
48
49 /**
50 * @var bool
51 */
52 private $addLineNumbers;
53
54 public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false)
55 {
56 $this->header = $header;
57 $this->addLineNumbers = $addLineNumbers;
58 }
59
60 public function getDiff(array $diff): string
61 {
62 $buffer = fopen('php://memory', 'r+b');
63
64 if ('' !== $this->header) {
65 fwrite($buffer, $this->header);
66
67 if ("\n" !== substr($this->header, -1, 1)) {
68 fwrite($buffer, "\n");
69 }
70 }
71
72 if (0 !== count($diff)) {
73 $this->writeDiffHunks($buffer, $diff);
74 }
75
76 $diff = stream_get_contents($buffer, -1, 0);
77
78 fclose($buffer);
79
80 // If the diff is non-empty and last char is not a linebreak: add it.
81 // This might happen when both the `from` and `to` do not have a trailing linebreak
82 $last = substr($diff, -1);
83
84 return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last
85 ? $diff . "\n"
86 : $diff;
87 }
88
89 private function writeDiffHunks($output, array $diff): void
90 {
91 // detect "No newline at end of file" and insert into `$diff` if needed
92
93 $upperLimit = count($diff);
94
95 if (0 === $diff[$upperLimit - 1][1]) {
96 $lc = substr($diff[$upperLimit - 1][0], -1);
97
98 if ("\n" !== $lc) {
99 array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
100 }
101 } else {
102 // search back for the last `+` and `-` line,
103 // check if has trailing linebreak, else add under it warning under it
104 $toFind = [1 => true, 2 => true];
105
106 for ($i = $upperLimit - 1; $i >= 0; --$i) {
107 if (isset($toFind[$diff[$i][1]])) {
108 unset($toFind[$diff[$i][1]]);
109 $lc = substr($diff[$i][0], -1);
110
111 if ("\n" !== $lc) {
112 array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]);
113 }
114
115 if (!count($toFind)) {
116 break;
117 }
118 }
119 }
120 }
121
122 // write hunks to output buffer
123
124 $cutOff = max($this->commonLineThreshold, $this->contextLines);
125 $hunkCapture = false;
126 $sameCount = $toRange = $fromRange = 0;
127 $toStart = $fromStart = 1;
128 $i = 0;
129
130 /** @var int $i */
131 foreach ($diff as $i => $entry) {
132 if (0 === $entry[1]) { // same
133 if (false === $hunkCapture) {
134 ++$fromStart;
135 ++$toStart;
136
137 continue;
138 }
139
140 ++$sameCount;
141 ++$toRange;
142 ++$fromRange;
143
144 if ($sameCount === $cutOff) {
145 $contextStartOffset = ($hunkCapture - $this->contextLines) < 0
146 ? $hunkCapture
147 : $this->contextLines;
148
149 // note: $contextEndOffset = $this->contextLines;
150 //
151 // because we never go beyond the end of the diff.
152 // with the cutoff/contextlines here the follow is never true;
153 //
154 // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) {
155 // $contextEndOffset = count($diff) - 1;
156 // }
157 //
158 // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop
159
160 $this->writeHunk(
161 $diff,
162 $hunkCapture - $contextStartOffset,
163 $i - $cutOff + $this->contextLines + 1,
164 $fromStart - $contextStartOffset,
165 $fromRange - $cutOff + $contextStartOffset + $this->contextLines,
166 $toStart - $contextStartOffset,
167 $toRange - $cutOff + $contextStartOffset + $this->contextLines,
168 $output
169 );
170
171 $fromStart += $fromRange;
172 $toStart += $toRange;
173
174 $hunkCapture = false;
175 $sameCount = $toRange = $fromRange = 0;
176 }
177
178 continue;
179 }
180
181 $sameCount = 0;
182
183 if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) {
184 continue;
185 }
186
187 if (false === $hunkCapture) {
188 $hunkCapture = $i;
189 }
190
191 if (Differ::ADDED === $entry[1]) {
192 ++$toRange;
193 }
194
195 if (Differ::REMOVED === $entry[1]) {
196 ++$fromRange;
197 }
198 }
199
200 if (false === $hunkCapture) {
201 return;
202 }
203
204 // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk,
205 // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold
206
207 $contextStartOffset = $hunkCapture - $this->contextLines < 0
208 ? $hunkCapture
209 : $this->contextLines;
210
211 // prevent trying to write out more common lines than there are in the diff _and_
212 // do not write more than configured through the context lines
213 $contextEndOffset = min($sameCount, $this->contextLines);
214
215 $fromRange -= $sameCount;
216 $toRange -= $sameCount;
217
218 $this->writeHunk(
219 $diff,
220 $hunkCapture - $contextStartOffset,
221 $i - $sameCount + $contextEndOffset + 1,
222 $fromStart - $contextStartOffset,
223 $fromRange + $contextStartOffset + $contextEndOffset,
224 $toStart - $contextStartOffset,
225 $toRange + $contextStartOffset + $contextEndOffset,
226 $output
227 );
228 }
229
230 private function writeHunk(
231 array $diff,
232 int $diffStartIndex,
233 int $diffEndIndex,
234 int $fromStart,
235 int $fromRange,
236 int $toStart,
237 int $toRange,
238 $output
239 ): void {
240 if ($this->addLineNumbers) {
241 fwrite($output, '@@ -' . $fromStart);
242
243 if (!$this->collapseRanges || 1 !== $fromRange) {
244 fwrite($output, ',' . $fromRange);
245 }
246
247 fwrite($output, ' +' . $toStart);
248
249 if (!$this->collapseRanges || 1 !== $toRange) {
250 fwrite($output, ',' . $toRange);
251 }
252
253 fwrite($output, " @@\n");
254 } else {
255 fwrite($output, "@@ @@\n");
256 }
257
258 for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
259 if ($diff[$i][1] === Differ::ADDED) {
260 fwrite($output, '+' . $diff[$i][0]);
261 } elseif ($diff[$i][1] === Differ::REMOVED) {
262 fwrite($output, '-' . $diff[$i][0]);
263 } elseif ($diff[$i][1] === Differ::OLD) {
264 fwrite($output, ' ' . $diff[$i][0]);
265 } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) {
266 fwrite($output, "\n"); // $diff[$i][0]
267 } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */
268 fwrite($output, ' ' . $diff[$i][0]);
269 }
270 }
271 }
272 }
273