diff --git a/src/Hint.php b/src/Hint.php new file mode 100644 index 0000000..e22fdcd --- /dev/null +++ b/src/Hint.php @@ -0,0 +1,57 @@ +getHeaderWords(); + + if (count($words) > 1) { + array_shift($words); + + return join(' ', $words); + } + + return null; + } + + public function getType(): ?string + { + $words = $this->getHeaderWords(); + + if (count($words) > 0) { + return $words[0]; + } + + return null; + } + + public function getHeaderWords(): array + { + return \preg_split('/\s+/', $this->header ?? '') ?: []; + } + + public function setHeader($header) + { + $this->header = $header; + } + + public function setLiteral(string $literal): void + { + $this->literal = $literal; + } + + public function getLiteral(): string + { + return $this->literal; + } +} diff --git a/src/HintParser.php b/src/HintParser.php index ee41902..6d0bd76 100644 --- a/src/HintParser.php +++ b/src/HintParser.php @@ -2,60 +2,91 @@ namespace Ueberdosis\CommonMark; -use League\CommonMark\Parser\Block\BlockContinueParserInterface; -use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote; +use League\CommonMark\Parser\Cursor; +use League\CommonMark\Util\RegexHelper; +use League\CommonMark\Util\ArrayCollection; use League\CommonMark\Node\Block\AbstractBlock; use League\CommonMark\Parser\Block\BlockContinue; -use League\CommonMark\Parser\Cursor; +use League\CommonMark\Parser\Block\AbstractBlockContinueParser; +use League\CommonMark\Parser\Block\BlockContinueParserInterface; +use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface; +use League\CommonMark\Parser\InlineParserEngineInterface; -class HintParser implements BlockContinueParserInterface +class HintParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface { /** @psalm-readonly */ - private BlockQuote $block; + private Hint $block; + + /** @var ArrayCollection */ + private ArrayCollection $strings; public function __construct() { - $this->block = new BlockQuote(); + $this->block = new Hint(); + $this->strings = new ArrayCollection(); } - public function getBlock(): BlockQuote + public function getBlock(): Hint { return $this->block; } public function isContainer(): bool { - return true; + return false; } public function canContain(AbstractBlock $childBlock): bool + { + return false; + } + + public function canHaveLazyContinuationLines(): bool { return true; } - public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue + public function parseInlines(InlineParserEngineInterface $inlineParser): void { - if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === '>') { - $cursor->advanceToNextNonSpaceOrTab(); - $cursor->advanceBy(1); - $cursor->advanceBySpaceOrTab(); + $inlineParser->parse($this->block->getLiteral(), $this->block); + } - return BlockContinue::at($cursor); + public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue + { + if ($cursor->getLine() === ':::') { + return BlockContinue::finished(); } - return BlockContinue::none(); - } + $cursor->advanceToNextNonSpaceOrTab(); + $cursor->advanceBySpaceOrTab(); - public function canHaveLazyContinuationLines(): bool - { - return false; + return BlockContinue::at($cursor); } + // public function canHaveLazyContinuationLines(): bool + // { + // return true; + // } + 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->setHeader(RegexHelper::unescape(\trim($firstLine))); + + if ($this->strings->count() === 1) { + $this->block->setLiteral(''); + } else { + $this->block->setLiteral(\implode("\n", $this->strings->slice(1)) . "\n"); + } } } diff --git a/src/HintRenderer.php b/src/HintRenderer.php index 20381c0..382e4d5 100644 --- a/src/HintRenderer.php +++ b/src/HintRenderer.php @@ -2,17 +2,15 @@ namespace Ueberdosis\CommonMark; -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 HintRenderer implements NodeRendererInterface, XmlNodeRendererInterface +final class HintRenderer implements NodeRendererInterface { /** - * @param BlockQuote $node + * @param Hint $node * * {@inheritDoc} * @@ -20,37 +18,38 @@ final class HintRenderer implements NodeRendererInterface, XmlNodeRendererInterf */ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable { - BlockQuote::assertInstanceOf($node); + Hint::assertInstanceOf($node); $attrs = $node->data->get('attributes'); + isset($attrs['class']) ? $attrs['class'] .= ' hint' : $attrs['class'] = 'hint'; - $filling = $childRenderer->renderNodes($node->children()); - $innerSeparator = $childRenderer->getInnerSeparator(); - if ($filling === '') { - return new HtmlElement('blockquote', $attrs, $innerSeparator); + if ($type = $node->getType()) { + $attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' ' : ''; + $attrs['class'] .= $type; } + $title = $node->getTitle(); + $title = $title + ? new HtmlElement( + 'h2', + ['class' => 'hint-title'], + $title, + ) + : ''; + + $content = new HtmlElement( + 'p', + ['class' => 'hint-content'], + $childRenderer->renderNodes($node->children()) + ); + return new HtmlElement( - 'blockquote', + 'div', $attrs, - $innerSeparator . $filling . $innerSeparator + "\n" . + $title . "\n" . + $content . + "\n" ); } - - public function getXmlTagName(Node $node): string - { - return 'block_quote'; - } - - /** - * @param BlockQuote $node - * - * @return array - * - * @psalm-suppress MoreSpecificImplementedParamType - */ - public function getXmlAttributes(Node $node): array - { - return []; - } } diff --git a/src/HintStartParser.php b/src/HintStartParser.php index 75d2ed4..47a8cfe 100644 --- a/src/HintStartParser.php +++ b/src/HintStartParser.php @@ -15,14 +15,11 @@ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserSta return BlockStart::none(); } - if ($cursor->getNextNonSpaceCharacter() !== '>') { + $fence = $cursor->match('/^(?:\:{3,}(?!.*`))/'); + if ($fence === null) { return BlockStart::none(); } - $cursor->advanceToNextNonSpaceOrTab(); - $cursor->advanceBy(1); - $cursor->advanceBySpaceOrTab(); - return BlockStart::of(new HintParser())->at($cursor); } } diff --git a/tests/HintExtensionTest.php b/tests/HintExtensionTest.php index 57ec957..0472d7f 100644 --- a/tests/HintExtensionTest.php +++ b/tests/HintExtensionTest.php @@ -11,7 +11,7 @@ class HintExtensionTest extends TestCase { /** @test */ - public function true_is_true() + public function markdown_hints_are_rendered() { // Configure the Environment with all the CommonMark parsers/renderers $environment = new Environment(); @@ -28,13 +28,72 @@ public function true_is_true() ::: MARKDOWN; - $this->assertEquals((string) $converter->convertToHtml($markdown), '
-

- Warning! -

-

- This is how the Markdown looks. -

-
'); + $this->assertEquals((string) $converter->convertToHtml($markdown), << +

Warning!

+

This is how the Markdown looks.

+ + +HTML); + } + + /** @test */ + public function markdown_hints_after_headings_are_rendered() + { + // Configure the Environment with all the CommonMark parsers/renderers + $environment = new Environment(); + $environment->addExtension(new CommonMarkCoreExtension()); + + // Add this extension + $environment->addExtension(new HintExtension()); + + // Instantiate the converter engine and start converting some Markdown! + $converter = new MarkdownConverter($environment); + $markdown = <<assertEquals((string) $converter->convertToHtml($markdown), <<Test +
+

Warning!

+

This is how the Markdown looks.

+
+ +HTML); + } + + /** @test */ + public function paragraphs_after_hints_are_rendered() + { + // Configure the Environment with all the CommonMark parsers/renderers + $environment = new Environment(); + $environment->addExtension(new CommonMarkCoreExtension()); + + // Add this extension + $environment->addExtension(new HintExtension()); + + // Instantiate the converter engine and start converting some Markdown! + $converter = new MarkdownConverter($environment); + $markdown = <<assertEquals((string) $converter->convertToHtml($markdown), << +

Warning!

+

This is how the Markdown looks.

+ +

Test

+ +HTML); } }