Chris@2: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace SebastianBergmann\Diff; Chris@0: Chris@0: /** Chris@0: * Unified diff parser. Chris@0: */ Chris@2: final class Parser Chris@0: { Chris@0: /** Chris@0: * @param string $string Chris@0: * Chris@0: * @return Diff[] Chris@0: */ Chris@2: public function parse(string $string): array Chris@0: { Chris@2: $lines = \preg_split('(\r\n|\r|\n)', $string); Chris@2: Chris@2: if (!empty($lines) && $lines[\count($lines) - 1] === '') { Chris@2: \array_pop($lines); Chris@2: } Chris@2: Chris@2: $lineCount = \count($lines); Chris@2: $diffs = []; Chris@0: $diff = null; Chris@2: $collected = []; Chris@0: Chris@0: for ($i = 0; $i < $lineCount; ++$i) { Chris@2: if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && Chris@2: \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { Chris@0: if ($diff !== null) { Chris@0: $this->parseFileDiff($diff, $collected); Chris@2: Chris@0: $diffs[] = $diff; Chris@2: $collected = []; Chris@0: } Chris@0: Chris@0: $diff = new Diff($fromMatch['file'], $toMatch['file']); Chris@2: Chris@0: ++$i; Chris@0: } else { Chris@2: if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { Chris@0: continue; Chris@0: } Chris@2: Chris@0: $collected[] = $lines[$i]; Chris@0: } Chris@0: } Chris@0: Chris@2: if ($diff !== null && \count($collected)) { Chris@0: $this->parseFileDiff($diff, $collected); Chris@2: Chris@0: $diffs[] = $diff; Chris@0: } Chris@0: Chris@0: return $diffs; Chris@0: } Chris@0: Chris@0: private function parseFileDiff(Diff $diff, array $lines) Chris@0: { Chris@2: $chunks = []; Chris@2: $chunk = null; Chris@0: Chris@0: foreach ($lines as $line) { Chris@2: if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { Chris@0: $chunk = new Chunk( Chris@2: (int) $match['start'], Chris@2: isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, Chris@2: (int) $match['end'], Chris@2: isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 Chris@0: ); Chris@0: Chris@0: $chunks[] = $chunk; Chris@2: $diffLines = []; Chris@2: Chris@0: continue; Chris@0: } Chris@0: Chris@2: if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { Chris@0: $type = Line::UNCHANGED; Chris@0: Chris@2: if ($match['type'] === '+') { Chris@0: $type = Line::ADDED; Chris@2: } elseif ($match['type'] === '-') { Chris@0: $type = Line::REMOVED; Chris@0: } Chris@0: Chris@0: $diffLines[] = new Line($type, $match['line']); Chris@0: Chris@2: if (null !== $chunk) { Chris@0: $chunk->setLines($diffLines); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: $diff->setChunks($chunks); Chris@0: } Chris@0: }