Inlines and Blocks as Configurables

This commit is contained in:
Aidan Woods 2019-01-25 19:49:18 +00:00
parent eb90905d27
commit fce4633ff9
No known key found for this signature in database
GPG Key ID: 9A6A8EFAA512BBB9
4 changed files with 223 additions and 86 deletions

View File

@ -28,6 +28,8 @@
<referencedMethod name="Erusev\Parsedown\Configurables\StrictMode::enabled" /> <referencedMethod name="Erusev\Parsedown\Configurables\StrictMode::enabled" />
<referencedMethod name="Erusev\Parsedown\Configurables\SafeMode::enabled" /> <referencedMethod name="Erusev\Parsedown\Configurables\SafeMode::enabled" />
<referencedMethod name="Erusev\Parsedown\Html\Renderables\Container::__construct" /> <referencedMethod name="Erusev\Parsedown\Html\Renderables\Container::__construct" />
<referencedMethod name="Erusev\Parsedown\Configurables\BlockTypes::settingMarked" />
<referencedMethod name="Erusev\Parsedown\Configurables\BlockTypes::settingUnmarked" />
</errorLevel> </errorLevel>
</PossiblyUnusedMethod> </PossiblyUnusedMethod>
</issueHandlers> </issueHandlers>

View File

@ -0,0 +1,123 @@
<?php
namespace Erusev\Parsedown\Configurables;
use Erusev\Parsedown\Components\Block;
use Erusev\Parsedown\Components\Blocks\BlockQuote;
use Erusev\Parsedown\Components\Blocks\Comment;
use Erusev\Parsedown\Components\Blocks\FencedCode;
use Erusev\Parsedown\Components\Blocks\Header;
use Erusev\Parsedown\Components\Blocks\IndentedCode;
use Erusev\Parsedown\Components\Blocks\Markup as BlockMarkup;
use Erusev\Parsedown\Components\Blocks\Reference;
use Erusev\Parsedown\Components\Blocks\Rule;
use Erusev\Parsedown\Components\Blocks\SetextHeader;
use Erusev\Parsedown\Components\Blocks\Table;
use Erusev\Parsedown\Components\Blocks\TList;
use Erusev\Parsedown\Configurable;
/**
* @psalm-type _Data=array{url: string, title: string|null}
*/
final class BlockTypes implements Configurable
{
/** @var array<array-key, class-string<Block>[]> */
private static $defaultBlockTypes = [
'#' => [Header::class],
'*' => [Rule::class, TList::class],
'+' => [TList::class],
'-' => [SetextHeader::class, Table::class, Rule::class, TList::class],
'0' => [TList::class],
'1' => [TList::class],
'2' => [TList::class],
'3' => [TList::class],
'4' => [TList::class],
'5' => [TList::class],
'6' => [TList::class],
'7' => [TList::class],
'8' => [TList::class],
'9' => [TList::class],
':' => [Table::class],
'<' => [Comment::class, BlockMarkup::class],
'=' => [SetextHeader::class],
'>' => [BlockQuote::class],
'[' => [Reference::class],
'_' => [Rule::class],
'`' => [FencedCode::class],
'|' => [Table::class],
'~' => [FencedCode::class],
];
/** @var class-string<Block>[] */
private static $defaultUnmarkedBlockTypes = [
IndentedCode::class,
];
/** @var array<array-key, class-string<Block>[]> */
private $blockTypes;
/** @var class-string<Block>[] */
private $unmarkedBlockTypes;
/**
* @param array<array-key, class-string<Block>[]> $blockTypes
* @param class-string<Block>[] $unmarkedBlockTypes
*/
public function __construct(array $blockTypes, array $unmarkedBlockTypes)
{
$this->blockTypes = $blockTypes;
$this->unmarkedBlockTypes = $unmarkedBlockTypes;
}
/** @return self */
public static function initial()
{
return new self(
self::$defaultBlockTypes,
self::$defaultUnmarkedBlockTypes
);
}
/**
* @param string $marker
* @param class-string<Block>[] $newBlockTypes
* @return self
*/
public function settingMarked($marker, array $newBlockTypes)
{
$blockTypes = $this->blockTypes;
$blockTypes[$marker] = $newBlockTypes;
return new self($blockTypes, $this->unmarkedBlockTypes);
}
/**
* @param class-string<Block>[] $newUnmarkedBlockTypes
* @return self
*/
public function settingUnmarked(array $newUnmarkedBlockTypes)
{
return new self($this->blockTypes, $newUnmarkedBlockTypes);
}
/**
* @param string $marker
* @return class-string<Block>[]
*/
public function for($marker)
{
if (isset($this->blockTypes[$marker])) {
return $this->blockTypes[$marker];
}
return [];
}
/**
* @return class-string<Block>[]
*/
public function unmarked()
{
return $this->unmarkedBlockTypes;
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Erusev\Parsedown\Configurables;
use Erusev\Parsedown\Components\Inline;
use Erusev\Parsedown\Components\Inlines\Code;
use Erusev\Parsedown\Components\Inlines\Email;
use Erusev\Parsedown\Components\Inlines\Emphasis;
use Erusev\Parsedown\Components\Inlines\EscapeSequence;
use Erusev\Parsedown\Components\Inlines\HardBreak;
use Erusev\Parsedown\Components\Inlines\Image;
use Erusev\Parsedown\Components\Inlines\Link;
use Erusev\Parsedown\Components\Inlines\Markup as InlineMarkup;
use Erusev\Parsedown\Components\Inlines\SoftBreak;
use Erusev\Parsedown\Components\Inlines\SpecialCharacter;
use Erusev\Parsedown\Components\Inlines\Strikethrough;
use Erusev\Parsedown\Components\Inlines\Url;
use Erusev\Parsedown\Components\Inlines\UrlTag;
use Erusev\Parsedown\Configurable;
/**
* @psalm-type _Data=array{url: string, title: string|null}
*/
final class InlineTypes implements Configurable
{
/** @var array<array-key, class-string<Inline>[]> */
private static $defaultInlineTypes = [
'!' => [Image::class],
'*' => [Emphasis::class],
'_' => [Emphasis::class],
'&' => [SpecialCharacter::class],
'[' => [Link::class],
':' => [Url::class],
'<' => [UrlTag::class, Email::class, InlineMarkup::class],
'`' => [Code::class],
'~' => [Strikethrough::class],
'\\' => [EscapeSequence::class],
"\n" => [HardBreak::class, SoftBreak::class],
];
/** @var array<array-key, class-string<Inline>[]> */
private $inlineTypes;
/** @var string */
private $inlineMarkers;
/**
* @param array<array-key, class-string<Inline>[]> $inlineTypes
*/
public function __construct(array $inlineTypes)
{
$this->inlineTypes = $inlineTypes;
$this->inlineMarkers = \implode('', \array_keys($inlineTypes));
}
/** @return self */
public static function initial()
{
return new self(self::$defaultInlineTypes);
}
/**
* @param string $marker
* @return class-string<Inline>[]
*/
public function for($marker)
{
if (isset($this->inlineTypes[$marker])) {
return $this->inlineTypes[$marker];
}
return [];
}
/** @return string */
public function markers()
{
return $this->inlineMarkers;
}
}

View File

@ -4,35 +4,13 @@ namespace Erusev\Parsedown;
use Erusev\Parsedown\AST\StateRenderable; use Erusev\Parsedown\AST\StateRenderable;
use Erusev\Parsedown\Components\Block; use Erusev\Parsedown\Components\Block;
use Erusev\Parsedown\Components\Blocks\BlockQuote;
use Erusev\Parsedown\Components\Blocks\Comment;
use Erusev\Parsedown\Components\Blocks\FencedCode;
use Erusev\Parsedown\Components\Blocks\Header;
use Erusev\Parsedown\Components\Blocks\IndentedCode;
use Erusev\Parsedown\Components\Blocks\Markup as BlockMarkup;
use Erusev\Parsedown\Components\Blocks\Paragraph; use Erusev\Parsedown\Components\Blocks\Paragraph;
use Erusev\Parsedown\Components\Blocks\Reference;
use Erusev\Parsedown\Components\Blocks\Rule;
use Erusev\Parsedown\Components\Blocks\SetextHeader;
use Erusev\Parsedown\Components\Blocks\Table;
use Erusev\Parsedown\Components\Blocks\TList;
use Erusev\Parsedown\Components\ContinuableBlock; use Erusev\Parsedown\Components\ContinuableBlock;
use Erusev\Parsedown\Components\Inline; use Erusev\Parsedown\Components\Inline;
use Erusev\Parsedown\Components\Inlines\Code;
use Erusev\Parsedown\Components\Inlines\Email;
use Erusev\Parsedown\Components\Inlines\Emphasis;
use Erusev\Parsedown\Components\Inlines\EscapeSequence;
use Erusev\Parsedown\Components\Inlines\HardBreak;
use Erusev\Parsedown\Components\Inlines\Image;
use Erusev\Parsedown\Components\Inlines\Link;
use Erusev\Parsedown\Components\Inlines\Markup as InlineMarkup;
use Erusev\Parsedown\Components\Inlines\PlainText; use Erusev\Parsedown\Components\Inlines\PlainText;
use Erusev\Parsedown\Components\Inlines\SoftBreak;
use Erusev\Parsedown\Components\Inlines\SpecialCharacter;
use Erusev\Parsedown\Components\Inlines\Strikethrough;
use Erusev\Parsedown\Components\Inlines\Url;
use Erusev\Parsedown\Components\Inlines\UrlTag;
use Erusev\Parsedown\Components\StateUpdatingBlock; use Erusev\Parsedown\Components\StateUpdatingBlock;
use Erusev\Parsedown\Configurables\BlockTypes;
use Erusev\Parsedown\Configurables\InlineTypes;
use Erusev\Parsedown\Html\Renderable; use Erusev\Parsedown\Html\Renderable;
use Erusev\Parsedown\Html\Renderables\Invisible; use Erusev\Parsedown\Html\Renderables\Invisible;
use Erusev\Parsedown\Html\Renderables\Text; use Erusev\Parsedown\Html\Renderables\Text;
@ -48,61 +26,15 @@ final class Parsedown
/** @var State */ /** @var State */
private $State; private $State;
/** @var array<array-key, class-string<Block>[]> */
private $BlockTypes = [
'#' => [Header::class],
'*' => [Rule::class, TList::class],
'+' => [TList::class],
'-' => [SetextHeader::class, Table::class, Rule::class, TList::class],
'0' => [TList::class],
'1' => [TList::class],
'2' => [TList::class],
'3' => [TList::class],
'4' => [TList::class],
'5' => [TList::class],
'6' => [TList::class],
'7' => [TList::class],
'8' => [TList::class],
'9' => [TList::class],
':' => [Table::class],
'<' => [Comment::class, BlockMarkup::class],
'=' => [SetextHeader::class],
'>' => [BlockQuote::class],
'[' => [Reference::class],
'_' => [Rule::class],
'`' => [FencedCode::class],
'|' => [Table::class],
'~' => [FencedCode::class],
];
/** @var class-string<Block>[] */
private $unmarkedBlockTypes = [
IndentedCode::class,
];
/** @var array<array-key, class-string<Inline>[]> */
private $InlineTypes = [
'!' => [Image::class],
'*' => [Emphasis::class],
'_' => [Emphasis::class],
'&' => [SpecialCharacter::class],
'[' => [Link::class],
':' => [Url::class],
'<' => [UrlTag::class, Email::class, InlineMarkup::class],
'`' => [Code::class],
'~' => [Strikethrough::class],
'\\' => [EscapeSequence::class],
"\n" => [HardBreak::class, SoftBreak::class],
];
/** @var string */
private $inlineMarkers;
public function __construct(State $State = null) public function __construct(State $State = null)
{ {
$this->State = $State ?: new State; $this->State = $State ?: new State;
$this->inlineMarkers = \implode('', \array_keys($this->InlineTypes)); // ensure we cache the initial value if these weren't explicitly set
$this->State = $this->State->mergingWith(new State([
$this->State->get(BlockTypes::class),
$this->State->get(InlineTypes::class),
]));
} }
/** /**
@ -174,18 +106,15 @@ final class Parsedown
# ~ # ~
$blockTypes = $this->unmarkedBlockTypes; $potentialBlockTypes = \array_merge(
$this->State->get(BlockTypes::class)->unmarked(),
if (isset($this->BlockTypes[$marker])) { $this->State->get(BlockTypes::class)->for($marker)
foreach ($this->BlockTypes[$marker] as $blockType) { );
$blockTypes []= $blockType;
}
}
# #
# ~ # ~
foreach ($blockTypes as $blockType) { foreach ($potentialBlockTypes as $blockType) {
$Block = $blockType::build($Context, $CurrentBlock, $this->State); $Block = $blockType::build($Context, $CurrentBlock, $this->State);
if (isset($Block)) { if (isset($Block)) {
@ -264,14 +193,17 @@ final class Parsedown
# $excerpt is based on the first occurrence of a marker # $excerpt is based on the first occurrence of a marker
$InlineTypes = $this->State->get(InlineTypes::class);
$markerMask = $InlineTypes->markers();
for ( for (
$Excerpt = (new Excerpt($text, 0))->pushingOffsetTo($this->inlineMarkers); $Excerpt = (new Excerpt($text, 0))->pushingOffsetTo($markerMask);
$Excerpt->text() !== ''; $Excerpt->text() !== '';
$Excerpt = $Excerpt->pushingOffsetTo($this->inlineMarkers) $Excerpt = $Excerpt->pushingOffsetTo($markerMask)
) { ) {
$marker = \substr($Excerpt->text(), 0, 1); $marker = \substr($Excerpt->text(), 0, 1);
foreach ($this->InlineTypes[$marker] as $inlineType) { foreach ($InlineTypes->for($marker) as $inlineType) {
# check to see if the current inline type is nestable in the current context # check to see if the current inline type is nestable in the current context
$Inline = $inlineType::build($Excerpt, $this->State); $Inline = $inlineType::build($Excerpt, $this->State);