芝麻web文件管理V1.00
编辑当前文件:/home/qrafawbu/tblonline.org/vendor/league/commonmark/src/Extension/Table/TableParser.php
* (c) Webuni s.r.o.
* (c) Colin O'Dell
* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace League\CommonMark\Extension\Table; use League\CommonMark\Block\Element\Document; use League\CommonMark\Block\Element\Paragraph; use League\CommonMark\Block\Parser\BlockParserInterface; use League\CommonMark\Context; use League\CommonMark\ContextInterface; use League\CommonMark\Cursor; use League\CommonMark\EnvironmentAwareInterface; use League\CommonMark\EnvironmentInterface; final class TableParser implements BlockParserInterface, EnvironmentAwareInterface { /** * @var EnvironmentInterface */ private $environment; public function parse(ContextInterface $context, Cursor $cursor): bool { $container = $context->getContainer(); if (!$container instanceof Paragraph) { return false; } $lines = $container->getStrings(); if (count($lines) === 0) { return false; } $lastLine = \array_pop($lines); if (\strpos($lastLine, '|') === false) { return false; } $oldState = $cursor->saveState(); $cursor->advanceToNextNonSpaceOrTab(); $columns = $this->parseColumns($cursor); if (empty($columns)) { $cursor->restoreState($oldState); return false; } $head = $this->parseRow(trim((string) $lastLine), $columns, TableCell::TYPE_HEAD); if (null === $head) { $cursor->restoreState($oldState); return false; } $table = new Table(function (Cursor $cursor, Table $table) use ($columns): bool { // The next line cannot be a new block start // This is a bit inefficient, but it's the only feasible way to check // given the current v1 API. if (self::isANewBlock($this->environment, $cursor->getLine())) { return false; } $row = $this->parseRow(\trim($cursor->getLine()), $columns); if (null === $row) { return false; } $table->getBody()->appendChild($row); return true; }); $table->getHead()->appendChild($head); if (count($lines) >= 1) { $paragraph = new Paragraph(); foreach ($lines as $line) { $paragraph->addLine($line); } $context->replaceContainerBlock($paragraph); $context->addBlock($table); } else { $context->replaceContainerBlock($table); } return true; } /** * @param string $line * @param array
$columns * @param string $type * * @return TableRow|null */ private function parseRow(string $line, array $columns, string $type = TableCell::TYPE_BODY): ?TableRow { $cells = $this->split(new Cursor(\trim($line))); if (empty($cells)) { return null; } // The header row must match the delimiter row in the number of cells if ($type === TableCell::TYPE_HEAD && \count($cells) !== \count($columns)) { return null; } $i = 0; $row = new TableRow(); foreach ($cells as $i => $cell) { if (!array_key_exists($i, $columns)) { return $row; } $row->appendChild(new TableCell(trim($cell), $type, $columns[$i])); } for ($j = count($columns) - 1; $j > $i; --$j) { $row->appendChild(new TableCell('', $type, null)); } return $row; } /** * @param Cursor $cursor * * @return array
*/ private function split(Cursor $cursor): array { if ($cursor->getCharacter() === '|') { $cursor->advanceBy(1); } $cells = []; $sb = ''; while (!$cursor->isAtEnd()) { switch ($c = $cursor->getCharacter()) { case '\\': if ($cursor->peek() === '|') { // Pipe is special for table parsing. An escaped pipe doesn't result in a new cell, but is // passed down to inline parsing as an unescaped pipe. Note that that applies even for the `\|` // in an input like `\\|` - in other words, table parsing doesn't support escaping backslashes. $sb .= '|'; $cursor->advanceBy(1); } else { // Preserve backslash before other characters or at end of line. $sb .= '\\'; } break; case '|': $cells[] = $sb; $sb = ''; break; default: $sb .= $c; } $cursor->advanceBy(1); } if ($sb !== '') { $cells[] = $sb; } return $cells; } /** * @param Cursor $cursor * * @return array
*/ private function parseColumns(Cursor $cursor): array { $columns = []; $pipes = 0; $valid = false; while (!$cursor->isAtEnd()) { switch ($c = $cursor->getCharacter()) { case '|': $cursor->advanceBy(1); $pipes++; if ($pipes > 1) { // More than one adjacent pipe not allowed return []; } // Need at least one pipe, even for a one-column table $valid = true; break; case '-': case ':': if ($pipes === 0 && !empty($columns)) { // Need a pipe after the first column (first column doesn't need to start with one) return []; } $left = false; $right = false; if ($c === ':') { $left = true; $cursor->advanceBy(1); } if ($cursor->match('/^-+/') === null) { // Need at least one dash return []; } if ($cursor->getCharacter() === ':') { $right = true; $cursor->advanceBy(1); } $columns[] = $this->getAlignment($left, $right); // Next, need another pipe $pipes = 0; break; case ' ': case "\t": // White space is allowed between pipes and columns $cursor->advanceToNextNonSpaceOrTab(); break; default: // Any other character is invalid return []; } } if (!$valid) { return []; } return $columns; } private static function getAlignment(bool $left, bool $right): ?string { if ($left && $right) { return TableCell::ALIGN_CENTER; } elseif ($left) { return TableCell::ALIGN_LEFT; } elseif ($right) { return TableCell::ALIGN_RIGHT; } return null; } public function setEnvironment(EnvironmentInterface $environment) { $this->environment = $environment; } private static function isANewBlock(EnvironmentInterface $environment, string $line): bool { $context = new Context(new Document(), $environment); $context->setNextLine($line); $cursor = new Cursor($line); /** @var BlockParserInterface $parser */ foreach ($environment->getBlockParsers() as $parser) { if ($parser->parse($context, $cursor)) { return true; } } return false; } }