chore: synchronize project dependencies and update vendor directory files
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark;
|
||||
|
||||
use League\CommonMark\Environment\EnvironmentBuilderInterface;
|
||||
use League\CommonMark\Extension\CommonMark\Delimiter\Processor\EmphasisDelimiterProcessor;
|
||||
use League\CommonMark\Extension\ConfigurableExtensionInterface;
|
||||
use League\CommonMark\Node as CoreNode;
|
||||
use League\CommonMark\Parser as CoreParser;
|
||||
use League\CommonMark\Renderer as CoreRenderer;
|
||||
use League\Config\ConfigurationBuilderInterface;
|
||||
use Nette\Schema\Expect;
|
||||
|
||||
final class CommonMarkCoreExtension implements ConfigurableExtensionInterface
|
||||
{
|
||||
public function configureSchema(ConfigurationBuilderInterface $builder): void
|
||||
{
|
||||
$builder->addSchema('commonmark', Expect::structure([
|
||||
'use_asterisk' => Expect::bool(true),
|
||||
'use_underscore' => Expect::bool(true),
|
||||
'enable_strong' => Expect::bool(true),
|
||||
'enable_em' => Expect::bool(true),
|
||||
'unordered_list_markers' => Expect::listOf('string')->min(1)->default(['*', '+', '-'])->mergeDefaults(false),
|
||||
]));
|
||||
}
|
||||
|
||||
// phpcs:disable Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma,Squiz.WhiteSpace.SemicolonSpacing.Incorrect
|
||||
public function register(EnvironmentBuilderInterface $environment): void
|
||||
{
|
||||
$environment
|
||||
->addBlockStartParser(new Parser\Block\BlockQuoteStartParser(), 70)
|
||||
->addBlockStartParser(new Parser\Block\HeadingStartParser(), 60)
|
||||
->addBlockStartParser(new Parser\Block\FencedCodeStartParser(), 50)
|
||||
->addBlockStartParser(new Parser\Block\HtmlBlockStartParser(), 40)
|
||||
->addBlockStartParser(new Parser\Block\ThematicBreakStartParser(), 20)
|
||||
->addBlockStartParser(new Parser\Block\ListBlockStartParser(), 10)
|
||||
->addBlockStartParser(new Parser\Block\IndentedCodeStartParser(), -100)
|
||||
|
||||
->addInlineParser(new CoreParser\Inline\NewlineParser(), 200)
|
||||
->addInlineParser(new Parser\Inline\BacktickParser(), 150)
|
||||
->addInlineParser(new Parser\Inline\EscapableParser(), 80)
|
||||
->addInlineParser(new Parser\Inline\EntityParser(), 70)
|
||||
->addInlineParser(new Parser\Inline\AutolinkParser(), 50)
|
||||
->addInlineParser(new Parser\Inline\HtmlInlineParser(), 40)
|
||||
->addInlineParser(new Parser\Inline\CloseBracketParser(), 30)
|
||||
->addInlineParser(new Parser\Inline\OpenBracketParser(), 20)
|
||||
->addInlineParser(new Parser\Inline\BangParser(), 10)
|
||||
|
||||
->addRenderer(Node\Block\BlockQuote::class, new Renderer\Block\BlockQuoteRenderer(), 0)
|
||||
->addRenderer(CoreNode\Block\Document::class, new CoreRenderer\Block\DocumentRenderer(), 0)
|
||||
->addRenderer(Node\Block\FencedCode::class, new Renderer\Block\FencedCodeRenderer(), 0)
|
||||
->addRenderer(Node\Block\Heading::class, new Renderer\Block\HeadingRenderer(), 0)
|
||||
->addRenderer(Node\Block\HtmlBlock::class, new Renderer\Block\HtmlBlockRenderer(), 0)
|
||||
->addRenderer(Node\Block\IndentedCode::class, new Renderer\Block\IndentedCodeRenderer(), 0)
|
||||
->addRenderer(Node\Block\ListBlock::class, new Renderer\Block\ListBlockRenderer(), 0)
|
||||
->addRenderer(Node\Block\ListItem::class, new Renderer\Block\ListItemRenderer(), 0)
|
||||
->addRenderer(CoreNode\Block\Paragraph::class, new CoreRenderer\Block\ParagraphRenderer(), 0)
|
||||
->addRenderer(Node\Block\ThematicBreak::class, new Renderer\Block\ThematicBreakRenderer(), 0)
|
||||
|
||||
->addRenderer(Node\Inline\Code::class, new Renderer\Inline\CodeRenderer(), 0)
|
||||
->addRenderer(Node\Inline\Emphasis::class, new Renderer\Inline\EmphasisRenderer(), 0)
|
||||
->addRenderer(Node\Inline\HtmlInline::class, new Renderer\Inline\HtmlInlineRenderer(), 0)
|
||||
->addRenderer(Node\Inline\Image::class, new Renderer\Inline\ImageRenderer(), 0)
|
||||
->addRenderer(Node\Inline\Link::class, new Renderer\Inline\LinkRenderer(), 0)
|
||||
->addRenderer(CoreNode\Inline\Newline::class, new CoreRenderer\Inline\NewlineRenderer(), 0)
|
||||
->addRenderer(Node\Inline\Strong::class, new Renderer\Inline\StrongRenderer(), 0)
|
||||
->addRenderer(CoreNode\Inline\Text::class, new CoreRenderer\Inline\TextRenderer(), 0)
|
||||
;
|
||||
|
||||
if ($environment->getConfiguration()->get('commonmark/use_asterisk')) {
|
||||
$environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('*'));
|
||||
}
|
||||
|
||||
if ($environment->getConfiguration()->get('commonmark/use_underscore')) {
|
||||
$environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('_'));
|
||||
}
|
||||
}
|
||||
}
|
||||
vendor/league/commonmark/src/Extension/CommonMark/Delimiter/Processor/EmphasisDelimiterProcessor.php
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
|
||||
* - (c) Atlassian Pty Ltd
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Delimiter\Processor;
|
||||
|
||||
use League\CommonMark\Delimiter\DelimiterInterface;
|
||||
use League\CommonMark\Delimiter\Processor\CacheableDelimiterProcessorInterface;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Strong;
|
||||
use League\CommonMark\Node\Inline\AbstractStringContainer;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class EmphasisDelimiterProcessor implements CacheableDelimiterProcessorInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private string $char;
|
||||
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ConfigurationInterface $config;
|
||||
|
||||
/**
|
||||
* @param string $char The emphasis character to use (typically '*' or '_')
|
||||
*/
|
||||
public function __construct(string $char)
|
||||
{
|
||||
$this->char = $char;
|
||||
}
|
||||
|
||||
public function getOpeningCharacter(): string
|
||||
{
|
||||
return $this->char;
|
||||
}
|
||||
|
||||
public function getClosingCharacter(): string
|
||||
{
|
||||
return $this->char;
|
||||
}
|
||||
|
||||
public function getMinLength(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
|
||||
{
|
||||
// "Multiple of 3" rule for internal delimiter runs
|
||||
if (($opener->canClose() || $closer->canOpen()) && $closer->getOriginalLength() % 3 !== 0 && ($opener->getOriginalLength() + $closer->getOriginalLength()) % 3 === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate actual number of delimiters used from this closer
|
||||
if ($opener->getLength() >= 2 && $closer->getLength() >= 2) {
|
||||
if ($this->config->get('commonmark/enable_strong')) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->config->get('commonmark/enable_em')) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
|
||||
{
|
||||
if ($delimiterUse === 1) {
|
||||
$emphasis = new Emphasis($this->char);
|
||||
} elseif ($delimiterUse === 2) {
|
||||
$emphasis = new Strong($this->char . $this->char);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$next = $opener->next();
|
||||
while ($next !== null && $next !== $closer) {
|
||||
$tmp = $next->next();
|
||||
$emphasis->appendChild($next);
|
||||
$next = $tmp;
|
||||
}
|
||||
|
||||
$opener->insertAfter($emphasis);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function getCacheKey(DelimiterInterface $closer): string
|
||||
{
|
||||
return \sprintf(
|
||||
'%s-%s-%d-%d',
|
||||
$this->char,
|
||||
$closer->canOpen() ? 'canOpen' : 'cannotOpen',
|
||||
$closer->getOriginalLength() % 3,
|
||||
$closer->getLength(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
|
||||
class BlockQuote extends AbstractBlock
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Node\StringContainerInterface;
|
||||
|
||||
final class FencedCode extends AbstractBlock implements StringContainerInterface
|
||||
{
|
||||
private ?string $info = null;
|
||||
|
||||
private string $literal = '';
|
||||
|
||||
private int $length;
|
||||
|
||||
private string $char;
|
||||
|
||||
private int $offset;
|
||||
|
||||
public function __construct(int $length, string $char, int $offset)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->length = $length;
|
||||
$this->char = $char;
|
||||
$this->offset = $offset;
|
||||
}
|
||||
|
||||
public function getInfo(): ?string
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getInfoWords(): array
|
||||
{
|
||||
return \preg_split('/\s+/', $this->info ?? '') ?: [];
|
||||
}
|
||||
|
||||
public function setInfo(string $info): void
|
||||
{
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
public function getLiteral(): string
|
||||
{
|
||||
return $this->literal;
|
||||
}
|
||||
|
||||
public function setLiteral(string $literal): void
|
||||
{
|
||||
$this->literal = $literal;
|
||||
}
|
||||
|
||||
public function getChar(): string
|
||||
{
|
||||
return $this->char;
|
||||
}
|
||||
|
||||
public function setChar(string $char): void
|
||||
{
|
||||
$this->char = $char;
|
||||
}
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return $this->length;
|
||||
}
|
||||
|
||||
public function setLength(int $length): void
|
||||
{
|
||||
$this->length = $length;
|
||||
}
|
||||
|
||||
public function getOffset(): int
|
||||
{
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
public function setOffset(int $offset): void
|
||||
{
|
||||
$this->offset = $offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
|
||||
final class Heading extends AbstractBlock
|
||||
{
|
||||
private int $level;
|
||||
|
||||
public function __construct(int $level)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
public function getLevel(): int
|
||||
{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
public function setLevel(int $level): void
|
||||
{
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Node\RawMarkupContainerInterface;
|
||||
|
||||
final class HtmlBlock extends AbstractBlock implements RawMarkupContainerInterface
|
||||
{
|
||||
// Any changes to these constants should be reflected in .phpstorm.meta.php
|
||||
public const TYPE_1_CODE_CONTAINER = 1;
|
||||
public const TYPE_2_COMMENT = 2;
|
||||
public const TYPE_3 = 3;
|
||||
public const TYPE_4 = 4;
|
||||
public const TYPE_5_CDATA = 5;
|
||||
public const TYPE_6_BLOCK_ELEMENT = 6;
|
||||
public const TYPE_7_MISC_ELEMENT = 7;
|
||||
|
||||
/**
|
||||
* @psalm-var self::TYPE_* $type
|
||||
* @phpstan-var self::TYPE_* $type
|
||||
*/
|
||||
private int $type;
|
||||
|
||||
private string $literal = '';
|
||||
|
||||
/**
|
||||
* @psalm-param self::TYPE_* $type
|
||||
*
|
||||
* @phpstan-param self::TYPE_* $type
|
||||
*/
|
||||
public function __construct(int $type)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return self::TYPE_*
|
||||
*
|
||||
* @phpstan-return self::TYPE_*
|
||||
*/
|
||||
public function getType(): int
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param self::TYPE_* $type
|
||||
*
|
||||
* @phpstan-param self::TYPE_* $type
|
||||
*/
|
||||
public function setType(int $type): void
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getLiteral(): string
|
||||
{
|
||||
return $this->literal;
|
||||
}
|
||||
|
||||
public function setLiteral(string $literal): void
|
||||
{
|
||||
$this->literal = $literal;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Node\StringContainerInterface;
|
||||
|
||||
final class IndentedCode extends AbstractBlock implements StringContainerInterface
|
||||
{
|
||||
private string $literal = '';
|
||||
|
||||
public function getLiteral(): string
|
||||
{
|
||||
return $this->literal;
|
||||
}
|
||||
|
||||
public function setLiteral(string $literal): void
|
||||
{
|
||||
$this->literal = $literal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Node\Block\TightBlockInterface;
|
||||
|
||||
class ListBlock extends AbstractBlock implements TightBlockInterface
|
||||
{
|
||||
public const TYPE_BULLET = 'bullet';
|
||||
public const TYPE_ORDERED = 'ordered';
|
||||
|
||||
public const DELIM_PERIOD = 'period';
|
||||
public const DELIM_PAREN = 'paren';
|
||||
|
||||
protected bool $tight = false; // TODO Make lists tight by default in v3
|
||||
|
||||
/** @psalm-readonly */
|
||||
protected ListData $listData;
|
||||
|
||||
public function __construct(ListData $listData)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->listData = $listData;
|
||||
}
|
||||
|
||||
public function getListData(): ListData
|
||||
{
|
||||
return $this->listData;
|
||||
}
|
||||
|
||||
public function isTight(): bool
|
||||
{
|
||||
return $this->tight;
|
||||
}
|
||||
|
||||
public function setTight(bool $tight): void
|
||||
{
|
||||
$this->tight = $tight;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
class ListData
|
||||
{
|
||||
public ?int $start = null;
|
||||
|
||||
public int $padding = 0;
|
||||
|
||||
/**
|
||||
* @psalm-var ListBlock::TYPE_*
|
||||
* @phpstan-var ListBlock::TYPE_*
|
||||
*/
|
||||
public string $type;
|
||||
|
||||
/**
|
||||
* @psalm-var ListBlock::DELIM_*|null
|
||||
* @phpstan-var ListBlock::DELIM_*|null
|
||||
*/
|
||||
public ?string $delimiter = null;
|
||||
|
||||
public ?string $bulletChar = null;
|
||||
|
||||
public int $markerOffset;
|
||||
|
||||
public function equals(ListData $data): bool
|
||||
{
|
||||
return $this->type === $data->type &&
|
||||
$this->delimiter === $data->delimiter &&
|
||||
$this->bulletChar === $data->bulletChar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
|
||||
class ListItem extends AbstractBlock
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
protected ListData $listData;
|
||||
|
||||
public function __construct(ListData $listData)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->listData = $listData;
|
||||
}
|
||||
|
||||
public function getListData(): ListData
|
||||
{
|
||||
return $this->listData;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
|
||||
class ThematicBreak extends AbstractBlock
|
||||
{
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\AbstractInline;
|
||||
|
||||
abstract class AbstractWebResource extends AbstractInline
|
||||
{
|
||||
protected string $url;
|
||||
|
||||
public function __construct(string $url)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl(string $url): void
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\AbstractStringContainer;
|
||||
|
||||
class Code extends AbstractStringContainer
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\AbstractInline;
|
||||
use League\CommonMark\Node\Inline\DelimitedInterface;
|
||||
|
||||
final class Emphasis extends AbstractInline implements DelimitedInterface
|
||||
{
|
||||
private string $delimiter;
|
||||
|
||||
public function __construct(string $delimiter = '_')
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
public function getOpeningDelimiter(): string
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
public function getClosingDelimiter(): string
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\AbstractStringContainer;
|
||||
use League\CommonMark\Node\RawMarkupContainerInterface;
|
||||
|
||||
final class HtmlInline extends AbstractStringContainer implements RawMarkupContainerInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
|
||||
class Image extends AbstractWebResource
|
||||
{
|
||||
protected ?string $title = null;
|
||||
|
||||
public function __construct(string $url, ?string $label = null, ?string $title = null)
|
||||
{
|
||||
parent::__construct($url);
|
||||
|
||||
if ($label !== null && $label !== '') {
|
||||
$this->appendChild(new Text($label));
|
||||
}
|
||||
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
if ($this->title === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(?string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
|
||||
class Link extends AbstractWebResource
|
||||
{
|
||||
protected ?string $title = null;
|
||||
|
||||
public function __construct(string $url, ?string $label = null, ?string $title = null)
|
||||
{
|
||||
parent::__construct($url);
|
||||
|
||||
if ($label !== null && $label !== '') {
|
||||
$this->appendChild(new Text($label));
|
||||
}
|
||||
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
if ($this->title === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(?string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Node\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\AbstractInline;
|
||||
use League\CommonMark\Node\Inline\DelimitedInterface;
|
||||
|
||||
final class Strong extends AbstractInline implements DelimitedInterface
|
||||
{
|
||||
private string $delimiter;
|
||||
|
||||
public function __construct(string $delimiter = '**')
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
|
||||
public function getOpeningDelimiter(): string
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
public function getClosingDelimiter(): string
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote;
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
|
||||
final class BlockQuoteParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private BlockQuote $block;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->block = new BlockQuote();
|
||||
}
|
||||
|
||||
public function getBlock(): BlockQuote
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function isContainer(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canContain(AbstractBlock $childBlock): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === '>') {
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
$cursor->advanceBy(1);
|
||||
$cursor->advanceBySpaceOrTab();
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
return BlockContinue::none();
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
|
||||
final class BlockQuoteStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented()) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
if ($cursor->getNextNonSpaceCharacter() !== '>') {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
$cursor->advanceBy(1);
|
||||
$cursor->advanceBySpaceOrTab();
|
||||
|
||||
return BlockStart::of(new BlockQuoteParser())->at($cursor);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Util\ArrayCollection;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class FencedCodeParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private FencedCode $block;
|
||||
|
||||
/** @var ArrayCollection<string> */
|
||||
private ArrayCollection $strings;
|
||||
|
||||
public function __construct(int $fenceLength, string $fenceChar, int $fenceOffset)
|
||||
{
|
||||
$this->block = new FencedCode($fenceLength, $fenceChar, $fenceOffset);
|
||||
$this->strings = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getBlock(): FencedCode
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
// Check for closing code fence
|
||||
if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === $this->block->getChar()) {
|
||||
$match = RegexHelper::matchFirst('/^(?:`{3,}|~{3,})(?=[ \t]*$)/', $cursor->getLine(), $cursor->getNextNonSpacePosition());
|
||||
if ($match !== null && \strlen($match[0]) >= $this->block->getLength()) {
|
||||
// closing fence - we're at end of line, so we can finalize now
|
||||
return BlockContinue::finished();
|
||||
}
|
||||
}
|
||||
|
||||
// Skip optional spaces of fence offset
|
||||
// Optimization: don't attempt to match if we're at a non-space position
|
||||
if ($cursor->getNextNonSpacePosition() > $cursor->getPosition()) {
|
||||
$cursor->match('/^ {0,' . $this->block->getOffset() . '}/');
|
||||
}
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
public function addLine(string $line): void
|
||||
{
|
||||
$this->strings[] = $line;
|
||||
}
|
||||
|
||||
public function closeBlock(): void
|
||||
{
|
||||
// first line becomes info string
|
||||
$firstLine = $this->strings->first();
|
||||
if ($firstLine === false) {
|
||||
$firstLine = '';
|
||||
}
|
||||
|
||||
$this->block->setInfo(RegexHelper::unescape(\trim($firstLine)));
|
||||
|
||||
if ($this->strings->count() === 1) {
|
||||
$this->block->setLiteral('');
|
||||
} else {
|
||||
$this->block->setLiteral(\implode("\n", $this->strings->slice(1)) . "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
|
||||
final class FencedCodeStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented() || ! \in_array($cursor->getNextNonSpaceCharacter(), ['`', '~'], true)) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$indent = $cursor->getIndent();
|
||||
$fence = $cursor->match('/^[ \t]*(?:`{3,}(?!.*`)|~{3,})/');
|
||||
if ($fence === null) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
// fenced code block
|
||||
$fence = \ltrim($fence, " \t");
|
||||
|
||||
return BlockStart::of(new FencedCodeParser(\strlen($fence), $fence[0], $indent))->at($cursor);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\InlineParserEngineInterface;
|
||||
|
||||
final class HeadingParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private Heading $block;
|
||||
|
||||
private string $content;
|
||||
|
||||
public function __construct(int $level, string $content)
|
||||
{
|
||||
$this->block = new Heading($level);
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getBlock(): Heading
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
public function parseInlines(InlineParserEngineInterface $inlineParser): void
|
||||
{
|
||||
$inlineParser->parse($this->content, $this->block);
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
class HeadingStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented() || ! \in_array($cursor->getNextNonSpaceCharacter(), ['#', '-', '='], true)) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
|
||||
if ($atxHeading = self::getAtxHeader($cursor)) {
|
||||
return BlockStart::of($atxHeading)->at($cursor);
|
||||
}
|
||||
|
||||
$setextHeadingLevel = self::getSetextHeadingLevel($cursor);
|
||||
if ($setextHeadingLevel > 0) {
|
||||
$content = $parserState->getParagraphContent();
|
||||
if ($content !== null) {
|
||||
$cursor->advanceToEnd();
|
||||
|
||||
return BlockStart::of(new HeadingParser($setextHeadingLevel, $content))
|
||||
->at($cursor)
|
||||
->replaceActiveBlockParser();
|
||||
}
|
||||
}
|
||||
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
private static function getAtxHeader(Cursor $cursor): ?HeadingParser
|
||||
{
|
||||
$match = RegexHelper::matchFirst('/^#{1,6}(?:[ \t]+|$)/', $cursor->getRemainder());
|
||||
if (! $match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
$cursor->advanceBy(\strlen($match[0]));
|
||||
|
||||
$level = \strlen(\trim($match[0]));
|
||||
$str = $cursor->getRemainder();
|
||||
$str = \preg_replace('/^[ \t]*#+[ \t]*$/', '', $str);
|
||||
\assert(\is_string($str));
|
||||
$str = \preg_replace('/[ \t]+#+[ \t]*$/', '', $str);
|
||||
\assert(\is_string($str));
|
||||
|
||||
return new HeadingParser($level, $str);
|
||||
}
|
||||
|
||||
private static function getSetextHeadingLevel(Cursor $cursor): int
|
||||
{
|
||||
$match = RegexHelper::matchFirst('/^(?:=+|-+)[ \t]*$/', $cursor->getRemainder());
|
||||
if ($match === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $match[0][0] === '=' ? 1 : 2;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class HtmlBlockParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private HtmlBlock $block;
|
||||
|
||||
private string $content = '';
|
||||
|
||||
private bool $finished = false;
|
||||
|
||||
/**
|
||||
* @psalm-param HtmlBlock::TYPE_* $blockType
|
||||
*
|
||||
* @phpstan-param HtmlBlock::TYPE_* $blockType
|
||||
*/
|
||||
public function __construct(int $blockType)
|
||||
{
|
||||
$this->block = new HtmlBlock($blockType);
|
||||
}
|
||||
|
||||
public function getBlock(): HtmlBlock
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
if ($this->finished) {
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
if ($cursor->isBlank() && \in_array($this->block->getType(), [HtmlBlock::TYPE_6_BLOCK_ELEMENT, HtmlBlock::TYPE_7_MISC_ELEMENT], true)) {
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
public function addLine(string $line): void
|
||||
{
|
||||
if ($this->content !== '') {
|
||||
$this->content .= "\n";
|
||||
}
|
||||
|
||||
$this->content .= $line;
|
||||
|
||||
// Check for end condition
|
||||
// phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
|
||||
if ($this->block->getType() <= HtmlBlock::TYPE_5_CDATA) {
|
||||
if (\preg_match(RegexHelper::getHtmlBlockCloseRegex($this->block->getType()), $line) === 1) {
|
||||
$this->finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function closeBlock(): void
|
||||
{
|
||||
$this->block->setLiteral($this->content);
|
||||
$this->content = '';
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
|
||||
use League\CommonMark\Node\Block\Paragraph;
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class HtmlBlockStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented() || $cursor->getNextNonSpaceCharacter() !== '<') {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$tmpCursor = clone $cursor;
|
||||
$tmpCursor->advanceToNextNonSpaceOrTab();
|
||||
$line = $tmpCursor->getRemainder();
|
||||
|
||||
for ($blockType = 1; $blockType <= 7; $blockType++) {
|
||||
/** @psalm-var HtmlBlock::TYPE_* $blockType */
|
||||
/** @phpstan-var HtmlBlock::TYPE_* $blockType */
|
||||
$match = RegexHelper::matchAt(
|
||||
RegexHelper::getHtmlBlockOpenRegex($blockType),
|
||||
$line
|
||||
);
|
||||
|
||||
if ($match !== null && ($blockType < 7 || $this->isType7BlockAllowed($cursor, $parserState))) {
|
||||
return BlockStart::of(new HtmlBlockParser($blockType))->at($cursor);
|
||||
}
|
||||
}
|
||||
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
private function isType7BlockAllowed(Cursor $cursor, MarkdownParserStateInterface $parserState): bool
|
||||
{
|
||||
// Type 7 blocks can't interrupt paragraphs
|
||||
if ($parserState->getLastMatchedBlockParser()->getBlock() instanceof Paragraph) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even lazy ones
|
||||
return ! $parserState->getActiveBlockParser()->canHaveLazyContinuationLines();
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Util\ArrayCollection;
|
||||
|
||||
final class IndentedCodeParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private IndentedCode $block;
|
||||
|
||||
/** @var ArrayCollection<string> */
|
||||
private ArrayCollection $strings;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->block = new IndentedCode();
|
||||
$this->strings = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getBlock(): IndentedCode
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
if ($cursor->isIndented()) {
|
||||
$cursor->advanceBy(Cursor::INDENT_LEVEL, true);
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
if ($cursor->isBlank()) {
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
public function addLine(string $line): void
|
||||
{
|
||||
$this->strings[] = $line;
|
||||
}
|
||||
|
||||
public function closeBlock(): void
|
||||
{
|
||||
$lines = $this->strings->toArray();
|
||||
|
||||
// Note that indented code block cannot be empty, so $lines will always have at least one non-empty element
|
||||
while (\preg_match('/^[ \t]*$/', \end($lines))) { // @phpstan-ignore-line
|
||||
\array_pop($lines);
|
||||
}
|
||||
|
||||
$this->block->setLiteral(\implode("\n", $lines) . "\n");
|
||||
$this->block->setEndLine($this->block->getStartLine() + \count($lines) - 1);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Node\Block\Paragraph;
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
|
||||
final class IndentedCodeStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if (! $cursor->isIndented()) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
if ($parserState->getActiveBlockParser()->getBlock() instanceof Paragraph) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
if ($cursor->isBlank()) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$cursor->advanceBy(Cursor::INDENT_LEVEL, true);
|
||||
|
||||
return BlockStart::of(new IndentedCodeParser())->at($cursor);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
|
||||
final class ListBlockParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private ListBlock $block;
|
||||
|
||||
public function __construct(ListData $listData)
|
||||
{
|
||||
$this->block = new ListBlock($listData);
|
||||
}
|
||||
|
||||
public function getBlock(): ListBlock
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function isContainer(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canContain(AbstractBlock $childBlock): bool
|
||||
{
|
||||
return $childBlock instanceof ListItem;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
// List blocks themselves don't have any markers, only list items. So try to stay in the list.
|
||||
// If there is a block start other than list item, canContain makes sure that this list is closed.
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
public function closeBlock(): void
|
||||
{
|
||||
$item = $this->block->firstChild();
|
||||
while ($item instanceof AbstractBlock) {
|
||||
// check for non-final list item ending with blank line:
|
||||
if ($item->next() !== null && self::endsWithBlankLine($item)) {
|
||||
$this->block->setTight(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// recurse into children of list item, to see if there are spaces between any of them
|
||||
$subitem = $item->firstChild();
|
||||
while ($subitem instanceof AbstractBlock) {
|
||||
if ($subitem->next() && self::endsWithBlankLine($subitem)) {
|
||||
$this->block->setTight(false);
|
||||
break 2;
|
||||
}
|
||||
|
||||
$subitem = $subitem->next();
|
||||
}
|
||||
|
||||
$item = $item->next();
|
||||
}
|
||||
|
||||
$lastChild = $this->block->lastChild();
|
||||
if ($lastChild instanceof AbstractBlock) {
|
||||
$this->block->setEndLine($lastChild->getEndLine());
|
||||
}
|
||||
}
|
||||
|
||||
private static function endsWithBlankLine(AbstractBlock $block): bool
|
||||
{
|
||||
$next = $block->next();
|
||||
|
||||
return $next instanceof AbstractBlock && $block->getEndLine() !== $next->getStartLine() - 1;
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class ListBlockStartParser implements BlockStartParserInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ?ConfigurationInterface $config = null;
|
||||
|
||||
/**
|
||||
* @psalm-var non-empty-string|null
|
||||
*
|
||||
* @psalm-readonly-allow-private-mutation
|
||||
*/
|
||||
private ?string $listMarkerRegex = null;
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented()) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$listData = $this->parseList($cursor, $parserState->getParagraphContent() !== null);
|
||||
if ($listData === null) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$listItemParser = new ListItemParser($listData);
|
||||
|
||||
// prepend the list block if needed
|
||||
$matched = $parserState->getLastMatchedBlockParser();
|
||||
if (! ($matched instanceof ListBlockParser) || ! $listData->equals($matched->getBlock()->getListData())) {
|
||||
$listBlockParser = new ListBlockParser($listData);
|
||||
// We start out with assuming a list is tight. If we find a blank line, we set it to loose later.
|
||||
// TODO for 3.0: Just make them tight by default in the block so we can remove this call
|
||||
$listBlockParser->getBlock()->setTight(true);
|
||||
|
||||
return BlockStart::of($listBlockParser, $listItemParser)->at($cursor);
|
||||
}
|
||||
|
||||
return BlockStart::of($listItemParser)->at($cursor);
|
||||
}
|
||||
|
||||
private function parseList(Cursor $cursor, bool $inParagraph): ?ListData
|
||||
{
|
||||
$indent = $cursor->getIndent();
|
||||
|
||||
$tmpCursor = clone $cursor;
|
||||
$tmpCursor->advanceToNextNonSpaceOrTab();
|
||||
$rest = $tmpCursor->getRemainder();
|
||||
|
||||
if (\preg_match($this->listMarkerRegex ?? $this->generateListMarkerRegex(), $rest) === 1) {
|
||||
$data = new ListData();
|
||||
$data->markerOffset = $indent;
|
||||
$data->type = ListBlock::TYPE_BULLET;
|
||||
$data->delimiter = null;
|
||||
$data->bulletChar = $rest[0];
|
||||
$markerLength = 1;
|
||||
} elseif (($matches = RegexHelper::matchFirst('/^(\d{1,9})([.)])/', $rest)) && (! $inParagraph || $matches[1] === '1')) {
|
||||
$data = new ListData();
|
||||
$data->markerOffset = $indent;
|
||||
$data->type = ListBlock::TYPE_ORDERED;
|
||||
$data->start = (int) $matches[1];
|
||||
$data->delimiter = $matches[2] === '.' ? ListBlock::DELIM_PERIOD : ListBlock::DELIM_PAREN;
|
||||
$data->bulletChar = null;
|
||||
$markerLength = \strlen($matches[0]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure we have spaces after
|
||||
$nextChar = $tmpCursor->peek($markerLength);
|
||||
if (! ($nextChar === null || $nextChar === "\t" || $nextChar === ' ')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If it interrupts paragraph, make sure first line isn't blank
|
||||
if ($inParagraph && ! RegexHelper::matchAt(RegexHelper::REGEX_NON_SPACE, $rest, $markerLength)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrTab(); // to start of marker
|
||||
$cursor->advanceBy($markerLength, true); // to end of marker
|
||||
$data->padding = self::calculateListMarkerPadding($cursor, $markerLength);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private static function calculateListMarkerPadding(Cursor $cursor, int $markerLength): int
|
||||
{
|
||||
$start = $cursor->saveState();
|
||||
$spacesStartCol = $cursor->getColumn();
|
||||
|
||||
while ($cursor->getColumn() - $spacesStartCol < 5) {
|
||||
if (! $cursor->advanceBySpaceOrTab()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$blankItem = $cursor->peek() === null;
|
||||
$spacesAfterMarker = $cursor->getColumn() - $spacesStartCol;
|
||||
|
||||
if ($spacesAfterMarker >= 5 || $spacesAfterMarker < 1 || $blankItem) {
|
||||
$cursor->restoreState($start);
|
||||
$cursor->advanceBySpaceOrTab();
|
||||
|
||||
return $markerLength + 1;
|
||||
}
|
||||
|
||||
return $markerLength + $spacesAfterMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return non-empty-string
|
||||
*/
|
||||
private function generateListMarkerRegex(): string
|
||||
{
|
||||
// No configuration given - use the defaults
|
||||
if ($this->config === null) {
|
||||
return $this->listMarkerRegex = '/^[*+-]/';
|
||||
}
|
||||
|
||||
$markers = $this->config->get('commonmark/unordered_list_markers');
|
||||
\assert(\is_array($markers));
|
||||
|
||||
return $this->listMarkerRegex = '/^[' . \preg_quote(\implode('', $markers), '/') . ']/';
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
|
||||
final class ListItemParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private ListItem $block;
|
||||
|
||||
public function __construct(ListData $listData)
|
||||
{
|
||||
$this->block = new ListItem($listData);
|
||||
}
|
||||
|
||||
public function getBlock(): ListItem
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function isContainer(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canContain(AbstractBlock $childBlock): bool
|
||||
{
|
||||
return ! $childBlock instanceof ListItem;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
if ($cursor->isBlank()) {
|
||||
if ($this->block->firstChild() === null) {
|
||||
// Blank line after empty list item
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrTab();
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
$contentIndent = $this->block->getListData()->markerOffset + $this->getBlock()->getListData()->padding;
|
||||
if ($cursor->getIndent() >= $contentIndent) {
|
||||
$cursor->advanceBy($contentIndent, true);
|
||||
|
||||
return BlockContinue::at($cursor);
|
||||
}
|
||||
|
||||
// Note: We'll hit this case for lazy continuation lines, they will get added later.
|
||||
return BlockContinue::none();
|
||||
}
|
||||
|
||||
public function closeBlock(): void
|
||||
{
|
||||
if (($lastChild = $this->block->lastChild()) instanceof AbstractBlock) {
|
||||
$this->block->setEndLine($lastChild->getEndLine());
|
||||
} else {
|
||||
// Empty list item
|
||||
$this->block->setEndLine($this->block->getStartLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak;
|
||||
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
|
||||
use League\CommonMark\Parser\Block\BlockContinue;
|
||||
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
|
||||
final class ThematicBreakParser extends AbstractBlockContinueParser
|
||||
{
|
||||
/** @psalm-readonly */
|
||||
private ThematicBreak $block;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->block = new ThematicBreak();
|
||||
}
|
||||
|
||||
public function getBlock(): ThematicBreak
|
||||
{
|
||||
return $this->block;
|
||||
}
|
||||
|
||||
public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
|
||||
{
|
||||
// a horizontal rule can never container > 1 line, so fail to match
|
||||
return BlockContinue::none();
|
||||
}
|
||||
}
|
||||
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Block;
|
||||
|
||||
use League\CommonMark\Parser\Block\BlockStart;
|
||||
use League\CommonMark\Parser\Block\BlockStartParserInterface;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\MarkdownParserStateInterface;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class ThematicBreakStartParser implements BlockStartParserInterface
|
||||
{
|
||||
public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
|
||||
{
|
||||
if ($cursor->isIndented()) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
$match = RegexHelper::matchAt(RegexHelper::REGEX_THEMATIC_BREAK, $cursor->getLine(), $cursor->getNextNonSpacePosition());
|
||||
if ($match === null) {
|
||||
return BlockStart::none();
|
||||
}
|
||||
|
||||
// Advance to the end of the string, consuming the entire line (of the thematic break)
|
||||
$cursor->advanceToEnd();
|
||||
|
||||
return BlockStart::of(new ThematicBreakParser())->at($cursor);
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
use League\CommonMark\Util\UrlEncoder;
|
||||
|
||||
final class AutolinkParser implements InlineParserInterface
|
||||
{
|
||||
private const EMAIL_REGEX = '<([a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>';
|
||||
private const OTHER_LINK_REGEX = '<([A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]*)>';
|
||||
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::regex(self::EMAIL_REGEX . '|' . self::OTHER_LINK_REGEX);
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
|
||||
$matches = $inlineContext->getMatches();
|
||||
|
||||
if ($matches[1] !== '') {
|
||||
$inlineContext->getContainer()->appendChild(new Link('mailto:' . UrlEncoder::unescapeAndEncode($matches[1]), $matches[1]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($matches[2] !== '') {
|
||||
$inlineContext->getContainer()->appendChild(new Link(UrlEncoder::unescapeAndEncode($matches[2]), $matches[2]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // This should never happen
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Code;
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
|
||||
final class BacktickParser implements InlineParserInterface
|
||||
{
|
||||
/**
|
||||
* Max bound for backtick code span delimiters.
|
||||
*
|
||||
* @see https://github.com/commonmark/cmark/commit/8ed5c9d
|
||||
*/
|
||||
private const MAX_BACKTICKS = 1000;
|
||||
|
||||
/** @var \WeakReference<Cursor>|null */
|
||||
private ?\WeakReference $lastCursor = null;
|
||||
private bool $lastCursorScanned = false;
|
||||
|
||||
/** @var array<int, int> backtick count => position of known ender */
|
||||
private array $seenBackticks = [];
|
||||
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::regex('`+');
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$ticks = $inlineContext->getFullMatch();
|
||||
$cursor = $inlineContext->getCursor();
|
||||
$cursor->advanceBy($inlineContext->getFullMatchLength());
|
||||
|
||||
$currentPosition = $cursor->getPosition();
|
||||
$previousState = $cursor->saveState();
|
||||
|
||||
if ($this->findMatchingTicks(\strlen($ticks), $cursor)) {
|
||||
$code = $cursor->getSubstring($currentPosition, $cursor->getPosition() - $currentPosition - \strlen($ticks));
|
||||
|
||||
$c = \preg_replace('/\n/m', ' ', $code) ?? '';
|
||||
|
||||
if (
|
||||
$c !== '' &&
|
||||
$c[0] === ' ' &&
|
||||
\substr($c, -1, 1) === ' ' &&
|
||||
\preg_match('/[^ ]/', $c)
|
||||
) {
|
||||
$c = \substr($c, 1, -1);
|
||||
}
|
||||
|
||||
$inlineContext->getContainer()->appendChild(new Code($c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we got here, we didn't match a closing backtick sequence
|
||||
$cursor->restoreState($previousState);
|
||||
$inlineContext->getContainer()->appendChild(new Text($ticks));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the matching closer for a backtick code span.
|
||||
*
|
||||
* Leverages some caching to avoid traversing the same cursor multiple times when
|
||||
* we've already seen all the potential backtick closers.
|
||||
*
|
||||
* @see https://github.com/commonmark/cmark/commit/8ed5c9d
|
||||
*
|
||||
* @param int $openTickLength Number of backticks in the opening sequence
|
||||
* @param Cursor $cursor Cursor to scan
|
||||
*
|
||||
* @return bool True if a matching closer was found, false otherwise
|
||||
*/
|
||||
private function findMatchingTicks(int $openTickLength, Cursor $cursor): bool
|
||||
{
|
||||
// Reset the seenBackticks cache if this is a new cursor
|
||||
if ($this->lastCursor === null || $this->lastCursor->get() !== $cursor) {
|
||||
$this->seenBackticks = [];
|
||||
$this->lastCursor = \WeakReference::create($cursor);
|
||||
$this->lastCursorScanned = false;
|
||||
}
|
||||
|
||||
if ($openTickLength > self::MAX_BACKTICKS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return if we already know there's no closer
|
||||
if ($this->lastCursorScanned && isset($this->seenBackticks[$openTickLength]) && $this->seenBackticks[$openTickLength] <= $cursor->getPosition()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($ticks = $cursor->match('/`{1,' . self::MAX_BACKTICKS . '}/m')) {
|
||||
$numTicks = \strlen($ticks);
|
||||
|
||||
// Did we find the closer?
|
||||
if ($numTicks === $openTickLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Store position of closer
|
||||
if ($numTicks <= self::MAX_BACKTICKS) {
|
||||
$this->seenBackticks[$numTicks] = $cursor->getPosition() - $numTicks;
|
||||
}
|
||||
}
|
||||
|
||||
// Got through whole input without finding closer
|
||||
$this->lastCursorScanned = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
|
||||
final class BangParser implements InlineParserInterface
|
||||
{
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::string('![');
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$cursor = $inlineContext->getCursor();
|
||||
$cursor->advanceBy(2);
|
||||
|
||||
$node = new Text('![', ['delim' => true]);
|
||||
$inlineContext->getContainer()->appendChild($node);
|
||||
|
||||
// Add entry to stack for this opener
|
||||
$inlineContext->getDelimiterStack()->addBracket($node, $cursor->getPosition(), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Delimiter\Bracket;
|
||||
use League\CommonMark\Environment\EnvironmentAwareInterface;
|
||||
use League\CommonMark\Environment\EnvironmentInterface;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\AbstractWebResource;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
|
||||
use League\CommonMark\Extension\Mention\Mention;
|
||||
use League\CommonMark\Node\Inline\AdjacentTextMerger;
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Cursor;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
use League\CommonMark\Reference\ReferenceInterface;
|
||||
use League\CommonMark\Reference\ReferenceMapInterface;
|
||||
use League\CommonMark\Util\LinkParserHelper;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class CloseBracketParser implements InlineParserInterface, EnvironmentAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private EnvironmentInterface $environment;
|
||||
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::string(']');
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
// Look through stack of delimiters for a [ or !
|
||||
$opener = $inlineContext->getDelimiterStack()->getLastBracket();
|
||||
if ($opener === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $opener->isImage() && ! $opener->isActive()) {
|
||||
// no matched opener; remove from stack
|
||||
$inlineContext->getDelimiterStack()->removeBracket();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$cursor = $inlineContext->getCursor();
|
||||
|
||||
$startPos = $cursor->getPosition();
|
||||
$previousState = $cursor->saveState();
|
||||
|
||||
$cursor->advanceBy(1);
|
||||
|
||||
// Check to see if we have a link/image
|
||||
|
||||
// Inline link?
|
||||
if ($result = $this->tryParseInlineLinkAndTitle($cursor)) {
|
||||
$link = $result;
|
||||
} elseif ($link = $this->tryParseReference($cursor, $inlineContext->getReferenceMap(), $opener, $startPos)) {
|
||||
$reference = $link;
|
||||
$link = ['url' => $link->getDestination(), 'title' => $link->getTitle()];
|
||||
} else {
|
||||
// No match; remove this opener from stack
|
||||
$inlineContext->getDelimiterStack()->removeBracket();
|
||||
$cursor->restoreState($previousState);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$inline = $this->createInline($link['url'], $link['title'], $opener->isImage(), $reference ?? null);
|
||||
$opener->getNode()->replaceWith($inline);
|
||||
while (($label = $inline->next()) !== null) {
|
||||
// Is there a Mention or Link contained within this link?
|
||||
// CommonMark does not allow nested links, so we'll restore the original text.
|
||||
if ($label instanceof Mention) {
|
||||
$label->replaceWith($replacement = new Text($label->getPrefix() . $label->getIdentifier()));
|
||||
$inline->appendChild($replacement);
|
||||
} elseif ($label instanceof Link) {
|
||||
foreach ($label->children() as $child) {
|
||||
$label->insertBefore($child);
|
||||
}
|
||||
|
||||
$label->detach();
|
||||
} else {
|
||||
$inline->appendChild($label);
|
||||
}
|
||||
}
|
||||
|
||||
// Process delimiters such as emphasis inside link/image
|
||||
$delimiterStack = $inlineContext->getDelimiterStack();
|
||||
$stackBottom = $opener->getPosition();
|
||||
$delimiterStack->processDelimiters($stackBottom, $this->environment->getDelimiterProcessors());
|
||||
$delimiterStack->removeBracket();
|
||||
$delimiterStack->removeAll($stackBottom);
|
||||
|
||||
// Merge any adjacent Text nodes together
|
||||
AdjacentTextMerger::mergeChildNodes($inline);
|
||||
|
||||
// processEmphasis will remove this and later delimiters.
|
||||
// Now, for a link, we also remove earlier link openers (no links in links)
|
||||
if (! $opener->isImage()) {
|
||||
$inlineContext->getDelimiterStack()->deactivateLinkOpeners();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setEnvironment(EnvironmentInterface $environment): void
|
||||
{
|
||||
$this->environment = $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>|null
|
||||
*/
|
||||
private function tryParseInlineLinkAndTitle(Cursor $cursor): ?array
|
||||
{
|
||||
if ($cursor->getCurrentCharacter() !== '(') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$previousState = $cursor->saveState();
|
||||
|
||||
$cursor->advanceBy(1);
|
||||
$cursor->advanceToNextNonSpaceOrNewline();
|
||||
if (($dest = LinkParserHelper::parseLinkDestination($cursor)) === null) {
|
||||
$cursor->restoreState($previousState);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrNewline();
|
||||
$previousCharacter = $cursor->peek(-1);
|
||||
// We know from previous lines that we've advanced at least one space so far, so this next call should never be null
|
||||
\assert(\is_string($previousCharacter));
|
||||
|
||||
$title = '';
|
||||
// make sure there's a space before the title:
|
||||
if (\preg_match(RegexHelper::REGEX_WHITESPACE_CHAR, $previousCharacter)) {
|
||||
$title = LinkParserHelper::parseLinkTitle($cursor) ?? '';
|
||||
}
|
||||
|
||||
$cursor->advanceToNextNonSpaceOrNewline();
|
||||
|
||||
if ($cursor->getCurrentCharacter() !== ')') {
|
||||
$cursor->restoreState($previousState);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$cursor->advanceBy(1);
|
||||
|
||||
return ['url' => $dest, 'title' => $title];
|
||||
}
|
||||
|
||||
private function tryParseReference(Cursor $cursor, ReferenceMapInterface $referenceMap, Bracket $opener, int $startPos): ?ReferenceInterface
|
||||
{
|
||||
$savePos = $cursor->saveState();
|
||||
$beforeLabel = $cursor->getPosition();
|
||||
$n = LinkParserHelper::parseLinkLabel($cursor);
|
||||
if ($n > 2) {
|
||||
$start = $beforeLabel + 1;
|
||||
$length = $n - 2;
|
||||
} elseif (! $opener->hasNext()) {
|
||||
// Empty or missing second label means to use the first label as the reference.
|
||||
// The reference must not contain a bracket. If we know there's a bracket, we don't even bother checking it.
|
||||
$start = $opener->getPosition();
|
||||
$length = $startPos - $start;
|
||||
} else {
|
||||
$cursor->restoreState($savePos);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$referenceLabel = $cursor->getSubstring($start, $length);
|
||||
|
||||
if ($n === 0) {
|
||||
// If shortcut reference link, rewind before spaces we skipped
|
||||
$cursor->restoreState($savePos);
|
||||
}
|
||||
|
||||
return $referenceMap->get($referenceLabel);
|
||||
}
|
||||
|
||||
private function createInline(string $url, string $title, bool $isImage, ?ReferenceInterface $reference = null): AbstractWebResource
|
||||
{
|
||||
if ($isImage) {
|
||||
$inline = new Image($url, null, $title);
|
||||
} else {
|
||||
$inline = new Link($url, null, $title);
|
||||
}
|
||||
|
||||
if ($reference) {
|
||||
$inline->data->set('reference', $reference);
|
||||
}
|
||||
|
||||
return $inline;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
use League\CommonMark\Util\Html5EntityDecoder;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class EntityParser implements InlineParserInterface
|
||||
{
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::regex(RegexHelper::PARTIAL_ENTITY);
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$entity = $inlineContext->getFullMatch();
|
||||
|
||||
$inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
|
||||
$inlineContext->getContainer()->appendChild(new Text(Html5EntityDecoder::decode($entity)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Newline;
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class EscapableParser implements InlineParserInterface
|
||||
{
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::string('\\');
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$cursor = $inlineContext->getCursor();
|
||||
$nextChar = $cursor->peek();
|
||||
|
||||
if ($nextChar === "\n") {
|
||||
$cursor->advanceBy(2);
|
||||
$inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($nextChar !== null && RegexHelper::isEscapable($nextChar)) {
|
||||
$cursor->advanceBy(2);
|
||||
$inlineContext->getContainer()->appendChild(new Text($nextChar));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$cursor->advanceBy(1);
|
||||
$inlineContext->getContainer()->appendChild(new Text('\\'));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
|
||||
final class HtmlInlineParser implements InlineParserInterface
|
||||
{
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::regex(RegexHelper::PARTIAL_HTMLTAG)->caseSensitive();
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$inline = $inlineContext->getFullMatch();
|
||||
|
||||
$inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
|
||||
$inlineContext->getContainer()->appendChild(new HtmlInline($inline));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
|
||||
|
||||
use League\CommonMark\Node\Inline\Text;
|
||||
use League\CommonMark\Parser\Inline\InlineParserInterface;
|
||||
use League\CommonMark\Parser\Inline\InlineParserMatch;
|
||||
use League\CommonMark\Parser\InlineParserContext;
|
||||
|
||||
final class OpenBracketParser implements InlineParserInterface
|
||||
{
|
||||
public function getMatchDefinition(): InlineParserMatch
|
||||
{
|
||||
return InlineParserMatch::string('[');
|
||||
}
|
||||
|
||||
public function parse(InlineParserContext $inlineContext): bool
|
||||
{
|
||||
$inlineContext->getCursor()->advanceBy(1);
|
||||
$node = new Text('[', ['delim' => true]);
|
||||
$inlineContext->getContainer()->appendChild($node);
|
||||
|
||||
// Add entry to stack for this opener
|
||||
$inlineContext->getDelimiterStack()->addBracket($node, $inlineContext->getCursor()->getPosition(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class BlockQuoteRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param BlockQuote $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
BlockQuote::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
$filling = $childRenderer->renderNodes($node->children());
|
||||
$innerSeparator = $childRenderer->getInnerSeparator();
|
||||
if ($filling === '') {
|
||||
return new HtmlElement('blockquote', $attrs, $innerSeparator);
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'blockquote',
|
||||
$attrs,
|
||||
$innerSeparator . $filling . $innerSeparator
|
||||
);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'block_quote';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BlockQuote $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Util\Xml;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class FencedCodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param FencedCode $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
FencedCode::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->getData('attributes');
|
||||
|
||||
$infoWords = $node->getInfoWords();
|
||||
if (\count($infoWords) !== 0 && $infoWords[0] !== '') {
|
||||
$class = $infoWords[0];
|
||||
if (! \str_starts_with($class, 'language-')) {
|
||||
$class = 'language-' . $class;
|
||||
}
|
||||
|
||||
$attrs->append('class', $class);
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'pre',
|
||||
[],
|
||||
new HtmlElement('code', $attrs->export(), Xml::escape($node->getLiteral()))
|
||||
);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'code_block';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FencedCode $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
FencedCode::assertInstanceOf($node);
|
||||
|
||||
if (($info = $node->getInfo()) === null || $info === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return ['info' => $info];
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class HeadingRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param Heading $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Heading::assertInstanceOf($node);
|
||||
|
||||
$tag = 'h' . $node->getLevel();
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement($tag, $attrs, $childRenderer->renderNodes($node->children()));
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'heading';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Heading $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
Heading::assertInstanceOf($node);
|
||||
|
||||
return ['level' => $node->getLevel()];
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlFilter;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class HtmlBlockRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ConfigurationInterface $config;
|
||||
|
||||
/**
|
||||
* @param HtmlBlock $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
|
||||
{
|
||||
HtmlBlock::assertInstanceOf($node);
|
||||
|
||||
$htmlInput = $this->config->get('html_input');
|
||||
|
||||
return HtmlFilter::filter($node->getLiteral(), $htmlInput);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'html_block';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Util\Xml;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class IndentedCodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param IndentedCode $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
IndentedCode::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement(
|
||||
'pre',
|
||||
[],
|
||||
new HtmlElement('code', $attrs, Xml::escape($node->getLiteral()))
|
||||
);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'code_block';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, scalar>
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class ListBlockRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param ListBlock $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
ListBlock::assertInstanceOf($node);
|
||||
|
||||
$listData = $node->getListData();
|
||||
|
||||
$tag = $listData->type === ListBlock::TYPE_BULLET ? 'ul' : 'ol';
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
if ($listData->start !== null && $listData->start !== 1) {
|
||||
$attrs['start'] = (string) $listData->start;
|
||||
}
|
||||
|
||||
$innerSeparator = $childRenderer->getInnerSeparator();
|
||||
|
||||
return new HtmlElement($tag, $attrs, $innerSeparator . $childRenderer->renderNodes($node->children()) . $innerSeparator);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'list';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ListBlock $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
ListBlock::assertInstanceOf($node);
|
||||
|
||||
$data = $node->getListData();
|
||||
|
||||
if ($data->type === ListBlock::TYPE_BULLET) {
|
||||
return [
|
||||
'type' => $data->type,
|
||||
'tight' => $node->isTight() ? 'true' : 'false',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => $data->type,
|
||||
'start' => $data->start ?? 1,
|
||||
'tight' => $node->isTight(),
|
||||
'delimiter' => $data->delimiter ?? ListBlock::DELIM_PERIOD,
|
||||
];
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
|
||||
use League\CommonMark\Node\Block\AbstractBlock;
|
||||
use League\CommonMark\Node\Block\Paragraph;
|
||||
use League\CommonMark\Node\Block\TightBlockInterface;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class ListItemRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param ListItem $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
ListItem::assertInstanceOf($node);
|
||||
|
||||
$contents = $childRenderer->renderNodes($node->children());
|
||||
|
||||
$inTightList = ($parent = $node->parent()) && $parent instanceof TightBlockInterface && $parent->isTight();
|
||||
|
||||
if ($this->needsBlockSeparator($node->firstChild(), $inTightList)) {
|
||||
$contents = "\n" . $contents;
|
||||
}
|
||||
|
||||
if ($this->needsBlockSeparator($node->lastChild(), $inTightList)) {
|
||||
$contents .= "\n";
|
||||
}
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement('li', $attrs, $contents);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'item';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function needsBlockSeparator(?Node $child, bool $inTightList): bool
|
||||
{
|
||||
if ($child instanceof Paragraph && $inTightList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $child instanceof AbstractBlock;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class ThematicBreakRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param ThematicBreak $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
ThematicBreak::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement('hr', $attrs, '', true);
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'thematic_break';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Code;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Util\Xml;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class CodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param Code $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Code::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement('code', $attrs, Xml::escape($node->getLiteral()));
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'code';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class EmphasisRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param Emphasis $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Emphasis::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement('em', $attrs, $childRenderer->renderNodes($node->children()));
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'emph';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlFilter;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class HtmlInlineRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ConfigurationInterface $config;
|
||||
|
||||
/**
|
||||
* @param HtmlInline $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
|
||||
{
|
||||
HtmlInline::assertInstanceOf($node);
|
||||
|
||||
$htmlInput = $this->config->get('html_input');
|
||||
|
||||
return HtmlFilter::filter($node->getLiteral(), $htmlInput);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'html_inline';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
|
||||
use League\CommonMark\Node\Inline\Newline;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Node\NodeIterator;
|
||||
use League\CommonMark\Node\StringContainerInterface;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class ImageRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ConfigurationInterface $config;
|
||||
|
||||
/**
|
||||
* @param Image $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Image::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
$forbidUnsafeLinks = ! $this->config->get('allow_unsafe_links');
|
||||
if ($forbidUnsafeLinks && RegexHelper::isLinkPotentiallyUnsafe($node->getUrl())) {
|
||||
$attrs['src'] = '';
|
||||
} else {
|
||||
$attrs['src'] = $node->getUrl();
|
||||
}
|
||||
|
||||
$attrs['alt'] = $this->getAltText($node);
|
||||
|
||||
if (($title = $node->getTitle()) !== null) {
|
||||
$attrs['title'] = $title;
|
||||
}
|
||||
|
||||
return new HtmlElement('img', $attrs, '', true);
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'image';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Image $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
Image::assertInstanceOf($node);
|
||||
|
||||
return [
|
||||
'destination' => $node->getUrl(),
|
||||
'title' => $node->getTitle() ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
private function getAltText(Image $node): string
|
||||
{
|
||||
$altText = '';
|
||||
|
||||
foreach ((new NodeIterator($node)) as $n) {
|
||||
if ($n instanceof StringContainerInterface) {
|
||||
$altText .= $n->getLiteral();
|
||||
} elseif ($n instanceof Newline) {
|
||||
$altText .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $altText;
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Util\RegexHelper;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
use League\Config\ConfigurationAwareInterface;
|
||||
use League\Config\ConfigurationInterface;
|
||||
|
||||
final class LinkRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
|
||||
{
|
||||
/** @psalm-readonly-allow-private-mutation */
|
||||
private ConfigurationInterface $config;
|
||||
|
||||
/**
|
||||
* @param Link $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Link::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
$forbidUnsafeLinks = ! $this->config->get('allow_unsafe_links');
|
||||
if (! ($forbidUnsafeLinks && RegexHelper::isLinkPotentiallyUnsafe($node->getUrl()))) {
|
||||
$attrs['href'] = $node->getUrl();
|
||||
}
|
||||
|
||||
if (($title = $node->getTitle()) !== null) {
|
||||
$attrs['title'] = $title;
|
||||
}
|
||||
|
||||
if (isset($attrs['target']) && $attrs['target'] === '_blank' && ! isset($attrs['rel'])) {
|
||||
$attrs['rel'] = 'noopener noreferrer';
|
||||
}
|
||||
|
||||
return new HtmlElement('a', $attrs, $childRenderer->renderNodes($node->children()));
|
||||
}
|
||||
|
||||
public function setConfiguration(ConfigurationInterface $configuration): void
|
||||
{
|
||||
$this->config = $configuration;
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'link';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Link $node
|
||||
*
|
||||
* @return array<string, scalar>
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
Link::assertInstanceOf($node);
|
||||
|
||||
return [
|
||||
'destination' => $node->getUrl(),
|
||||
'title' => $node->getTitle() ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the league/commonmark package.
|
||||
*
|
||||
* (c) Colin O'Dell <colinodell@gmail.com>
|
||||
*
|
||||
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
|
||||
* - (c) John MacFarlane
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
|
||||
|
||||
use League\CommonMark\Extension\CommonMark\Node\Inline\Strong;
|
||||
use League\CommonMark\Node\Node;
|
||||
use League\CommonMark\Renderer\ChildNodeRendererInterface;
|
||||
use League\CommonMark\Renderer\NodeRendererInterface;
|
||||
use League\CommonMark\Util\HtmlElement;
|
||||
use League\CommonMark\Xml\XmlNodeRendererInterface;
|
||||
|
||||
final class StrongRenderer implements NodeRendererInterface, XmlNodeRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param Strong $node
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
|
||||
{
|
||||
Strong::assertInstanceOf($node);
|
||||
|
||||
$attrs = $node->data->get('attributes');
|
||||
|
||||
return new HtmlElement('strong', $attrs, $childRenderer->renderNodes($node->children()));
|
||||
}
|
||||
|
||||
public function getXmlTagName(Node $node): string
|
||||
{
|
||||
return 'strong';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getXmlAttributes(Node $node): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user