Update website
This commit is contained in:
parent
4413528994
commit
1d90fbf296
6865 changed files with 1091082 additions and 0 deletions
78
vendor/doctrine/rst-parser/lib/Parser/Buffer.php
vendored
Normal file
78
vendor/doctrine/rst-parser/lib/Parser/Buffer.php
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use function array_pop;
|
||||
use function count;
|
||||
use function implode;
|
||||
|
||||
final class Buffer
|
||||
{
|
||||
/** @var string[] */
|
||||
private $lines = [];
|
||||
|
||||
/** @param string[] $lines */
|
||||
public function __construct(array $lines = [])
|
||||
{
|
||||
$this->lines = $lines;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->lines === [];
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->lines);
|
||||
}
|
||||
|
||||
public function has(int $key): bool
|
||||
{
|
||||
return isset($this->lines[$key]);
|
||||
}
|
||||
|
||||
public function get(int $key): string
|
||||
{
|
||||
return $this->lines[$key] ?? '';
|
||||
}
|
||||
|
||||
public function push(string $line): void
|
||||
{
|
||||
$this->lines[] = $line;
|
||||
}
|
||||
|
||||
public function set(int $key, string $line): void
|
||||
{
|
||||
$this->lines[$key] = $line;
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getLines(): array
|
||||
{
|
||||
return $this->lines;
|
||||
}
|
||||
|
||||
public function getLinesString(): string
|
||||
{
|
||||
return implode("\n", $this->lines);
|
||||
}
|
||||
|
||||
public function pop(): ?string
|
||||
{
|
||||
return array_pop($this->lines);
|
||||
}
|
||||
|
||||
public function getLastLine(): ?string
|
||||
{
|
||||
$lastLineKey = count($this->lines) - 1;
|
||||
|
||||
if (! isset($this->lines[$lastLineKey])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->lines[$lastLineKey];
|
||||
}
|
||||
}
|
23
vendor/doctrine/rst-parser/lib/Parser/DefinitionList.php
vendored
Normal file
23
vendor/doctrine/rst-parser/lib/Parser/DefinitionList.php
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
final class DefinitionList
|
||||
{
|
||||
/** @var DefinitionListTerm[] */
|
||||
private $terms = [];
|
||||
|
||||
/** @param DefinitionListTerm[] $terms */
|
||||
public function __construct(array $terms)
|
||||
{
|
||||
$this->terms = $terms;
|
||||
}
|
||||
|
||||
/** @return DefinitionListTerm[] */
|
||||
public function getTerms(): array
|
||||
{
|
||||
return $this->terms;
|
||||
}
|
||||
}
|
58
vendor/doctrine/rst-parser/lib/Parser/DefinitionListTerm.php
vendored
Normal file
58
vendor/doctrine/rst-parser/lib/Parser/DefinitionListTerm.php
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\RST\Nodes\Node;
|
||||
use Doctrine\RST\Nodes\SpanNode;
|
||||
use RuntimeException;
|
||||
|
||||
final class DefinitionListTerm
|
||||
{
|
||||
/** @var SpanNode */
|
||||
private $term;
|
||||
|
||||
/** @var SpanNode[] */
|
||||
private $classifiers = [];
|
||||
|
||||
/** @var Node[] */
|
||||
private $definitions = [];
|
||||
|
||||
/**
|
||||
* @param SpanNode[] $classifiers
|
||||
* @param Node[] $definitions
|
||||
*/
|
||||
public function __construct(SpanNode $term, array $classifiers, array $definitions)
|
||||
{
|
||||
$this->term = $term;
|
||||
$this->classifiers = $classifiers;
|
||||
$this->definitions = $definitions;
|
||||
}
|
||||
|
||||
public function getTerm(): SpanNode
|
||||
{
|
||||
return $this->term;
|
||||
}
|
||||
|
||||
/** @return SpanNode[] */
|
||||
public function getClassifiers(): array
|
||||
{
|
||||
return $this->classifiers;
|
||||
}
|
||||
|
||||
/** @return Node[] */
|
||||
public function getDefinitions(): array
|
||||
{
|
||||
return $this->definitions;
|
||||
}
|
||||
|
||||
public function getFirstDefinition(): Node
|
||||
{
|
||||
if (! isset($this->definitions[0])) {
|
||||
throw new RuntimeException('No definitions found.');
|
||||
}
|
||||
|
||||
return $this->definitions[0];
|
||||
}
|
||||
}
|
56
vendor/doctrine/rst-parser/lib/Parser/Directive.php
vendored
Normal file
56
vendor/doctrine/rst-parser/lib/Parser/Directive.php
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
final class Directive
|
||||
{
|
||||
/** @var string */
|
||||
private $variable;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var string */
|
||||
private $data;
|
||||
|
||||
/** @var mixed[] */
|
||||
private $options = [];
|
||||
|
||||
/** @param mixed[] $options */
|
||||
public function __construct(string $variable, string $name, string $data, array $options = [])
|
||||
{
|
||||
$this->variable = $variable;
|
||||
$this->name = $name;
|
||||
$this->data = $data;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function getVariable(): string
|
||||
{
|
||||
return $this->variable;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getData(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/** @return mixed[] */
|
||||
public function getOptions(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
public function setOption(string $key, $value): void
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
32
vendor/doctrine/rst-parser/lib/Parser/DirectiveOption.php
vendored
Normal file
32
vendor/doctrine/rst-parser/lib/Parser/DirectiveOption.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
final class DirectiveOption
|
||||
{
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var mixed */
|
||||
private $value;
|
||||
|
||||
/** @param mixed $value */
|
||||
public function __construct(string $name, $value)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/** @return mixed */
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
754
vendor/doctrine/rst-parser/lib/Parser/DocumentParser.php
vendored
Normal file
754
vendor/doctrine/rst-parser/lib/Parser/DocumentParser.php
vendored
Normal file
|
@ -0,0 +1,754 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\RST\Directives\Directive;
|
||||
use Doctrine\RST\Environment;
|
||||
use Doctrine\RST\Event\PostParseDocumentEvent;
|
||||
use Doctrine\RST\Event\PreParseDocumentEvent;
|
||||
use Doctrine\RST\FileIncluder;
|
||||
use Doctrine\RST\NodeFactory\NodeFactory;
|
||||
use Doctrine\RST\Nodes\DocumentNode;
|
||||
use Doctrine\RST\Nodes\Node;
|
||||
use Doctrine\RST\Nodes\TableNode;
|
||||
use Doctrine\RST\Nodes\TitleNode;
|
||||
use Doctrine\RST\Parser;
|
||||
use Doctrine\RST\Parser\Directive as ParserDirective;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
use function array_reverse;
|
||||
use function array_search;
|
||||
use function assert;
|
||||
use function chr;
|
||||
use function explode;
|
||||
use function fwrite;
|
||||
use function getenv;
|
||||
use function ltrim;
|
||||
use function max;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function trim;
|
||||
|
||||
use const PHP_SAPI;
|
||||
use const STDERR;
|
||||
|
||||
final class DocumentParser
|
||||
{
|
||||
/** @var Parser */
|
||||
private $parser;
|
||||
|
||||
/** @var Environment */
|
||||
private $environment;
|
||||
|
||||
/** @var NodeFactory */
|
||||
private $nodeFactory;
|
||||
|
||||
/** @var EventManager */
|
||||
private $eventManager;
|
||||
|
||||
/** @var Directive[] */
|
||||
private $directives = [];
|
||||
|
||||
/** @var bool */
|
||||
private $includeAllowed = true;
|
||||
|
||||
/** @var string */
|
||||
private $includeRoot = '';
|
||||
|
||||
/** @var DocumentNode */
|
||||
private $document;
|
||||
|
||||
/** @var false|string|null */
|
||||
private $specialLetter;
|
||||
|
||||
/** @var ParserDirective|null */
|
||||
private $directive;
|
||||
|
||||
/** @var LineDataParser */
|
||||
private $lineDataParser;
|
||||
|
||||
/** @var LineChecker */
|
||||
private $lineChecker;
|
||||
|
||||
/** @var TableParser */
|
||||
private $tableParser;
|
||||
|
||||
/** @var Buffer */
|
||||
private $buffer;
|
||||
|
||||
/** @var Node|null */
|
||||
private $nodeBuffer;
|
||||
|
||||
/** @var bool */
|
||||
private $isCode = false;
|
||||
|
||||
/** @var Lines */
|
||||
private $lines;
|
||||
|
||||
/** @var int|null */
|
||||
private $currentLineNumber;
|
||||
|
||||
/** @var string */
|
||||
private $state;
|
||||
|
||||
/** @var TitleNode */
|
||||
private $lastTitleNode;
|
||||
|
||||
/** @var TitleNode[] */
|
||||
private $openTitleNodes = [];
|
||||
|
||||
/** @var int */
|
||||
private $listOffset = 0;
|
||||
|
||||
/** @var string|null */
|
||||
private $listMarker = null;
|
||||
|
||||
/** @param Directive[] $directives */
|
||||
public function __construct(
|
||||
Parser $parser,
|
||||
Environment $environment,
|
||||
NodeFactory $nodeFactory,
|
||||
EventManager $eventManager,
|
||||
array $directives,
|
||||
bool $includeAllowed,
|
||||
string $includeRoot
|
||||
) {
|
||||
$this->parser = $parser;
|
||||
$this->environment = $environment;
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->eventManager = $eventManager;
|
||||
$this->directives = $directives;
|
||||
$this->includeAllowed = $includeAllowed;
|
||||
$this->includeRoot = $includeRoot;
|
||||
$this->lineDataParser = new LineDataParser($this->parser, $eventManager);
|
||||
$this->lineChecker = new LineChecker();
|
||||
$this->tableParser = new TableParser();
|
||||
$this->buffer = new Buffer();
|
||||
}
|
||||
|
||||
public function getDocument(): DocumentNode
|
||||
{
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
public function parse(string $contents): DocumentNode
|
||||
{
|
||||
$preParseDocumentEvent = new PreParseDocumentEvent($this->parser, $contents);
|
||||
|
||||
$this->eventManager->dispatchEvent(
|
||||
PreParseDocumentEvent::PRE_PARSE_DOCUMENT,
|
||||
$preParseDocumentEvent
|
||||
);
|
||||
|
||||
$this->document = $this->nodeFactory->createDocumentNode($this->environment);
|
||||
|
||||
$this->init();
|
||||
|
||||
$this->parseLines(trim($preParseDocumentEvent->getContents()));
|
||||
|
||||
foreach ($this->directives as $name => $directive) {
|
||||
$directive->finalize($this->document);
|
||||
}
|
||||
|
||||
$this->eventManager->dispatchEvent(
|
||||
PostParseDocumentEvent::POST_PARSE_DOCUMENT,
|
||||
new PostParseDocumentEvent($this->document)
|
||||
);
|
||||
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
private function init(): void
|
||||
{
|
||||
$this->specialLetter = false;
|
||||
$this->buffer = new Buffer();
|
||||
$this->nodeBuffer = null;
|
||||
$this->listOffset = 0;
|
||||
$this->listMarker = null;
|
||||
}
|
||||
|
||||
private function setState(string $state): void
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
private function prepareDocument(string $document): string
|
||||
{
|
||||
$document = str_replace("\r\n", "\n", $document);
|
||||
$document = sprintf("\n%s\n", $document);
|
||||
|
||||
$document = (new FileIncluder(
|
||||
$this->environment,
|
||||
$this->includeAllowed,
|
||||
$this->includeRoot
|
||||
))->includeFiles($document);
|
||||
|
||||
// Removing UTF-8 BOM
|
||||
$document = str_replace("\xef\xbb\xbf", '', $document);
|
||||
|
||||
// Replace \u00a0 with " "
|
||||
$document = str_replace(chr(194) . chr(160), ' ', $document);
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
private function createLines(string $document): Lines
|
||||
{
|
||||
return new Lines(explode("\n", $document));
|
||||
}
|
||||
|
||||
private function parseLines(string $document): void
|
||||
{
|
||||
$document = $this->prepareDocument($document);
|
||||
|
||||
$this->lines = $this->createLines($document);
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
foreach ($this->lines as $i => $line) {
|
||||
$this->currentLineNumber = $i + 1;
|
||||
while (true) {
|
||||
if ($this->parseLine($line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->currentLineNumber = null;
|
||||
|
||||
// DocumentNode is flushed twice to trigger the directives
|
||||
$this->flush();
|
||||
$this->flush();
|
||||
|
||||
foreach ($this->openTitleNodes as $titleNode) {
|
||||
$this->endOpenSection($titleNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this line has completed process.
|
||||
*
|
||||
* If false is returned, this function will be called again with the same line.
|
||||
* This is useful when you switched state and want to parse the line again
|
||||
* with the new state (e.g. when the end of a list is found, you want the line
|
||||
* to be parsed as "BEGIN" again).
|
||||
*/
|
||||
private function parseLine(string $line): bool
|
||||
{
|
||||
if (getenv('SHELL_VERBOSITY') >= 3 && PHP_SAPI === 'cli') {
|
||||
fwrite(STDERR, sprintf("Parsing line: %s\n", $line));
|
||||
}
|
||||
|
||||
switch ($this->state) {
|
||||
case State::BEGIN:
|
||||
if (trim($line) !== '') {
|
||||
if ($this->lineChecker->isListLine($line, $this->listMarker, $this->listOffset, $this->lines->getNextLine())) {
|
||||
$this->setState(State::LIST);
|
||||
$this->buffer->push($line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Represents a literal block here the entire line is literally "::"
|
||||
// Ref: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#literal-blocks
|
||||
// > If it occurs as a paragraph of its own, that paragraph is completely left out of the document.
|
||||
if (trim($line) === '::') {
|
||||
$this->isCode = true;
|
||||
|
||||
// return true to move onto the next line, this line is omitted
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->lineChecker->isBlockLine($line)) {
|
||||
if ($this->isCode) {
|
||||
$this->setState(State::CODE);
|
||||
} else {
|
||||
$this->setState(State::BLOCK);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->lineChecker->isComment($line)) {
|
||||
$this->flush();
|
||||
$this->setState(State::COMMENT);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->parseLink($line)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->lineChecker->isDirective($line)) {
|
||||
$this->setState(State::DIRECTIVE);
|
||||
$this->buffer = new Buffer();
|
||||
$this->flush();
|
||||
$this->initDirective($line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$separatorLineConfig = $this->tableParser->parseTableSeparatorLine($line);
|
||||
if ($separatorLineConfig !== null) {
|
||||
$this->setState(State::TABLE);
|
||||
|
||||
$tableNode = $this->nodeFactory->createTableNode(
|
||||
$separatorLineConfig,
|
||||
$this->tableParser->guessTableType($line),
|
||||
$this->lineChecker
|
||||
);
|
||||
|
||||
$this->nodeBuffer = $tableNode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trim($this->lines->getNextLine()) !== '' && $this->lineChecker->isIndented($this->lines->getNextLine())) {
|
||||
$this->setState(State::DEFINITION_LIST);
|
||||
$this->buffer->push($line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->getCurrentDirective() !== null && ! $this->getCurrentDirective()->appliesToNonBlockContent()) {
|
||||
// If there is a directive set, it means we are the line *after* that directive
|
||||
// But the state is being set to NORMAL, which means we are a non-indented line.
|
||||
// Some special directives (like class) allow their content to be non-indented.
|
||||
// But most do not, which means that our directive is now finished.
|
||||
// We flush so that the directive can be processed. It will be passed a
|
||||
// null node (We know because we are currently in a NEW state. If there
|
||||
// had been legitimately-indented content, that would have matched some
|
||||
// other state (e.g. BLOCK or CODE) and flushed when it finished.
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
$this->setState(State::NORMAL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State::LIST:
|
||||
if (! $this->lineChecker->isListLine($line, $this->listMarker, $this->listOffset) && ! $this->lineChecker->isBlockLine($line, max(1, $this->listOffset))) {
|
||||
if (trim($this->lines->getPreviousLine()) !== '') {
|
||||
$this->environment->getErrorManager()->warning(
|
||||
'List ends without a blank line; unexpected unindent',
|
||||
$this->environment->getCurrentFileName(),
|
||||
$this->currentLineNumber !== null ? $this->currentLineNumber - 1 : null
|
||||
);
|
||||
}
|
||||
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// the list item offset is determined by the offset of the first text.
|
||||
// An offset of 1 or lower indicates that the list line didn't contain any text.
|
||||
if ($this->listOffset <= 1) {
|
||||
$this->listOffset = strlen($line) - strlen(ltrim($line));
|
||||
}
|
||||
|
||||
$this->buffer->push($line);
|
||||
|
||||
break;
|
||||
|
||||
case State::DEFINITION_LIST:
|
||||
if ($this->lineChecker->isDefinitionListEnded($line, $this->lines->getNextLine())) {
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->buffer->push($line);
|
||||
|
||||
break;
|
||||
|
||||
case State::TABLE:
|
||||
if (trim($line) === '') {
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
} else {
|
||||
$separatorLineConfig = $this->tableParser->parseTableSeparatorLine($line);
|
||||
|
||||
// not sure if this is possible, being cautious
|
||||
if (! $this->nodeBuffer instanceof TableNode) {
|
||||
throw new Exception('Node Buffer should be a TableNode instance');
|
||||
}
|
||||
|
||||
// push the separator or content line onto the TableNode
|
||||
if ($separatorLineConfig !== null) {
|
||||
$this->nodeBuffer->pushSeparatorLine($separatorLineConfig);
|
||||
} else {
|
||||
$this->nodeBuffer->pushContentLine($line);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State::NORMAL:
|
||||
if (trim($line) !== '') {
|
||||
$specialLetter = $this->lineChecker->isSpecialLine($line);
|
||||
|
||||
if ($specialLetter !== null) {
|
||||
$this->specialLetter = $specialLetter;
|
||||
|
||||
$lastLine = $this->buffer->pop();
|
||||
|
||||
if ($lastLine !== null) {
|
||||
$this->buffer = new Buffer([$lastLine]);
|
||||
$this->setState(State::TITLE);
|
||||
} else {
|
||||
$this->buffer->push($line);
|
||||
$this->setState(State::SEPARATOR);
|
||||
}
|
||||
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
} elseif ($this->lineChecker->isDirective($line)) {
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
} elseif ($this->lineChecker->isComment($line)) {
|
||||
$this->flush();
|
||||
$this->setState(State::COMMENT);
|
||||
} else {
|
||||
$this->buffer->push($line);
|
||||
}
|
||||
} else {
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State::COMMENT:
|
||||
if (! $this->lineChecker->isComment($line) && (trim($line) === '' || $line[0] !== ' ')) {
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State::BLOCK:
|
||||
case State::CODE:
|
||||
if (! $this->lineChecker->isBlockLine($line)) {
|
||||
// the previous line(s) was in a block (indented), but
|
||||
// this line is no longer indented
|
||||
$this->flush();
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->buffer->push($line);
|
||||
|
||||
break;
|
||||
|
||||
case State::DIRECTIVE:
|
||||
if (! $this->isDirectiveOption($line)) {
|
||||
if (! $this->lineChecker->isDirective($line)) {
|
||||
$directive = $this->getCurrentDirective();
|
||||
$this->isCode = $directive !== null ? $directive->wantCode() : false;
|
||||
$this->setState(State::BEGIN);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->flush();
|
||||
$this->initDirective($line);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->environment->getErrorManager()->error('Parser ended in an unexcepted state');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function flush(): void
|
||||
{
|
||||
$node = null;
|
||||
|
||||
$this->isCode = false;
|
||||
|
||||
if ($this->hasBuffer()) {
|
||||
switch ($this->state) {
|
||||
case State::TITLE:
|
||||
$data = $this->buffer->getLinesString();
|
||||
|
||||
$level = $this->environment->getLevel((string) $this->specialLetter);
|
||||
$level = $this->environment->getConfiguration()->getInitialHeaderLevel() + $level - 1;
|
||||
|
||||
$token = $this->environment->createTitle($level);
|
||||
|
||||
$node = $this->nodeFactory->createTitleNode(
|
||||
$this->parser->createSpanNode($data),
|
||||
$level,
|
||||
$token
|
||||
);
|
||||
|
||||
if ($this->lastTitleNode !== null) {
|
||||
// current level is less than previous so we need to
|
||||
// end previous open sections with a greater or equal level
|
||||
if ($node->getLevel() < $this->lastTitleNode->getLevel()) {
|
||||
foreach (array_reverse($this->openTitleNodes) as $titleNode) {
|
||||
if ($node->getLevel() > $titleNode->getLevel()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->endOpenSection($titleNode);
|
||||
}
|
||||
// same level as the last so just close the last open section
|
||||
} elseif ($node->getLevel() === $this->lastTitleNode->getLevel()) {
|
||||
$this->endOpenSection($this->lastTitleNode);
|
||||
}
|
||||
}
|
||||
|
||||
$this->lastTitleNode = $node;
|
||||
|
||||
$this->document->addNode(
|
||||
$this->nodeFactory->createSectionBeginNode($node)
|
||||
);
|
||||
|
||||
$this->openTitleNodes[] = $node;
|
||||
|
||||
break;
|
||||
|
||||
case State::SEPARATOR:
|
||||
$level = $this->environment->getLevel((string) $this->specialLetter);
|
||||
|
||||
$node = $this->nodeFactory->createSeparatorNode($level);
|
||||
|
||||
break;
|
||||
|
||||
case State::CODE:
|
||||
/** @var string[] $buffer */
|
||||
$buffer = $this->buffer->getLines();
|
||||
|
||||
$node = $this->nodeFactory->createCodeNode($buffer);
|
||||
|
||||
break;
|
||||
|
||||
case State::BLOCK:
|
||||
/** @var string[] $lines */
|
||||
$lines = $this->buffer->getLines();
|
||||
|
||||
$node = $this->nodeFactory->createBlockNode($lines);
|
||||
|
||||
// This means we are in an indented area that is not a code block
|
||||
// or definition list.
|
||||
// If we're NOT in a directive, then this must be a blockquote.
|
||||
// If we ARE in a directive, allow the directive to convert
|
||||
// the BlockNode into what it needs
|
||||
if ($this->directive === null) {
|
||||
$document = $this->parser->getSubParser()->parseLocal($node->getValue());
|
||||
|
||||
$node = $this->nodeFactory->createQuoteNode($document);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State::LIST:
|
||||
$list = $this->lineDataParser->parseList(
|
||||
$this->buffer->getLines()
|
||||
);
|
||||
|
||||
$node = $this->nodeFactory->createListNode($list, $list[0]->isOrdered());
|
||||
|
||||
break;
|
||||
|
||||
case State::DEFINITION_LIST:
|
||||
$definitionList = $this->lineDataParser->parseDefinitionList(
|
||||
$this->buffer->getLines()
|
||||
);
|
||||
|
||||
$node = $this->nodeFactory->createDefinitionListNode($definitionList);
|
||||
|
||||
break;
|
||||
|
||||
case State::TABLE:
|
||||
$node = $this->nodeBuffer;
|
||||
assert($node instanceof TableNode);
|
||||
|
||||
$node->finalize($this->parser);
|
||||
|
||||
break;
|
||||
|
||||
case State::NORMAL:
|
||||
$this->isCode = $this->prepareCode();
|
||||
|
||||
$buffer = $this->buffer->getLinesString();
|
||||
|
||||
$node = $this->nodeFactory->createParagraphNode($this->parser->createSpanNode($buffer));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->directive !== null) {
|
||||
$currentDirective = $this->getCurrentDirective();
|
||||
|
||||
if ($currentDirective !== null) {
|
||||
try {
|
||||
$currentDirective->process(
|
||||
$this->parser,
|
||||
$node,
|
||||
$this->directive->getVariable(),
|
||||
$this->directive->getData(),
|
||||
$this->directive->getOptions()
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
$this->environment->getErrorManager()->error(
|
||||
sprintf('Error while processing "%s" directive: "%s"', $currentDirective->getName(), $e->getMessage()),
|
||||
$this->environment->getCurrentFileName(),
|
||||
$this->currentLineNumber ?? null,
|
||||
$e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$node = null;
|
||||
}
|
||||
|
||||
$this->directive = null;
|
||||
|
||||
if ($node !== null) {
|
||||
$this->document->addNode($node);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
private function hasBuffer(): bool
|
||||
{
|
||||
return ! $this->buffer->isEmpty() || $this->nodeBuffer !== null;
|
||||
}
|
||||
|
||||
private function getCurrentDirective(): ?Directive
|
||||
{
|
||||
if ($this->directive === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$name = $this->directive->getName();
|
||||
|
||||
return $this->directives[$name];
|
||||
}
|
||||
|
||||
private function isDirectiveOption(string $line): bool
|
||||
{
|
||||
if ($this->directive === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$directiveOption = $this->lineDataParser->parseDirectiveOption($line);
|
||||
|
||||
if ($directiveOption === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->directive->setOption($directiveOption->getName(), $directiveOption->getValue());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function initDirective(string $line): bool
|
||||
{
|
||||
$parserDirective = $this->lineDataParser->parseDirective($line);
|
||||
|
||||
if ($parserDirective === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! isset($this->directives[$parserDirective->getName()])) {
|
||||
$this->environment->getErrorManager()->error(
|
||||
sprintf('Unknown directive "%s" for line "%s"', $parserDirective->getName(), $line),
|
||||
$this->environment->getCurrentFileName()
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->directive = $parserDirective;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on a NORMAL state line: it's used to determine if this
|
||||
* it beginning a code block - by having a line ending in "::"
|
||||
*/
|
||||
private function prepareCode(): bool
|
||||
{
|
||||
$lastLine = $this->buffer->getLastLine();
|
||||
|
||||
if ($lastLine === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$trimmedLastLine = trim($lastLine);
|
||||
|
||||
if (strlen($trimmedLastLine) >= 2) {
|
||||
if (substr($trimmedLastLine, -2) === '::') {
|
||||
if (trim($trimmedLastLine) === '::') {
|
||||
$this->buffer->pop();
|
||||
} else {
|
||||
$this->buffer->set($this->buffer->count() - 1, substr($trimmedLastLine, 0, -1));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function parseLink(string $line): bool
|
||||
{
|
||||
$link = $this->lineDataParser->parseLink($line);
|
||||
|
||||
if ($link === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($link->getType() === Link::TYPE_ANCHOR) {
|
||||
$anchorNode = $this->nodeFactory
|
||||
->createAnchorNode($link->getName());
|
||||
|
||||
$this->document->addNode($anchorNode);
|
||||
}
|
||||
|
||||
$this->environment->setLink($link->getName(), $link->getUrl());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function endOpenSection(TitleNode $titleNode): void
|
||||
{
|
||||
$this->document->addNode(
|
||||
$this->nodeFactory->createSectionEndNode($titleNode)
|
||||
);
|
||||
|
||||
$key = array_search($titleNode, $this->openTitleNodes, true);
|
||||
|
||||
if ($key === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->openTitleNodes[$key]);
|
||||
}
|
||||
}
|
158
vendor/doctrine/rst-parser/lib/Parser/LineChecker.php
vendored
Normal file
158
vendor/doctrine/rst-parser/lib/Parser/LineChecker.php
vendored
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use function in_array;
|
||||
use function preg_match;
|
||||
use function preg_replace;
|
||||
use function str_repeat;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function trim;
|
||||
|
||||
class LineChecker
|
||||
{
|
||||
private const HEADER_LETTERS = ['=', '-', '~', '*', '+', '^', '"', '.', '`', "'", '_', '#', ':'];
|
||||
|
||||
/**
|
||||
* A regex matching all bullet list markers and a subset of the enumerated list markers.
|
||||
*
|
||||
* @see https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#bullet-lists
|
||||
* @see https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#enumerated-lists
|
||||
*/
|
||||
public const LIST_MARKER = '/
|
||||
^(
|
||||
[-+*\x{2022}\x{2023}\x{2043}] # match bullet list markers: "*", "+", "-", "•", "‣", or "⁃"
|
||||
|(?:[\d#]+\.|[\d#]+\)|\([\d#]+\)) # match arabic (1-9) or auto-enumerated ("#") lists with formats: "1.", "1)", or "(1)"
|
||||
)
|
||||
(?:\s+|$) # capture the spaces between marker and text to determine the list item text offset (or eol, if text starts on a new line)
|
||||
/ux';
|
||||
|
||||
public function isSpecialLine(string $line): ?string
|
||||
{
|
||||
if (strlen($line) < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$letter = $line[0];
|
||||
|
||||
if (! in_array($letter, self::HEADER_LETTERS, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for ($i = 1; $i < strlen($line); $i++) {
|
||||
if ($line[$i] !== $letter) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $letter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this line is the start of a list item.
|
||||
*
|
||||
* @see self::LIST_MARKER
|
||||
*
|
||||
* @param string|null $listMarker if provided, this function only returns "true" if the
|
||||
* same list marker format is used (e.g. all dashes).
|
||||
* @param int|null $listOffset if this line is a list, this will be set to the column
|
||||
* number of the start of the list item content (used to
|
||||
* match multiline items)
|
||||
* @param string|null $nextLine if set, this line must also be a valid list line or
|
||||
* indented content for enumerated lists
|
||||
*/
|
||||
public function isListLine(string $line, ?string &$listMarker = null, ?int &$listOffset = 0, ?string $nextLine = null): bool
|
||||
{
|
||||
$isList = preg_match(self::LIST_MARKER, $line, $m) > 0;
|
||||
if (! $isList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = strlen($m[0]);
|
||||
$normalizedMarker = preg_replace('/\d+/', 'd', $m[1]);
|
||||
if (
|
||||
// validate if next line can be considered part of a list for enumerated lists
|
||||
$normalizedMarker !== $m[1]
|
||||
&& $nextLine !== null
|
||||
&& trim($nextLine) !== ''
|
||||
&& ! $this->isBlockLine($nextLine, $offset)
|
||||
&& ! $this->isListLine($nextLine, $normalizedMarker)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($listMarker !== null) {
|
||||
$isList = $normalizedMarker === $listMarker;
|
||||
}
|
||||
|
||||
if ($isList) {
|
||||
$listOffset = $offset;
|
||||
$listMarker = $normalizedMarker;
|
||||
}
|
||||
|
||||
return $isList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this line "indented"?
|
||||
*
|
||||
* A blank line also counts as a "block" line, as it
|
||||
* may be the empty line between, for example, a
|
||||
* ".. note::" directive and the indented content on the
|
||||
* next lines.
|
||||
*
|
||||
* @param int $minIndent can be used to require a specific level of
|
||||
* indentation for non-blank lines (number of spaces)
|
||||
*/
|
||||
public function isBlockLine(string $line, int $minIndent = 1): bool
|
||||
{
|
||||
return (trim($line) === '' || $this->isIndented($line, $minIndent)) && ! $this->isComment($line);
|
||||
}
|
||||
|
||||
public function isComment(string $line): bool
|
||||
{
|
||||
return preg_match('/^\.\.(?: [^_]((?:(?!::).)*))?$/mUsi', $line) > 0;
|
||||
}
|
||||
|
||||
public function isDirective(string $line): bool
|
||||
{
|
||||
return preg_match('/^\.\. (\|(.+)\| |)([^\s]+)::( (.*)|)$/mUsi', $line) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if line is an indented one.
|
||||
*
|
||||
* This does *not* include blank lines, use {@see isBlockLine()} to check
|
||||
* for blank or indented lines.
|
||||
*
|
||||
* @param int $minIndent can be used to require a specific level of indentation (number of spaces)
|
||||
*/
|
||||
public function isIndented(string $line, int $minIndent = 1): bool
|
||||
{
|
||||
return strpos($line, str_repeat(' ', $minIndent)) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current line can be considered part of the definition list.
|
||||
*
|
||||
* Either the current line, or the next line must be indented to be considered
|
||||
* definition.
|
||||
*
|
||||
* @see https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#definition-lists
|
||||
*/
|
||||
public function isDefinitionListEnded(string $line, string $nextLine): bool
|
||||
{
|
||||
if (trim($line) === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isIndented($line)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! $this->isIndented($nextLine);
|
||||
}
|
||||
}
|
234
vendor/doctrine/rst-parser/lib/Parser/LineDataParser.php
vendored
Normal file
234
vendor/doctrine/rst-parser/lib/Parser/LineDataParser.php
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\RST\Event\OnLinkParsedEvent;
|
||||
use Doctrine\RST\Nodes\ParagraphNode;
|
||||
use Doctrine\RST\Nodes\SpanNode;
|
||||
use Doctrine\RST\Parser;
|
||||
|
||||
use function array_map;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function ltrim;
|
||||
use function mb_strlen;
|
||||
use function preg_match;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use function trim;
|
||||
|
||||
final class LineDataParser
|
||||
{
|
||||
/** @var Parser */
|
||||
private $parser;
|
||||
|
||||
/** @var EventManager */
|
||||
private $eventManager;
|
||||
|
||||
public function __construct(Parser $parser, EventManager $eventManager)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
$this->eventManager = $eventManager;
|
||||
}
|
||||
|
||||
public function parseLink(string $line): ?Link
|
||||
{
|
||||
// Links
|
||||
if (preg_match('/^\.\. _`(.+)`: (.+)$/mUsi', $line, $match) > 0) {
|
||||
return $this->createLink($match[1], $match[2], Link::TYPE_LINK);
|
||||
}
|
||||
|
||||
// anonymous links
|
||||
if (preg_match('/^\.\. _(.+): (.+)$/mUsi', $line, $match) > 0) {
|
||||
return $this->createLink($match[1], $match[2], Link::TYPE_LINK);
|
||||
}
|
||||
|
||||
// Short anonymous links
|
||||
if (preg_match('/^__ (.+)$/mUsi', trim($line), $match) > 0) {
|
||||
$url = $match[1];
|
||||
|
||||
return $this->createLink('_', $url, Link::TYPE_LINK);
|
||||
}
|
||||
|
||||
// Anchor links - ".. _`anchor-link`:"
|
||||
if (preg_match('/^\.\. _`(.+)`:$/mUsi', trim($line), $match) > 0) {
|
||||
$anchor = $match[1];
|
||||
|
||||
return new Link($anchor, '#' . $anchor, Link::TYPE_ANCHOR);
|
||||
}
|
||||
|
||||
if (preg_match('/^\.\. _(.+):$/mUsi', trim($line), $match) > 0) {
|
||||
$anchor = $match[1];
|
||||
|
||||
return $this->createLink($anchor, '#' . $anchor, Link::TYPE_ANCHOR);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createLink(string $name, string $url, string $type): Link
|
||||
{
|
||||
$this->eventManager->dispatchEvent(
|
||||
OnLinkParsedEvent::ON_LINK_PARSED,
|
||||
new OnLinkParsedEvent($url, $type, $this->parser->getEnvironment()->getCurrentFileName())
|
||||
);
|
||||
|
||||
return new Link($name, $url, $type);
|
||||
}
|
||||
|
||||
public function parseDirectiveOption(string $line): ?DirectiveOption
|
||||
{
|
||||
if (preg_match('/^(\s+):(.+): (.*)$/mUsi', $line, $match) > 0) {
|
||||
return new DirectiveOption($match[2], trim($match[3]));
|
||||
}
|
||||
|
||||
if (preg_match('/^(\s+):(.+):(\s*)$/mUsi', $line, $match) > 0) {
|
||||
$value = trim($match[3]);
|
||||
|
||||
return new DirectiveOption($match[2], true);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function parseDirective(string $line): ?Directive
|
||||
{
|
||||
if (preg_match('/^\.\. (\|(.+)\| |)([^\s]+)::( (.*)|)$/mUsi', $line, $match) > 0) {
|
||||
return new Directive(
|
||||
$match[2],
|
||||
$match[3],
|
||||
trim($match[4])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $lines
|
||||
*
|
||||
* @return ListItem[]
|
||||
*/
|
||||
public function parseList(array $lines): array
|
||||
{
|
||||
$list = [];
|
||||
$currentItem = null;
|
||||
$currentPrefix = null;
|
||||
$currentOffset = 0;
|
||||
|
||||
$createListItem = function (string $item, string $prefix): ListItem {
|
||||
// parse any markup in the list item (e.g. sublists, directives)
|
||||
$nodes = $this->parser->getSubParser()->parseLocal($item)->getNodes();
|
||||
if (count($nodes) === 1 && $nodes[0] instanceof ParagraphNode) {
|
||||
// if there is only one paragraph node, the value is put directly in the <li> element
|
||||
$nodes = [$nodes[0]->getValue()];
|
||||
}
|
||||
|
||||
return new ListItem($prefix, mb_strlen($prefix) > 1, $nodes);
|
||||
};
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (preg_match(LineChecker::LIST_MARKER, $line, $m) > 0) {
|
||||
// a list marker indicates the start of a new list item,
|
||||
// complete the previous one and start a new one
|
||||
if ($currentItem !== null) {
|
||||
$list[] = $createListItem($currentItem, $currentPrefix);
|
||||
}
|
||||
|
||||
$currentOffset = strlen($m[0]);
|
||||
$currentPrefix = $m[1];
|
||||
$currentItem = substr($line, $currentOffset) . "\n";
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// the list item offset is determined by the offset of the first text
|
||||
if (trim($currentItem) === '') {
|
||||
$currentOffset = strlen($line) - strlen(ltrim($line));
|
||||
}
|
||||
|
||||
$currentItem .= substr($line, $currentOffset) . "\n";
|
||||
}
|
||||
|
||||
if ($currentItem !== null) {
|
||||
$list[] = $createListItem($currentItem, $currentPrefix);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/** @param string[] $lines */
|
||||
public function parseDefinitionList(array $lines): DefinitionList
|
||||
{
|
||||
/** @var array{term: SpanNode, classifiers: list<SpanNode>, definition: string}|null $definitionListTerm */
|
||||
$definitionListTerm = null;
|
||||
$definitionList = [];
|
||||
|
||||
$createDefinitionTerm = function (array $definitionListTerm): DefinitionListTerm {
|
||||
// parse any markup in the definition (e.g. lists, directives)
|
||||
$definitionNodes = $this->parser->getSubParser()->parseLocal($definitionListTerm['definition'])->getNodes();
|
||||
if (count($definitionNodes) === 1 && $definitionNodes[0] instanceof ParagraphNode) {
|
||||
// if there is only one paragraph node, the value is put directly in the <dd> element
|
||||
$definitionNodes = [$definitionNodes[0]->getValue()];
|
||||
} else {
|
||||
// otherwise, .first and .last are added to the first and last nodes of the definition
|
||||
$definitionNodes[0]->setClasses($definitionNodes[0]->getClasses() + ['first']);
|
||||
$definitionNodes[count($definitionNodes) - 1]->setClasses($definitionNodes[count($definitionNodes) - 1]->getClasses() + ['last']);
|
||||
}
|
||||
|
||||
return new DefinitionListTerm(
|
||||
$definitionListTerm['term'],
|
||||
$definitionListTerm['classifiers'],
|
||||
$definitionNodes
|
||||
);
|
||||
};
|
||||
|
||||
$currentOffset = 0;
|
||||
foreach ($lines as $key => $line) {
|
||||
// indent or empty line = term definition line
|
||||
if ($definitionListTerm !== null && (trim($line) === '') || $line[0] === ' ') {
|
||||
if ($currentOffset === 0) {
|
||||
// first line of a definition determines the indentation offset
|
||||
$definition = ltrim($line);
|
||||
$currentOffset = strlen($line) - strlen($definition);
|
||||
} else {
|
||||
$definition = substr($line, $currentOffset);
|
||||
}
|
||||
|
||||
$definitionListTerm['definition'] .= $definition . "\n";
|
||||
|
||||
// non empty string at the start of the line = definition term
|
||||
} elseif (trim($line) !== '') {
|
||||
// we are starting a new term so if we have an existing
|
||||
// term with definitions, add it to the definition list
|
||||
if ($definitionListTerm !== null) {
|
||||
$definitionList[] = $createDefinitionTerm($definitionListTerm);
|
||||
}
|
||||
|
||||
$parts = explode(' : ', trim($line));
|
||||
$term = array_shift($parts);
|
||||
$classifiers = array_map(function (string $classifier): SpanNode {
|
||||
return $this->parser->createSpanNode($classifier);
|
||||
}, array_map('trim', $parts));
|
||||
|
||||
$currentOffset = 0;
|
||||
$definitionListTerm = [
|
||||
'term' => $this->parser->createSpanNode($term),
|
||||
'classifiers' => $classifiers,
|
||||
'definition' => '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// append the last definition of the list
|
||||
if ($definitionListTerm !== null) {
|
||||
$definitionList[] = $createDefinitionTerm($definitionListTerm);
|
||||
}
|
||||
|
||||
return new DefinitionList($definitionList);
|
||||
}
|
||||
}
|
58
vendor/doctrine/rst-parser/lib/Parser/Lines.php
vendored
Normal file
58
vendor/doctrine/rst-parser/lib/Parser/Lines.php
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Iterator;
|
||||
|
||||
/** @template-implements Iterator<array-key, string> */
|
||||
final class Lines implements Iterator
|
||||
{
|
||||
/** @var string[] */
|
||||
private $lines = [];
|
||||
|
||||
/** @var int */
|
||||
private $position = 0;
|
||||
|
||||
/** @param string[] $lines */
|
||||
public function __construct(array $lines)
|
||||
{
|
||||
$this->lines = $lines;
|
||||
}
|
||||
|
||||
public function getPreviousLine(): string
|
||||
{
|
||||
return $this->lines[$this->position - 1] ?? '';
|
||||
}
|
||||
|
||||
public function getNextLine(): string
|
||||
{
|
||||
return $this->lines[$this->position + 1] ?? '';
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
public function current(): string
|
||||
{
|
||||
return $this->lines[$this->position];
|
||||
}
|
||||
|
||||
public function key(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
++$this->position;
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return isset($this->lines[$this->position]);
|
||||
}
|
||||
}
|
42
vendor/doctrine/rst-parser/lib/Parser/Link.php
vendored
Normal file
42
vendor/doctrine/rst-parser/lib/Parser/Link.php
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
final class Link
|
||||
{
|
||||
public const TYPE_LINK = 'link';
|
||||
public const TYPE_ANCHOR = 'anchor';
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var string */
|
||||
private $url;
|
||||
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
public function __construct(string $name, string $url, string $type)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->url = $url;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
56
vendor/doctrine/rst-parser/lib/Parser/ListItem.php
vendored
Normal file
56
vendor/doctrine/rst-parser/lib/Parser/ListItem.php
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\RST\Nodes\Node;
|
||||
|
||||
use function array_reduce;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* Represents a single item of a bullet or enumerated list.
|
||||
*/
|
||||
final class ListItem
|
||||
{
|
||||
/** @var string the list marker used for this item */
|
||||
private $prefix;
|
||||
|
||||
/** @var bool whether the list marker represents an enumerated list */
|
||||
private $ordered;
|
||||
|
||||
/** @var Node[] */
|
||||
private $contents;
|
||||
|
||||
/** @param Node[] $contents */
|
||||
public function __construct(string $prefix, bool $ordered, array $contents)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
$this->ordered = $ordered;
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
public function getPrefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
public function isOrdered(): bool
|
||||
{
|
||||
return $this->ordered;
|
||||
}
|
||||
|
||||
/** @return Node[] */
|
||||
public function getContents(): array
|
||||
{
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
public function getContentsAsString(): string
|
||||
{
|
||||
return trim(array_reduce($this->contents, static function (string $contents, Node $node): string {
|
||||
return $contents . $node->render() . "\n";
|
||||
}, ''));
|
||||
}
|
||||
}
|
34
vendor/doctrine/rst-parser/lib/Parser/State.php
vendored
Normal file
34
vendor/doctrine/rst-parser/lib/Parser/State.php
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
/**
|
||||
* "States" for DocumentParser as it parses line-by-line.
|
||||
*/
|
||||
final class State
|
||||
{
|
||||
/**
|
||||
* There is currently no state: the next line will begin a new state
|
||||
*/
|
||||
public const BEGIN = 'begin';
|
||||
|
||||
/**
|
||||
* Normal, non-indented, non-table lines
|
||||
*/
|
||||
public const NORMAL = 'normal';
|
||||
public const DIRECTIVE = 'directive';
|
||||
|
||||
/**
|
||||
* Indented lines
|
||||
*/
|
||||
public const BLOCK = 'block';
|
||||
public const TITLE = 'title';
|
||||
public const LIST = 'list';
|
||||
public const SEPARATOR = 'separator';
|
||||
public const CODE = 'code';
|
||||
public const TABLE = 'table';
|
||||
public const COMMENT = 'comment';
|
||||
public const DEFINITION_LIST = 'definition_list';
|
||||
}
|
162
vendor/doctrine/rst-parser/lib/Parser/TableParser.php
vendored
Normal file
162
vendor/doctrine/rst-parser/lib/Parser/TableParser.php
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\RST\Nodes\TableNode;
|
||||
use Exception;
|
||||
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function trim;
|
||||
|
||||
final class TableParser
|
||||
{
|
||||
private const SIMPLE_TABLE_LETTER = '=';
|
||||
|
||||
// "-" is valid as a separator in a simple table, except
|
||||
// on the first and last lines
|
||||
private const SIMPLE_TABLE_LETTER_ALT = '-';
|
||||
|
||||
private const PRETTY_TABLE_LETTER = '-';
|
||||
|
||||
private const PRETTY_TABLE_HEADER = '=';
|
||||
|
||||
private const PRETTY_TABLE_JOINT = '+';
|
||||
|
||||
/**
|
||||
* Parses a line from a table to see if it is a separator line.
|
||||
*
|
||||
* Returns TableSeparatorLineConfig if it *is* a separator, null otherwise.
|
||||
*/
|
||||
public function parseTableSeparatorLine(string $line): ?TableSeparatorLineConfig
|
||||
{
|
||||
$header = false;
|
||||
$pretty = false;
|
||||
$line = trim($line);
|
||||
|
||||
if ($line === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Finds the table chars
|
||||
$chars = $this->findTableChars($line);
|
||||
|
||||
if ($chars === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($chars[0] === self::PRETTY_TABLE_JOINT && $chars[1] === self::PRETTY_TABLE_LETTER) {
|
||||
$pretty = true;
|
||||
// reverse the chars: - is the line char, + is the space char
|
||||
$chars = [self::PRETTY_TABLE_LETTER, self::PRETTY_TABLE_JOINT];
|
||||
} elseif ($chars[0] === self::PRETTY_TABLE_JOINT && $chars[1] === self::PRETTY_TABLE_HEADER) {
|
||||
$pretty = true;
|
||||
$header = true;
|
||||
// reverse the chars: = is the line char, + is the space char
|
||||
$chars = [self::PRETTY_TABLE_HEADER, self::PRETTY_TABLE_JOINT];
|
||||
} else {
|
||||
// either a simple table or not a separator line
|
||||
|
||||
// if line char is not "=" or "-", not a separator line
|
||||
if (! in_array($chars[0], [self::SIMPLE_TABLE_LETTER, self::SIMPLE_TABLE_LETTER_ALT], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if space char is not a space, not a separator line
|
||||
if ($chars[1] !== ' ') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
$currentPartStart = null;
|
||||
|
||||
for ($i = 0; $i < strlen($line); $i++) {
|
||||
// we found the "line char": "-" or "="
|
||||
if ($line[$i] === $chars[0]) {
|
||||
if ($currentPartStart === null) {
|
||||
$currentPartStart = $i;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($line[$i] !== $chars[1]) {
|
||||
throw new Exception(sprintf('Unexpected char "%s"', $line[$i]));
|
||||
}
|
||||
|
||||
// found the "space" char
|
||||
// record the part "range" if we're at the end of a range
|
||||
if ($currentPartStart === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts[] = [$currentPartStart, $i];
|
||||
$currentPartStart = null;
|
||||
}
|
||||
|
||||
// finish the last "part"
|
||||
if ($currentPartStart !== null) {
|
||||
$parts[] = [$currentPartStart, $i];
|
||||
}
|
||||
|
||||
if (count($parts) > 1) {
|
||||
return new TableSeparatorLineConfig(
|
||||
$header,
|
||||
$pretty ? TableNode::TYPE_PRETTY : TableNode::TYPE_SIMPLE,
|
||||
$parts,
|
||||
$chars[0],
|
||||
$line
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function guessTableType(string $line): string
|
||||
{
|
||||
return $line[0] === self::SIMPLE_TABLE_LETTER ? TableNode::TYPE_SIMPLE : TableNode::TYPE_PRETTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* A "line" separator always has only two characters.
|
||||
* This method returns those two characters.
|
||||
*
|
||||
* This returns null if this is not a separator line
|
||||
* or it's malformed in any way.
|
||||
*
|
||||
* @return string[]|null
|
||||
* @psalm-return array{string, ?string}
|
||||
*/
|
||||
private function findTableChars(string $line): ?array
|
||||
{
|
||||
$lineChar = $line[0];
|
||||
$spaceChar = null;
|
||||
|
||||
for ($i = 0; $i < strlen($line); $i++) {
|
||||
if ($line[$i] === $lineChar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($spaceChar === null) {
|
||||
$spaceChar = $line[$i];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($line[$i] !== $spaceChar) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($spaceChar === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [$lineChar, $spaceChar];
|
||||
}
|
||||
}
|
87
vendor/doctrine/rst-parser/lib/Parser/TableSeparatorLineConfig.php
vendored
Normal file
87
vendor/doctrine/rst-parser/lib/Parser/TableSeparatorLineConfig.php
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\RST\Parser;
|
||||
|
||||
use Doctrine\RST\Nodes\TableNode;
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
|
||||
final class TableSeparatorLineConfig
|
||||
{
|
||||
/** @var bool */
|
||||
private $isHeader;
|
||||
|
||||
/** @var string */
|
||||
private $tableType;
|
||||
|
||||
/** @var int[][] */
|
||||
private $partRanges;
|
||||
|
||||
/** @var string */
|
||||
private $lineCharacter;
|
||||
|
||||
/** @var string */
|
||||
private $rawContent;
|
||||
|
||||
/** @param int[][] $partRanges */
|
||||
public function __construct(bool $isHeader, string $tableType, array $partRanges, string $lineCharacter, string $rawContent)
|
||||
{
|
||||
if (! in_array($tableType, [TableNode::TYPE_SIMPLE, TableNode::TYPE_PRETTY], true)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid table type'));
|
||||
}
|
||||
|
||||
if (! in_array($lineCharacter, ['=', '-'], true)) {
|
||||
throw new InvalidArgumentException(sprintf('Unexpected line character "%s"', $lineCharacter));
|
||||
}
|
||||
|
||||
$this->isHeader = $isHeader;
|
||||
$this->tableType = $tableType;
|
||||
$this->partRanges = $partRanges;
|
||||
$this->lineCharacter = $lineCharacter;
|
||||
$this->rawContent = $rawContent;
|
||||
}
|
||||
|
||||
public function isHeader(): bool
|
||||
{
|
||||
return $this->isHeader;
|
||||
}
|
||||
|
||||
public function isSimpleTableType(): bool
|
||||
{
|
||||
return $this->tableType === TableNode::TYPE_SIMPLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of position "ranges" where content should exist.
|
||||
*
|
||||
* For example:
|
||||
* === ===== === ===
|
||||
*
|
||||
* Would yield:
|
||||
* [[0, 3], [6, 11], [14, 17], [18, 21]]
|
||||
*
|
||||
* @return int[][]
|
||||
*/
|
||||
public function getPartRanges(): array
|
||||
{
|
||||
return $this->partRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "line" character used in the separator,
|
||||
* either - or =
|
||||
*/
|
||||
public function getLineCharacter(): string
|
||||
{
|
||||
return $this->lineCharacter;
|
||||
}
|
||||
|
||||
public function getRawContent(): string
|
||||
{
|
||||
return $this->rawContent;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue