Chris@14: 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@14: final class Parser Chris@0: { Chris@0: /** Chris@0: * @param string $string Chris@0: * Chris@0: * @return Diff[] Chris@0: */ Chris@14: public function parse(string $string): array Chris@0: { Chris@12: $lines = \preg_split('(\r\n|\r|\n)', $string); Chris@12: Chris@14: if (!empty($lines) && $lines[\count($lines) - 1] === '') { Chris@12: \array_pop($lines); Chris@12: } Chris@12: Chris@12: $lineCount = \count($lines); Chris@14: $diffs = []; Chris@0: $diff = null; Chris@14: $collected = []; Chris@0: Chris@0: for ($i = 0; $i < $lineCount; ++$i) { Chris@12: if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && Chris@12: \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { Chris@0: if ($diff !== null) { Chris@0: $this->parseFileDiff($diff, $collected); Chris@12: Chris@0: $diffs[] = $diff; Chris@14: $collected = []; Chris@0: } Chris@0: Chris@0: $diff = new Diff($fromMatch['file'], $toMatch['file']); Chris@12: Chris@0: ++$i; Chris@0: } else { Chris@12: if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { Chris@0: continue; Chris@0: } Chris@12: Chris@0: $collected[] = $lines[$i]; Chris@0: } Chris@0: } Chris@0: Chris@12: if ($diff !== null && \count($collected)) { Chris@0: $this->parseFileDiff($diff, $collected); Chris@12: 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@14: $chunks = []; Chris@12: $chunk = null; Chris@0: Chris@0: foreach ($lines as $line) { Chris@12: if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { Chris@0: $chunk = new Chunk( Chris@14: (int) $match['start'], Chris@14: isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, Chris@14: (int) $match['end'], Chris@14: isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 Chris@0: ); Chris@0: Chris@0: $chunks[] = $chunk; Chris@14: $diffLines = []; Chris@12: Chris@0: continue; Chris@0: } Chris@0: Chris@12: if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { Chris@0: $type = Line::UNCHANGED; Chris@0: Chris@12: if ($match['type'] === '+') { Chris@0: $type = Line::ADDED; Chris@12: } elseif ($match['type'] === '-') { Chris@0: $type = Line::REMOVED; Chris@0: } Chris@0: Chris@0: $diffLines[] = new Line($type, $match['line']); Chris@0: Chris@12: 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: }