15
15
*/
16
16
final class Differ
17
17
{
18
- /** @var int a safe number for indicating showing all contexts */
18
+ /**
19
+ * @var int a safe number for indicating showing all contexts
20
+ */
19
21
const CONTEXT_ALL = \PHP_INT_MAX >> 3 ;
20
22
23
+ /**
24
+ * @var string used to indicate a line has no EOL
25
+ *
26
+ * Arbitrary chars from the 15-16th Unicode reserved areas
27
+ * and hopefully, they won't appear in source texts
28
+ */
29
+ const LINE_NO_EOL = "\u{fcf28}\u{fc231}" ;
30
+
21
31
/**
22
32
* @var array cached properties and their default values
23
33
*/
@@ -267,7 +277,7 @@ public function getGroupedOpcodes(): array
267
277
}
268
278
269
279
/**
270
- * A EOL-at-EOF-sensitve version of getGroupedOpcodes().
280
+ * A EOL-at-EOF-sensitive version of getGroupedOpcodes().
271
281
*
272
282
* @return int[][][] array of the grouped opcodes for the generated diff (GNU version)
273
283
*/
@@ -279,44 +289,11 @@ public function getGroupedOpcodesGnu(): array
279
289
return $ this ->groupedOpcodesGnu ;
280
290
}
281
291
282
- $ old = \array_map (
283
- function (string $ line ): string {
284
- return "{$ line }\n" ;
285
- },
286
- $ this ->old
287
- );
288
-
289
- $ new = \array_map (
290
- function (string $ line ): string {
291
- return "{$ line }\n" ;
292
- },
293
- $ this ->new
294
- );
295
-
296
- // note that the old and the new are never empty at this point
297
- // they have at least one element "\n" in the array because explode("\n", "") === [""]
298
- $ oldLastIdx = \count ($ old ) - 1 ;
299
- $ newLastIdx = \count ($ new ) - 1 ;
300
- $ oldLast = &$ old [$ oldLastIdx ];
301
- $ newLast = &$ new [$ newLastIdx ];
302
-
303
- if ($ oldLast === "\n" ) {
304
- // remove the last plain "\n" line since we don't need it anymore
305
- unset($ old [$ oldLastIdx ]);
306
- } else {
307
- // this means the original source has no EOL at EOF
308
- // we have to remove the trailing \n from the last line
309
- $ oldLast = \substr ($ oldLast , 0 , -1 );
310
- }
311
-
312
- if ($ newLast === "\n" ) {
313
- unset($ new [$ newLastIdx ]);
314
- } else {
315
- $ newLast = \substr ($ newLast , 0 , -1 );
316
- }
317
-
318
292
return $ this ->groupedOpcodesGnu = $ this ->sequenceMatcher
319
- ->setSequences ($ old , $ new )
293
+ ->setSequences (
294
+ $ this ->makeLinesGnuCompatible ($ this ->old ),
295
+ $ this ->makeLinesGnuCompatible ($ this ->new )
296
+ )
320
297
->getGroupedOpcodes ($ this ->options ['context ' ]);
321
298
}
322
299
@@ -403,4 +380,31 @@ private function getText(array $lines, int $start = 0, ?int $end = null): array
403
380
// hence the length for array_slice() must be non-negative
404
381
return \array_slice ($ lines , $ start , \max (0 , $ end - $ start ));
405
382
}
383
+
384
+ /**
385
+ * Make the lines to be prepared for GNU-style diff.
386
+ *
387
+ * This method checks whether $lines has no EOL at EOF and append a special
388
+ * indicator to the last line.
389
+ *
390
+ * @param string[] $lines the lines
391
+ */
392
+ private function makeLinesGnuCompatible (array $ lines ): array
393
+ {
394
+ // note that the $lines should not be empty at this point
395
+ // they have at least one element "" in the array because explode("\n", "") === [""]
396
+ $ lastLineIdx = \count ($ lines ) - 1 ;
397
+ $ lastLine = &$ lines [$ lastLineIdx ];
398
+
399
+ if ($ lastLine === '' ) {
400
+ // remove the last plain "" line since we don't need it anymore
401
+ unset($ lines [$ lastLineIdx ]);
402
+ } else {
403
+ // this means the original source has no EOL at EOF
404
+ // we append a special indicator to that line so it no longer matches
405
+ $ lastLine .= self ::LINE_NO_EOL ;
406
+ }
407
+
408
+ return $ lines ;
409
+ }
406
410
}
0 commit comments