annotate vendor/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@14 1 <?php declare(strict_types=1);
Chris@14 2 /*
Chris@14 3 * This file is part of sebastian/diff.
Chris@14 4 *
Chris@14 5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
Chris@14 6 *
Chris@14 7 * For the full copyright and license information, please view the LICENSE
Chris@14 8 * file that was distributed with this source code.
Chris@14 9 */
Chris@14 10
Chris@14 11 namespace SebastianBergmann\Diff\Output;
Chris@14 12
Chris@14 13 /**
Chris@14 14 * Builds a diff string representation in unified diff format in chunks.
Chris@14 15 */
Chris@14 16 final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder
Chris@14 17 {
Chris@14 18 /**
Chris@14 19 * @var string
Chris@14 20 */
Chris@14 21 private $header;
Chris@14 22
Chris@14 23 /**
Chris@14 24 * @var bool
Chris@14 25 */
Chris@14 26 private $addLineNumbers;
Chris@14 27
Chris@14 28 public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false)
Chris@14 29 {
Chris@14 30 $this->header = $header;
Chris@14 31 $this->addLineNumbers = $addLineNumbers;
Chris@14 32 }
Chris@14 33
Chris@14 34 public function getDiff(array $diff): string
Chris@14 35 {
Chris@14 36 $buffer = \fopen('php://memory', 'r+b');
Chris@14 37
Chris@14 38 if ('' !== $this->header) {
Chris@14 39 \fwrite($buffer, $this->header);
Chris@14 40 if ("\n" !== \substr($this->header, -1, 1)) {
Chris@14 41 \fwrite($buffer, "\n");
Chris@14 42 }
Chris@14 43 }
Chris@14 44
Chris@14 45 $this->writeDiffChunked($buffer, $diff, $this->getCommonChunks($diff));
Chris@14 46
Chris@14 47 $diff = \stream_get_contents($buffer, -1, 0);
Chris@14 48
Chris@14 49 \fclose($buffer);
Chris@14 50
Chris@14 51 return $diff;
Chris@14 52 }
Chris@14 53
Chris@14 54 // `old` is an array with key => value pairs . Each pair represents a start and end index of `diff`
Chris@14 55 // of a list of elements all containing `same` (0) entries.
Chris@14 56 private function writeDiffChunked($output, array $diff, array $old)
Chris@14 57 {
Chris@14 58 $upperLimit = \count($diff);
Chris@14 59 $start = 0;
Chris@14 60 $fromStart = 0;
Chris@14 61 $toStart = 0;
Chris@14 62
Chris@14 63 if (\count($old)) { // no common parts, list all diff entries
Chris@14 64 \reset($old);
Chris@14 65
Chris@14 66 // iterate the diff, go from chunk to chunk skipping common chunk of lines between those
Chris@14 67 do {
Chris@14 68 $commonStart = \key($old);
Chris@14 69 $commonEnd = \current($old);
Chris@14 70
Chris@14 71 if ($commonStart !== $start) {
Chris@14 72 list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $commonStart);
Chris@14 73 $this->writeChunk($output, $diff, $start, $commonStart, $fromStart, $fromRange, $toStart, $toRange);
Chris@14 74
Chris@14 75 $fromStart += $fromRange;
Chris@14 76 $toStart += $toRange;
Chris@14 77 }
Chris@14 78
Chris@14 79 $start = $commonEnd + 1;
Chris@14 80 $commonLength = $commonEnd - $commonStart + 1; // calculate number of non-change lines in the common part
Chris@14 81 $fromStart += $commonLength;
Chris@14 82 $toStart += $commonLength;
Chris@14 83 } while (false !== \next($old));
Chris@14 84
Chris@14 85 \end($old); // short cut for finding possible last `change entry`
Chris@14 86 $tmp = \key($old);
Chris@14 87 \reset($old);
Chris@14 88 if ($old[$tmp] === $upperLimit - 1) {
Chris@14 89 $upperLimit = $tmp;
Chris@14 90 }
Chris@14 91 }
Chris@14 92
Chris@14 93 if ($start < $upperLimit - 1) { // check for trailing (non) diff entries
Chris@14 94 do {
Chris@14 95 --$upperLimit;
Chris@14 96 } while (isset($diff[$upperLimit][1]) && $diff[$upperLimit][1] === 0);
Chris@14 97 ++$upperLimit;
Chris@14 98
Chris@14 99 list($fromRange, $toRange) = $this->getChunkRange($diff, $start, $upperLimit);
Chris@14 100 $this->writeChunk($output, $diff, $start, $upperLimit, $fromStart, $fromRange, $toStart, $toRange);
Chris@14 101 }
Chris@14 102 }
Chris@14 103
Chris@14 104 private function writeChunk(
Chris@14 105 $output,
Chris@14 106 array $diff,
Chris@14 107 int $diffStartIndex,
Chris@14 108 int $diffEndIndex,
Chris@14 109 int $fromStart,
Chris@14 110 int $fromRange,
Chris@14 111 int $toStart,
Chris@14 112 int $toRange
Chris@14 113 ) {
Chris@14 114 if ($this->addLineNumbers) {
Chris@14 115 \fwrite($output, '@@ -' . (1 + $fromStart));
Chris@14 116
Chris@14 117 if ($fromRange > 1) {
Chris@14 118 \fwrite($output, ',' . $fromRange);
Chris@14 119 }
Chris@14 120
Chris@14 121 \fwrite($output, ' +' . (1 + $toStart));
Chris@14 122 if ($toRange > 1) {
Chris@14 123 \fwrite($output, ',' . $toRange);
Chris@14 124 }
Chris@14 125
Chris@14 126 \fwrite($output, " @@\n");
Chris@14 127 } else {
Chris@14 128 \fwrite($output, "@@ @@\n");
Chris@14 129 }
Chris@14 130
Chris@14 131 for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
Chris@14 132 if ($diff[$i][1] === 1 /* ADDED */) {
Chris@14 133 \fwrite($output, '+' . $diff[$i][0]);
Chris@14 134 } elseif ($diff[$i][1] === 2 /* REMOVED */) {
Chris@14 135 \fwrite($output, '-' . $diff[$i][0]);
Chris@14 136 } else { /* Not changed (old) 0 or Warning 3 */
Chris@14 137 \fwrite($output, ' ' . $diff[$i][0]);
Chris@14 138 }
Chris@14 139
Chris@14 140 $lc = \substr($diff[$i][0], -1);
Chris@14 141 if ($lc !== "\n" && $lc !== "\r") {
Chris@14 142 \fwrite($output, "\n"); // \No newline at end of file
Chris@14 143 }
Chris@14 144 }
Chris@14 145 }
Chris@14 146
Chris@14 147 private function getChunkRange(array $diff, int $diffStartIndex, int $diffEndIndex): array
Chris@14 148 {
Chris@14 149 $toRange = 0;
Chris@14 150 $fromRange = 0;
Chris@14 151
Chris@14 152 for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) {
Chris@14 153 if ($diff[$i][1] === 1) { // added
Chris@14 154 ++$toRange;
Chris@14 155 } elseif ($diff[$i][1] === 2) { // removed
Chris@14 156 ++$fromRange;
Chris@14 157 } elseif ($diff[$i][1] === 0) { // same
Chris@14 158 ++$fromRange;
Chris@14 159 ++$toRange;
Chris@14 160 }
Chris@14 161 }
Chris@14 162
Chris@14 163 return [$fromRange, $toRange];
Chris@14 164 }
Chris@14 165 }