├── .phpstorm.meta.php ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json └── src ├── CommonMarkConverter.php ├── ConverterInterface.php ├── Delimiter ├── Bracket.php ├── Delimiter.php ├── DelimiterInterface.php ├── DelimiterParser.php ├── DelimiterStack.php └── Processor │ ├── CacheableDelimiterProcessorInterface.php │ ├── DelimiterProcessorCollection.php │ ├── DelimiterProcessorCollectionInterface.php │ ├── DelimiterProcessorInterface.php │ └── StaggeredDelimiterProcessor.php ├── Environment ├── Environment.php ├── EnvironmentAwareInterface.php ├── EnvironmentBuilderInterface.php └── EnvironmentInterface.php ├── Event ├── AbstractEvent.php ├── DocumentParsedEvent.php ├── DocumentPreParsedEvent.php ├── DocumentPreRenderEvent.php ├── DocumentRenderedEvent.php └── ListenerData.php ├── Exception ├── AlreadyInitializedException.php ├── CommonMarkException.php ├── IOException.php ├── InvalidArgumentException.php ├── LogicException.php ├── MissingDependencyException.php └── UnexpectedEncodingException.php ├── Extension ├── Attributes │ ├── AttributesExtension.php │ ├── Event │ │ └── AttributesListener.php │ ├── Node │ │ ├── Attributes.php │ │ └── AttributesInline.php │ ├── Parser │ │ ├── AttributesBlockContinueParser.php │ │ ├── AttributesBlockStartParser.php │ │ └── AttributesInlineParser.php │ └── Util │ │ └── AttributesHelper.php ├── Autolink │ ├── AutolinkExtension.php │ ├── EmailAutolinkParser.php │ └── UrlAutolinkParser.php ├── CommonMark │ ├── CommonMarkCoreExtension.php │ ├── Delimiter │ │ └── Processor │ │ │ └── EmphasisDelimiterProcessor.php │ ├── Node │ │ ├── Block │ │ │ ├── BlockQuote.php │ │ │ ├── FencedCode.php │ │ │ ├── Heading.php │ │ │ ├── HtmlBlock.php │ │ │ ├── IndentedCode.php │ │ │ ├── ListBlock.php │ │ │ ├── ListData.php │ │ │ ├── ListItem.php │ │ │ └── ThematicBreak.php │ │ └── Inline │ │ │ ├── AbstractWebResource.php │ │ │ ├── Code.php │ │ │ ├── Emphasis.php │ │ │ ├── HtmlInline.php │ │ │ ├── Image.php │ │ │ ├── Link.php │ │ │ └── Strong.php │ ├── Parser │ │ ├── Block │ │ │ ├── BlockQuoteParser.php │ │ │ ├── BlockQuoteStartParser.php │ │ │ ├── FencedCodeParser.php │ │ │ ├── FencedCodeStartParser.php │ │ │ ├── HeadingParser.php │ │ │ ├── HeadingStartParser.php │ │ │ ├── HtmlBlockParser.php │ │ │ ├── HtmlBlockStartParser.php │ │ │ ├── IndentedCodeParser.php │ │ │ ├── IndentedCodeStartParser.php │ │ │ ├── ListBlockParser.php │ │ │ ├── ListBlockStartParser.php │ │ │ ├── ListItemParser.php │ │ │ ├── ThematicBreakParser.php │ │ │ └── ThematicBreakStartParser.php │ │ └── Inline │ │ │ ├── AutolinkParser.php │ │ │ ├── BacktickParser.php │ │ │ ├── BangParser.php │ │ │ ├── CloseBracketParser.php │ │ │ ├── EntityParser.php │ │ │ ├── EscapableParser.php │ │ │ ├── HtmlInlineParser.php │ │ │ └── OpenBracketParser.php │ └── Renderer │ │ ├── Block │ │ ├── BlockQuoteRenderer.php │ │ ├── FencedCodeRenderer.php │ │ ├── HeadingRenderer.php │ │ ├── HtmlBlockRenderer.php │ │ ├── IndentedCodeRenderer.php │ │ ├── ListBlockRenderer.php │ │ ├── ListItemRenderer.php │ │ └── ThematicBreakRenderer.php │ │ └── Inline │ │ ├── CodeRenderer.php │ │ ├── EmphasisRenderer.php │ │ ├── HtmlInlineRenderer.php │ │ ├── ImageRenderer.php │ │ ├── LinkRenderer.php │ │ └── StrongRenderer.php ├── ConfigurableExtensionInterface.php ├── DefaultAttributes │ ├── ApplyDefaultAttributesProcessor.php │ └── DefaultAttributesExtension.php ├── DescriptionList │ ├── DescriptionListExtension.php │ ├── Event │ │ ├── ConsecutiveDescriptionListMerger.php │ │ └── LooseDescriptionHandler.php │ ├── Node │ │ ├── Description.php │ │ ├── DescriptionList.php │ │ └── DescriptionTerm.php │ ├── Parser │ │ ├── DescriptionContinueParser.php │ │ ├── DescriptionListContinueParser.php │ │ ├── DescriptionStartParser.php │ │ └── DescriptionTermContinueParser.php │ └── Renderer │ │ ├── DescriptionListRenderer.php │ │ ├── DescriptionRenderer.php │ │ └── DescriptionTermRenderer.php ├── DisallowedRawHtml │ ├── DisallowedRawHtmlExtension.php │ └── DisallowedRawHtmlRenderer.php ├── Embed │ ├── Bridge │ │ └── OscaroteroEmbedAdapter.php │ ├── DomainFilteringAdapter.php │ ├── Embed.php │ ├── EmbedAdapterInterface.php │ ├── EmbedExtension.php │ ├── EmbedParser.php │ ├── EmbedProcessor.php │ ├── EmbedRenderer.php │ └── EmbedStartParser.php ├── ExtensionInterface.php ├── ExternalLink │ ├── ExternalLinkExtension.php │ └── ExternalLinkProcessor.php ├── Footnote │ ├── Event │ │ ├── AnonymousFootnotesListener.php │ │ ├── FixOrphanedFootnotesAndRefsListener.php │ │ ├── GatherFootnotesListener.php │ │ └── NumberFootnotesListener.php │ ├── FootnoteExtension.php │ ├── Node │ │ ├── Footnote.php │ │ ├── FootnoteBackref.php │ │ ├── FootnoteContainer.php │ │ └── FootnoteRef.php │ ├── Parser │ │ ├── AnonymousFootnoteRefParser.php │ │ ├── FootnoteParser.php │ │ ├── FootnoteRefParser.php │ │ └── FootnoteStartParser.php │ └── Renderer │ │ ├── FootnoteBackrefRenderer.php │ │ ├── FootnoteContainerRenderer.php │ │ ├── FootnoteRefRenderer.php │ │ └── FootnoteRenderer.php ├── FrontMatter │ ├── Data │ │ ├── FrontMatterDataParserInterface.php │ │ ├── LibYamlFrontMatterParser.php │ │ └── SymfonyYamlFrontMatterParser.php │ ├── Exception │ │ └── InvalidFrontMatterException.php │ ├── FrontMatterExtension.php │ ├── FrontMatterParser.php │ ├── FrontMatterParserInterface.php │ ├── FrontMatterProviderInterface.php │ ├── Input │ │ └── MarkdownInputWithFrontMatter.php │ ├── Listener │ │ ├── FrontMatterPostRenderListener.php │ │ └── FrontMatterPreParser.php │ └── Output │ │ └── RenderedContentWithFrontMatter.php ├── GithubFlavoredMarkdownExtension.php ├── HeadingPermalink │ ├── HeadingPermalink.php │ ├── HeadingPermalinkExtension.php │ ├── HeadingPermalinkProcessor.php │ └── HeadingPermalinkRenderer.php ├── InlinesOnly │ ├── ChildRenderer.php │ └── InlinesOnlyExtension.php ├── Mention │ ├── Generator │ │ ├── CallbackGenerator.php │ │ ├── MentionGeneratorInterface.php │ │ └── StringTemplateLinkGenerator.php │ ├── Mention.php │ ├── MentionExtension.php │ └── MentionParser.php ├── SmartPunct │ ├── DashParser.php │ ├── EllipsesParser.php │ ├── Quote.php │ ├── QuoteParser.php │ ├── QuoteProcessor.php │ ├── ReplaceUnpairedQuotesListener.php │ └── SmartPunctExtension.php ├── Strikethrough │ ├── Strikethrough.php │ ├── StrikethroughDelimiterProcessor.php │ ├── StrikethroughExtension.php │ └── StrikethroughRenderer.php ├── Table │ ├── Table.php │ ├── TableCell.php │ ├── TableCellRenderer.php │ ├── TableExtension.php │ ├── TableParser.php │ ├── TableRenderer.php │ ├── TableRow.php │ ├── TableRowRenderer.php │ ├── TableSection.php │ ├── TableSectionRenderer.php │ └── TableStartParser.php ├── TableOfContents │ ├── Node │ │ ├── TableOfContents.php │ │ └── TableOfContentsPlaceholder.php │ ├── Normalizer │ │ ├── AsIsNormalizerStrategy.php │ │ ├── FlatNormalizerStrategy.php │ │ ├── NormalizerStrategyInterface.php │ │ └── RelativeNormalizerStrategy.php │ ├── TableOfContentsBuilder.php │ ├── TableOfContentsExtension.php │ ├── TableOfContentsGenerator.php │ ├── TableOfContentsGeneratorInterface.php │ ├── TableOfContentsPlaceholderParser.php │ ├── TableOfContentsPlaceholderRenderer.php │ └── TableOfContentsRenderer.php └── TaskList │ ├── TaskListExtension.php │ ├── TaskListItemMarker.php │ ├── TaskListItemMarkerParser.php │ └── TaskListItemMarkerRenderer.php ├── GithubFlavoredMarkdownConverter.php ├── Input ├── MarkdownInput.php └── MarkdownInputInterface.php ├── MarkdownConverter.php ├── MarkdownConverterInterface.php ├── Node ├── Block │ ├── AbstractBlock.php │ ├── Document.php │ ├── Paragraph.php │ └── TightBlockInterface.php ├── Inline │ ├── AbstractInline.php │ ├── AbstractStringContainer.php │ ├── AdjacentTextMerger.php │ ├── DelimitedInterface.php │ ├── Newline.php │ └── Text.php ├── Node.php ├── NodeIterator.php ├── NodeWalker.php ├── NodeWalkerEvent.php ├── Query.php ├── Query │ ├── AndExpr.php │ ├── ExpressionInterface.php │ └── OrExpr.php ├── RawMarkupContainerInterface.php ├── StringContainerHelper.php └── StringContainerInterface.php ├── Normalizer ├── SlugNormalizer.php ├── TextNormalizer.php ├── TextNormalizerInterface.php ├── UniqueSlugNormalizer.php └── UniqueSlugNormalizerInterface.php ├── Output ├── RenderedContent.php └── RenderedContentInterface.php ├── Parser ├── Block │ ├── AbstractBlockContinueParser.php │ ├── BlockContinue.php │ ├── BlockContinueParserInterface.php │ ├── BlockContinueParserWithInlinesInterface.php │ ├── BlockStart.php │ ├── BlockStartParserInterface.php │ ├── DocumentBlockParser.php │ ├── ParagraphParser.php │ └── SkipLinesStartingWithLettersParser.php ├── Cursor.php ├── CursorState.php ├── Inline │ ├── InlineParserInterface.php │ ├── InlineParserMatch.php │ └── NewlineParser.php ├── InlineParserContext.php ├── InlineParserEngine.php ├── InlineParserEngineInterface.php ├── MarkdownParser.php ├── MarkdownParserInterface.php ├── MarkdownParserState.php ├── MarkdownParserStateInterface.php └── ParserLogicException.php ├── Reference ├── MemoryLimitedReferenceMap.php ├── Reference.php ├── ReferenceInterface.php ├── ReferenceMap.php ├── ReferenceMapInterface.php ├── ReferenceParser.php └── ReferenceableInterface.php ├── Renderer ├── Block │ ├── DocumentRenderer.php │ └── ParagraphRenderer.php ├── ChildNodeRendererInterface.php ├── DocumentRendererInterface.php ├── HtmlDecorator.php ├── HtmlRenderer.php ├── Inline │ ├── NewlineRenderer.php │ └── TextRenderer.php ├── MarkdownRendererInterface.php ├── NoMatchingRendererException.php └── NodeRendererInterface.php ├── Util ├── ArrayCollection.php ├── Html5EntityDecoder.php ├── HtmlElement.php ├── HtmlFilter.php ├── LinkParserHelper.php ├── PrioritizedList.php ├── RegexHelper.php ├── SpecReader.php ├── UrlEncoder.php └── Xml.php └── Xml ├── FallbackNodeXmlRenderer.php ├── MarkdownToXmlConverter.php ├── XmlNodeRendererInterface.php └── XmlRenderer.php /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2014-2022, Colin O'Dell. All rights reserved. Some code based on commonmark.js (copyright 2014-2018, John MacFarlane) and commonmark-java (copyright 2015-2016, Atlassian Pty Ltd) 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/CommonMarkConverter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark; 18 | 19 | use League\CommonMark\Environment\Environment; 20 | use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; 21 | 22 | /** 23 | * Converts CommonMark-compatible Markdown to HTML. 24 | */ 25 | final class CommonMarkConverter extends MarkdownConverter 26 | { 27 | /** 28 | * Create a new Markdown converter pre-configured for CommonMark 29 | * 30 | * @param array $config 31 | */ 32 | public function __construct(array $config = []) 33 | { 34 | $environment = new Environment($config); 35 | $environment->addExtension(new CommonMarkCoreExtension()); 36 | 37 | parent::__construct($environment); 38 | } 39 | 40 | public function getEnvironment(): Environment 41 | { 42 | \assert($this->environment instanceof Environment); 43 | 44 | return $this->environment; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ConverterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark; 15 | 16 | use League\CommonMark\Exception\CommonMarkException; 17 | use League\CommonMark\Output\RenderedContentInterface; 18 | use League\Config\Exception\ConfigurationExceptionInterface; 19 | 20 | /** 21 | * Interface for a service which converts content from one format (like Markdown) to another (like HTML). 22 | */ 23 | interface ConverterInterface 24 | { 25 | /** 26 | * @throws CommonMarkException 27 | * @throws ConfigurationExceptionInterface 28 | */ 29 | public function convert(string $input): RenderedContentInterface; 30 | } 31 | -------------------------------------------------------------------------------- /src/Delimiter/DelimiterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Delimiter; 18 | 19 | use League\CommonMark\Node\Inline\AbstractStringContainer; 20 | 21 | interface DelimiterInterface 22 | { 23 | public function canClose(): bool; 24 | 25 | public function canOpen(): bool; 26 | 27 | /** 28 | * @deprecated This method is no longer used internally and will be removed in 3.0 29 | */ 30 | public function isActive(): bool; 31 | 32 | /** 33 | * @deprecated This method is no longer used internally and will be removed in 3.0 34 | */ 35 | public function setActive(bool $active): void; 36 | 37 | public function getChar(): string; 38 | 39 | public function getIndex(): ?int; 40 | 41 | public function getNext(): ?DelimiterInterface; 42 | 43 | public function setNext(?DelimiterInterface $next): void; 44 | 45 | public function getLength(): int; 46 | 47 | public function setLength(int $length): void; 48 | 49 | public function getOriginalLength(): int; 50 | 51 | public function getInlineNode(): AbstractStringContainer; 52 | 53 | public function getPrevious(): ?DelimiterInterface; 54 | 55 | public function setPrevious(?DelimiterInterface $previous): void; 56 | } 57 | -------------------------------------------------------------------------------- /src/Delimiter/Processor/DelimiterProcessorCollectionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java) 14 | * - (c) Atlassian Pty Ltd 15 | * 16 | * For the full copyright and license information, please view the LICENSE 17 | * file that was distributed with this source code. 18 | */ 19 | 20 | namespace League\CommonMark\Delimiter\Processor; 21 | 22 | use League\CommonMark\Exception\InvalidArgumentException; 23 | 24 | interface DelimiterProcessorCollectionInterface extends \Countable 25 | { 26 | /** 27 | * Add the given delim processor to the collection 28 | * 29 | * @param DelimiterProcessorInterface $processor The delim processor to add 30 | * 31 | * @throws InvalidArgumentException Exception will be thrown if attempting to add multiple processors for the same character 32 | */ 33 | public function add(DelimiterProcessorInterface $processor): void; 34 | 35 | /** 36 | * Returns the delim processor which handles the given character if one exists 37 | */ 38 | public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface; 39 | 40 | /** 41 | * Returns an array of delimiter characters who have associated processors 42 | * 43 | * @return string[] 44 | */ 45 | public function getDelimiterCharacters(): array; 46 | } 47 | -------------------------------------------------------------------------------- /src/Environment/EnvironmentAwareInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Environment; 15 | 16 | interface EnvironmentAwareInterface 17 | { 18 | public function setEnvironment(EnvironmentInterface $environment): void; 19 | } 20 | -------------------------------------------------------------------------------- /src/Environment/EnvironmentInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Environment; 15 | 16 | use League\CommonMark\Delimiter\Processor\DelimiterProcessorCollection; 17 | use League\CommonMark\Extension\ExtensionInterface; 18 | use League\CommonMark\Node\Node; 19 | use League\CommonMark\Normalizer\TextNormalizerInterface; 20 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\Config\ConfigurationProviderInterface; 24 | use Psr\EventDispatcher\EventDispatcherInterface; 25 | 26 | interface EnvironmentInterface extends ConfigurationProviderInterface, EventDispatcherInterface 27 | { 28 | /** 29 | * Get all registered extensions 30 | * 31 | * @return ExtensionInterface[] 32 | */ 33 | public function getExtensions(): iterable; 34 | 35 | /** 36 | * @return iterable 37 | */ 38 | public function getBlockStartParsers(): iterable; 39 | 40 | /** 41 | * @return iterable 42 | */ 43 | public function getInlineParsers(): iterable; 44 | 45 | public function getDelimiterProcessors(): DelimiterProcessorCollection; 46 | 47 | /** 48 | * @psalm-param class-string $nodeClass 49 | * 50 | * @return iterable 51 | */ 52 | public function getRenderersForClass(string $nodeClass): iterable; 53 | 54 | public function getSlugNormalizer(): TextNormalizerInterface; 55 | } 56 | -------------------------------------------------------------------------------- /src/Event/AbstractEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the Symfony EventDispatcher "Event" contract 11 | * - (c) 2018-2019 Fabien Potencier 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Event; 18 | 19 | use Psr\EventDispatcher\StoppableEventInterface; 20 | 21 | /** 22 | * Base class for classes containing event data. 23 | * 24 | * This class contains no event data. It is used by events that do not pass 25 | * state information to an event handler when an event is raised. 26 | * 27 | * You can call the method stopPropagation() to abort the execution of 28 | * further listeners in your event listener. 29 | */ 30 | abstract class AbstractEvent implements StoppableEventInterface 31 | { 32 | /** @psalm-readonly-allow-private-mutation */ 33 | private bool $propagationStopped = false; 34 | 35 | /** 36 | * Returns whether further event listeners should be triggered. 37 | */ 38 | final public function isPropagationStopped(): bool 39 | { 40 | return $this->propagationStopped; 41 | } 42 | 43 | /** 44 | * Stops the propagation of the event to further event listeners. 45 | * 46 | * If multiple event listeners are connected to the same event, no 47 | * further event listener will be triggered once any trigger calls 48 | * stopPropagation(). 49 | */ 50 | final public function stopPropagation(): void 51 | { 52 | $this->propagationStopped = true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Event/DocumentParsedEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Event; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | 18 | /** 19 | * Event dispatched when the document has been fully parsed 20 | */ 21 | final class DocumentParsedEvent extends AbstractEvent 22 | { 23 | /** @psalm-readonly */ 24 | private Document $document; 25 | 26 | public function __construct(Document $document) 27 | { 28 | $this->document = $document; 29 | } 30 | 31 | public function getDocument(): Document 32 | { 33 | return $this->document; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Event/DocumentPreParsedEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Event; 15 | 16 | use League\CommonMark\Input\MarkdownInputInterface; 17 | use League\CommonMark\Node\Block\Document; 18 | 19 | /** 20 | * Event dispatched when the document is about to be parsed 21 | */ 22 | final class DocumentPreParsedEvent extends AbstractEvent 23 | { 24 | /** @psalm-readonly */ 25 | private Document $document; 26 | 27 | private MarkdownInputInterface $markdown; 28 | 29 | public function __construct(Document $document, MarkdownInputInterface $markdown) 30 | { 31 | $this->document = $document; 32 | $this->markdown = $markdown; 33 | } 34 | 35 | public function getDocument(): Document 36 | { 37 | return $this->document; 38 | } 39 | 40 | public function getMarkdown(): MarkdownInputInterface 41 | { 42 | return $this->markdown; 43 | } 44 | 45 | public function replaceMarkdown(MarkdownInputInterface $markdownInput): void 46 | { 47 | $this->markdown = $markdownInput; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Event/DocumentPreRenderEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Event; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | 18 | /** 19 | * Event dispatched just before rendering begins 20 | */ 21 | final class DocumentPreRenderEvent extends AbstractEvent 22 | { 23 | /** @psalm-readonly */ 24 | private Document $document; 25 | 26 | /** @psalm-readonly */ 27 | private string $format; 28 | 29 | public function __construct(Document $document, string $format) 30 | { 31 | $this->document = $document; 32 | $this->format = $format; 33 | } 34 | 35 | public function getDocument(): Document 36 | { 37 | return $this->document; 38 | } 39 | 40 | public function getFormat(): string 41 | { 42 | return $this->format; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Event/DocumentRenderedEvent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Event; 15 | 16 | use League\CommonMark\Output\RenderedContentInterface; 17 | 18 | final class DocumentRenderedEvent extends AbstractEvent 19 | { 20 | private RenderedContentInterface $output; 21 | 22 | public function __construct(RenderedContentInterface $output) 23 | { 24 | $this->output = $output; 25 | } 26 | 27 | /** 28 | * @psalm-mutation-free 29 | */ 30 | public function getOutput(): RenderedContentInterface 31 | { 32 | return $this->output; 33 | } 34 | 35 | /** 36 | * @psalm-external-mutation-free 37 | */ 38 | public function replaceOutput(RenderedContentInterface $output): void 39 | { 40 | $this->output = $output; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Event/ListenerData.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Event; 15 | 16 | /** 17 | * @internal 18 | * 19 | * @psalm-immutable 20 | */ 21 | final class ListenerData 22 | { 23 | /** @var class-string */ 24 | private string $event; 25 | 26 | /** @var callable */ 27 | private $listener; 28 | 29 | /** 30 | * @param class-string $event 31 | */ 32 | public function __construct(string $event, callable $listener) 33 | { 34 | $this->event = $event; 35 | $this->listener = $listener; 36 | } 37 | 38 | /** 39 | * @return class-string 40 | */ 41 | public function getEvent(): string 42 | { 43 | return $this->event; 44 | } 45 | 46 | public function getListener(): callable 47 | { 48 | return $this->listener; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Exception/AlreadyInitializedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | class AlreadyInitializedException extends LogicException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/CommonMarkException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | /** 17 | * Marker interface for all exceptions thrown by this library. 18 | */ 19 | interface CommonMarkException extends \Throwable 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Exception/IOException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | class IOException extends \RuntimeException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | class InvalidArgumentException extends \InvalidArgumentException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/LogicException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | class LogicException extends \LogicException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/MissingDependencyException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | class MissingDependencyException extends \RuntimeException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Exception/UnexpectedEncodingException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Exception; 15 | 16 | final class UnexpectedEncodingException extends \RuntimeException implements CommonMarkException 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /src/Extension/Attributes/AttributesExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2015 Martin Hasoň 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Attributes; 16 | 17 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 18 | use League\CommonMark\Event\DocumentParsedEvent; 19 | use League\CommonMark\Extension\Attributes\Event\AttributesListener; 20 | use League\CommonMark\Extension\Attributes\Parser\AttributesBlockStartParser; 21 | use League\CommonMark\Extension\Attributes\Parser\AttributesInlineParser; 22 | use League\CommonMark\Extension\ConfigurableExtensionInterface; 23 | use League\Config\ConfigurationBuilderInterface; 24 | use Nette\Schema\Expect; 25 | 26 | final class AttributesExtension implements ConfigurableExtensionInterface 27 | { 28 | public function configureSchema(ConfigurationBuilderInterface $builder): void 29 | { 30 | $builder->addSchema('attributes', Expect::structure([ 31 | 'allow' => Expect::arrayOf('string')->default([]), 32 | ])); 33 | } 34 | 35 | public function register(EnvironmentBuilderInterface $environment): void 36 | { 37 | $allowList = $environment->getConfiguration()->get('attributes.allow'); 38 | $allowUnsafeLinks = $environment->getConfiguration()->get('allow_unsafe_links'); 39 | 40 | $environment->addBlockStartParser(new AttributesBlockStartParser()); 41 | $environment->addInlineParser(new AttributesInlineParser()); 42 | $environment->addEventListener(DocumentParsedEvent::class, [new AttributesListener($allowList, $allowUnsafeLinks), 'processDocument']); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Extension/Attributes/Node/Attributes.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2015 Martin Hasoň 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Attributes\Node; 16 | 17 | use League\CommonMark\Node\Block\AbstractBlock; 18 | 19 | final class Attributes extends AbstractBlock 20 | { 21 | public const TARGET_PARENT = 0; 22 | public const TARGET_PREVIOUS = 1; 23 | public const TARGET_NEXT = 2; 24 | 25 | /** @var array */ 26 | private array $attributes; 27 | 28 | private int $target = self::TARGET_NEXT; 29 | 30 | /** 31 | * @param array $attributes 32 | */ 33 | public function __construct(array $attributes) 34 | { 35 | parent::__construct(); 36 | 37 | $this->attributes = $attributes; 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function getAttributes(): array 44 | { 45 | return $this->attributes; 46 | } 47 | 48 | /** 49 | * @param array $attributes 50 | */ 51 | public function setAttributes(array $attributes): void 52 | { 53 | $this->attributes = $attributes; 54 | } 55 | 56 | public function getTarget(): int 57 | { 58 | return $this->target; 59 | } 60 | 61 | public function setTarget(int $target): void 62 | { 63 | $this->target = $target; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Extension/Attributes/Node/AttributesInline.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2015 Martin Hasoň 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Attributes\Node; 16 | 17 | use League\CommonMark\Node\Inline\AbstractInline; 18 | 19 | final class AttributesInline extends AbstractInline 20 | { 21 | /** @var array */ 22 | private array $attributes; 23 | 24 | private bool $block; 25 | 26 | /** 27 | * @param array $attributes 28 | */ 29 | public function __construct(array $attributes, bool $block) 30 | { 31 | parent::__construct(); 32 | 33 | $this->attributes = $attributes; 34 | $this->block = $block; 35 | } 36 | 37 | /** 38 | * @return array 39 | */ 40 | public function getAttributes(): array 41 | { 42 | return $this->attributes; 43 | } 44 | 45 | /** 46 | * @param array $attributes 47 | */ 48 | public function setAttributes(array $attributes): void 49 | { 50 | $this->attributes = $attributes; 51 | } 52 | 53 | public function isBlock(): bool 54 | { 55 | return $this->block; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Extension/Attributes/Parser/AttributesBlockStartParser.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2015 Martin Hasoň 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Attributes\Parser; 16 | 17 | use League\CommonMark\Extension\Attributes\Util\AttributesHelper; 18 | use League\CommonMark\Parser\Block\BlockStart; 19 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 20 | use League\CommonMark\Parser\Cursor; 21 | use League\CommonMark\Parser\MarkdownParserStateInterface; 22 | 23 | final class AttributesBlockStartParser implements BlockStartParserInterface 24 | { 25 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 26 | { 27 | $originalPosition = $cursor->getPosition(); 28 | $attributes = AttributesHelper::parseAttributes($cursor); 29 | 30 | if ($attributes === [] && $originalPosition === $cursor->getPosition()) { 31 | return BlockStart::none(); 32 | } 33 | 34 | if ($cursor->getNextNonSpaceCharacter() !== null) { 35 | return BlockStart::none(); 36 | } 37 | 38 | return BlockStart::of(new AttributesBlockContinueParser($attributes, $parserState->getActiveBlockParser()->getBlock()))->at($cursor); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extension/Attributes/Parser/AttributesInlineParser.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) 2015 Martin Hasoň 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Attributes\Parser; 16 | 17 | use League\CommonMark\Extension\Attributes\Node\AttributesInline; 18 | use League\CommonMark\Extension\Attributes\Util\AttributesHelper; 19 | use League\CommonMark\Node\StringContainerInterface; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | 24 | final class AttributesInlineParser implements InlineParserInterface 25 | { 26 | public function getMatchDefinition(): InlineParserMatch 27 | { 28 | return InlineParserMatch::string('{'); 29 | } 30 | 31 | public function parse(InlineParserContext $inlineContext): bool 32 | { 33 | $cursor = $inlineContext->getCursor(); 34 | $char = (string) $cursor->peek(-1); 35 | 36 | $attributes = AttributesHelper::parseAttributes($cursor); 37 | if ($attributes === []) { 38 | return false; 39 | } 40 | 41 | if ($char === ' ' && ($prev = $inlineContext->getContainer()->lastChild()) instanceof StringContainerInterface) { 42 | $prev->setLiteral(\rtrim($prev->getLiteral(), ' ')); 43 | } 44 | 45 | if ($char === '') { 46 | $cursor->advanceToNextNonSpaceOrNewline(); 47 | } 48 | 49 | $node = new AttributesInline($attributes, $char === ' ' || $char === ''); 50 | $inlineContext->getContainer()->appendChild($node); 51 | 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Extension/Autolink/AutolinkExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Autolink; 15 | 16 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 17 | use League\CommonMark\Extension\ConfigurableExtensionInterface; 18 | use League\Config\ConfigurationBuilderInterface; 19 | use Nette\Schema\Expect; 20 | 21 | final class AutolinkExtension implements ConfigurableExtensionInterface 22 | { 23 | public function configureSchema(ConfigurationBuilderInterface $builder): void 24 | { 25 | $builder->addSchema('autolink', Expect::structure([ 26 | 'allowed_protocols' => Expect::listOf('string')->default(['http', 'https', 'ftp'])->mergeDefaults(false), 27 | 'default_protocol' => Expect::string()->default('http'), 28 | ])); 29 | } 30 | 31 | public function register(EnvironmentBuilderInterface $environment): void 32 | { 33 | $environment->addInlineParser(new EmailAutolinkParser()); 34 | $environment->addInlineParser(new UrlAutolinkParser( 35 | $environment->getConfiguration()->get('autolink.allowed_protocols'), 36 | $environment->getConfiguration()->get('autolink.default_protocol'), 37 | )); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/Autolink/EmailAutolinkParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Autolink; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Inline\Link; 17 | use League\CommonMark\Parser\Inline\InlineParserInterface; 18 | use League\CommonMark\Parser\Inline\InlineParserMatch; 19 | use League\CommonMark\Parser\InlineParserContext; 20 | 21 | final class EmailAutolinkParser implements InlineParserInterface 22 | { 23 | private const REGEX = '[A-Za-z0-9.\-_+]+@[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_.]+'; 24 | 25 | public function getMatchDefinition(): InlineParserMatch 26 | { 27 | return InlineParserMatch::regex(self::REGEX); 28 | } 29 | 30 | public function parse(InlineParserContext $inlineContext): bool 31 | { 32 | $email = $inlineContext->getFullMatch(); 33 | // The last character cannot be - or _ 34 | if (\in_array(\substr($email, -1), ['-', '_'], true)) { 35 | return false; 36 | } 37 | 38 | // Does the URL end with punctuation that should be stripped? 39 | if (\substr($email, -1) === '.') { 40 | $email = \substr($email, 0, -1); 41 | } 42 | 43 | $inlineContext->getCursor()->advanceBy(\strlen($email)); 44 | $inlineContext->getContainer()->appendChild(new Link('mailto:' . $email, $email)); 45 | 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/BlockQuote.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | class BlockQuote extends AbstractBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/Heading.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 18 | 19 | use League\CommonMark\Node\Block\AbstractBlock; 20 | 21 | final class Heading extends AbstractBlock 22 | { 23 | private int $level; 24 | 25 | public function __construct(int $level) 26 | { 27 | parent::__construct(); 28 | 29 | $this->level = $level; 30 | } 31 | 32 | public function getLevel(): int 33 | { 34 | return $this->level; 35 | } 36 | 37 | public function setLevel(int $level): void 38 | { 39 | $this->level = $level; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/IndentedCode.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | use League\CommonMark\Node\StringContainerInterface; 18 | 19 | final class IndentedCode extends AbstractBlock implements StringContainerInterface 20 | { 21 | private string $literal = ''; 22 | 23 | public function getLiteral(): string 24 | { 25 | return $this->literal; 26 | } 27 | 28 | public function setLiteral(string $literal): void 29 | { 30 | $this->literal = $literal; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/ListBlock.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 18 | 19 | use League\CommonMark\Node\Block\AbstractBlock; 20 | use League\CommonMark\Node\Block\TightBlockInterface; 21 | 22 | class ListBlock extends AbstractBlock implements TightBlockInterface 23 | { 24 | public const TYPE_BULLET = 'bullet'; 25 | public const TYPE_ORDERED = 'ordered'; 26 | 27 | public const DELIM_PERIOD = 'period'; 28 | public const DELIM_PAREN = 'paren'; 29 | 30 | protected bool $tight = false; // TODO Make lists tight by default in v3 31 | 32 | /** @psalm-readonly */ 33 | protected ListData $listData; 34 | 35 | public function __construct(ListData $listData) 36 | { 37 | parent::__construct(); 38 | 39 | $this->listData = $listData; 40 | } 41 | 42 | public function getListData(): ListData 43 | { 44 | return $this->listData; 45 | } 46 | 47 | public function isTight(): bool 48 | { 49 | return $this->tight; 50 | } 51 | 52 | public function setTight(bool $tight): void 53 | { 54 | $this->tight = $tight; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/ListData.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 18 | 19 | class ListData 20 | { 21 | public ?int $start = null; 22 | 23 | public int $padding = 0; 24 | 25 | /** 26 | * @psalm-var ListBlock::TYPE_* 27 | * @phpstan-var ListBlock::TYPE_* 28 | */ 29 | public string $type; 30 | 31 | /** 32 | * @psalm-var ListBlock::DELIM_*|null 33 | * @phpstan-var ListBlock::DELIM_*|null 34 | */ 35 | public ?string $delimiter = null; 36 | 37 | public ?string $bulletChar = null; 38 | 39 | public int $markerOffset; 40 | 41 | public function equals(ListData $data): bool 42 | { 43 | return $this->type === $data->type && 44 | $this->delimiter === $data->delimiter && 45 | $this->bulletChar === $data->bulletChar; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/ListItem.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 18 | 19 | use League\CommonMark\Node\Block\AbstractBlock; 20 | 21 | class ListItem extends AbstractBlock 22 | { 23 | /** @psalm-readonly */ 24 | protected ListData $listData; 25 | 26 | public function __construct(ListData $listData) 27 | { 28 | parent::__construct(); 29 | 30 | $this->listData = $listData; 31 | } 32 | 33 | public function getListData(): ListData 34 | { 35 | return $this->listData; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Block/ThematicBreak.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Node\Block; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | class ThematicBreak extends AbstractBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/AbstractWebResource.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\AbstractInline; 20 | 21 | abstract class AbstractWebResource extends AbstractInline 22 | { 23 | protected string $url; 24 | 25 | public function __construct(string $url) 26 | { 27 | parent::__construct(); 28 | 29 | $this->url = $url; 30 | } 31 | 32 | public function getUrl(): string 33 | { 34 | return $this->url; 35 | } 36 | 37 | public function setUrl(string $url): void 38 | { 39 | $this->url = $url; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/Code.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\AbstractStringContainer; 20 | 21 | class Code extends AbstractStringContainer 22 | { 23 | } 24 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/Emphasis.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\AbstractInline; 20 | use League\CommonMark\Node\Inline\DelimitedInterface; 21 | 22 | final class Emphasis extends AbstractInline implements DelimitedInterface 23 | { 24 | private string $delimiter; 25 | 26 | public function __construct(string $delimiter = '_') 27 | { 28 | parent::__construct(); 29 | 30 | $this->delimiter = $delimiter; 31 | } 32 | 33 | public function getOpeningDelimiter(): string 34 | { 35 | return $this->delimiter; 36 | } 37 | 38 | public function getClosingDelimiter(): string 39 | { 40 | return $this->delimiter; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/HtmlInline.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\AbstractStringContainer; 20 | use League\CommonMark\Node\RawMarkupContainerInterface; 21 | 22 | final class HtmlInline extends AbstractStringContainer implements RawMarkupContainerInterface 23 | { 24 | } 25 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/Image.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | 21 | class Image extends AbstractWebResource 22 | { 23 | protected ?string $title = null; 24 | 25 | public function __construct(string $url, ?string $label = null, ?string $title = null) 26 | { 27 | parent::__construct($url); 28 | 29 | if ($label !== null && $label !== '') { 30 | $this->appendChild(new Text($label)); 31 | } 32 | 33 | $this->title = $title; 34 | } 35 | 36 | public function getTitle(): ?string 37 | { 38 | if ($this->title === '') { 39 | return null; 40 | } 41 | 42 | return $this->title; 43 | } 44 | 45 | public function setTitle(?string $title): void 46 | { 47 | $this->title = $title; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/Link.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | 21 | class Link extends AbstractWebResource 22 | { 23 | protected ?string $title = null; 24 | 25 | public function __construct(string $url, ?string $label = null, ?string $title = null) 26 | { 27 | parent::__construct($url); 28 | 29 | if ($label !== null && $label !== '') { 30 | $this->appendChild(new Text($label)); 31 | } 32 | 33 | $this->title = $title; 34 | } 35 | 36 | public function getTitle(): ?string 37 | { 38 | if ($this->title === '') { 39 | return null; 40 | } 41 | 42 | return $this->title; 43 | } 44 | 45 | public function setTitle(?string $title): void 46 | { 47 | $this->title = $title; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Node/Inline/Strong.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Inline\AbstractInline; 20 | use League\CommonMark\Node\Inline\DelimitedInterface; 21 | 22 | final class Strong extends AbstractInline implements DelimitedInterface 23 | { 24 | private string $delimiter; 25 | 26 | public function __construct(string $delimiter = '**') 27 | { 28 | parent::__construct(); 29 | 30 | $this->delimiter = $delimiter; 31 | } 32 | 33 | public function getOpeningDelimiter(): string 34 | { 35 | return $this->delimiter; 36 | } 37 | 38 | public function getClosingDelimiter(): string 39 | { 40 | return $this->delimiter; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/BlockQuoteParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote; 17 | use League\CommonMark\Node\Block\AbstractBlock; 18 | use League\CommonMark\Parser\Block\AbstractBlockContinueParser; 19 | use League\CommonMark\Parser\Block\BlockContinue; 20 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 21 | use League\CommonMark\Parser\Cursor; 22 | 23 | final class BlockQuoteParser extends AbstractBlockContinueParser 24 | { 25 | /** @psalm-readonly */ 26 | private BlockQuote $block; 27 | 28 | public function __construct() 29 | { 30 | $this->block = new BlockQuote(); 31 | } 32 | 33 | public function getBlock(): BlockQuote 34 | { 35 | return $this->block; 36 | } 37 | 38 | public function isContainer(): bool 39 | { 40 | return true; 41 | } 42 | 43 | public function canContain(AbstractBlock $childBlock): bool 44 | { 45 | return true; 46 | } 47 | 48 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 49 | { 50 | if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === '>') { 51 | $cursor->advanceToNextNonSpaceOrTab(); 52 | $cursor->advanceBy(1); 53 | $cursor->advanceBySpaceOrTab(); 54 | 55 | return BlockContinue::at($cursor); 56 | } 57 | 58 | return BlockContinue::none(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/BlockQuoteStartParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Block\BlockStart; 17 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 18 | use League\CommonMark\Parser\Cursor; 19 | use League\CommonMark\Parser\MarkdownParserStateInterface; 20 | 21 | final class BlockQuoteStartParser implements BlockStartParserInterface 22 | { 23 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 24 | { 25 | if ($cursor->isIndented()) { 26 | return BlockStart::none(); 27 | } 28 | 29 | if ($cursor->getNextNonSpaceCharacter() !== '>') { 30 | return BlockStart::none(); 31 | } 32 | 33 | $cursor->advanceToNextNonSpaceOrTab(); 34 | $cursor->advanceBy(1); 35 | $cursor->advanceBySpaceOrTab(); 36 | 37 | return BlockStart::of(new BlockQuoteParser())->at($cursor); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/FencedCodeStartParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Block\BlockStart; 17 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 18 | use League\CommonMark\Parser\Cursor; 19 | use League\CommonMark\Parser\MarkdownParserStateInterface; 20 | 21 | final class FencedCodeStartParser implements BlockStartParserInterface 22 | { 23 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 24 | { 25 | if ($cursor->isIndented() || ! \in_array($cursor->getNextNonSpaceCharacter(), ['`', '~'], true)) { 26 | return BlockStart::none(); 27 | } 28 | 29 | $indent = $cursor->getIndent(); 30 | $fence = $cursor->match('/^[ \t]*(?:`{3,}(?!.*`)|~{3,})/'); 31 | if ($fence === null) { 32 | return BlockStart::none(); 33 | } 34 | 35 | // fenced code block 36 | $fence = \ltrim($fence, " \t"); 37 | 38 | return BlockStart::of(new FencedCodeParser(\strlen($fence), $fence[0], $indent))->at($cursor); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/HeadingParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\Heading; 17 | use League\CommonMark\Parser\Block\AbstractBlockContinueParser; 18 | use League\CommonMark\Parser\Block\BlockContinue; 19 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 20 | use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface; 21 | use League\CommonMark\Parser\Cursor; 22 | use League\CommonMark\Parser\InlineParserEngineInterface; 23 | 24 | final class HeadingParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface 25 | { 26 | /** @psalm-readonly */ 27 | private Heading $block; 28 | 29 | private string $content; 30 | 31 | public function __construct(int $level, string $content) 32 | { 33 | $this->block = new Heading($level); 34 | $this->content = $content; 35 | } 36 | 37 | public function getBlock(): Heading 38 | { 39 | return $this->block; 40 | } 41 | 42 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 43 | { 44 | return BlockContinue::none(); 45 | } 46 | 47 | public function parseInlines(InlineParserEngineInterface $inlineParser): void 48 | { 49 | $inlineParser->parse($this->content, $this->block); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/IndentedCodeStartParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Node\Block\Paragraph; 17 | use League\CommonMark\Parser\Block\BlockStart; 18 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 19 | use League\CommonMark\Parser\Cursor; 20 | use League\CommonMark\Parser\MarkdownParserStateInterface; 21 | 22 | final class IndentedCodeStartParser implements BlockStartParserInterface 23 | { 24 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 25 | { 26 | if (! $cursor->isIndented()) { 27 | return BlockStart::none(); 28 | } 29 | 30 | if ($parserState->getActiveBlockParser()->getBlock() instanceof Paragraph) { 31 | return BlockStart::none(); 32 | } 33 | 34 | if ($cursor->isBlank()) { 35 | return BlockStart::none(); 36 | } 37 | 38 | $cursor->advanceBy(Cursor::INDENT_LEVEL, true); 39 | 40 | return BlockStart::of(new IndentedCodeParser())->at($cursor); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/ThematicBreakParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak; 17 | use League\CommonMark\Parser\Block\AbstractBlockContinueParser; 18 | use League\CommonMark\Parser\Block\BlockContinue; 19 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 20 | use League\CommonMark\Parser\Cursor; 21 | 22 | final class ThematicBreakParser extends AbstractBlockContinueParser 23 | { 24 | /** @psalm-readonly */ 25 | private ThematicBreak $block; 26 | 27 | public function __construct() 28 | { 29 | $this->block = new ThematicBreak(); 30 | } 31 | 32 | public function getBlock(): ThematicBreak 33 | { 34 | return $this->block; 35 | } 36 | 37 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 38 | { 39 | // a horizontal rule can never container > 1 line, so fail to match 40 | return BlockContinue::none(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Block/ThematicBreakStartParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Block\BlockStart; 17 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 18 | use League\CommonMark\Parser\Cursor; 19 | use League\CommonMark\Parser\MarkdownParserStateInterface; 20 | use League\CommonMark\Util\RegexHelper; 21 | 22 | final class ThematicBreakStartParser implements BlockStartParserInterface 23 | { 24 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 25 | { 26 | if ($cursor->isIndented()) { 27 | return BlockStart::none(); 28 | } 29 | 30 | $match = RegexHelper::matchAt(RegexHelper::REGEX_THEMATIC_BREAK, $cursor->getLine(), $cursor->getNextNonSpacePosition()); 31 | if ($match === null) { 32 | return BlockStart::none(); 33 | } 34 | 35 | // Advance to the end of the string, consuming the entire line (of the thematic break) 36 | $cursor->advanceToEnd(); 37 | 38 | return BlockStart::of(new ThematicBreakParser())->at($cursor); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Inline/BangParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | 24 | final class BangParser implements InlineParserInterface 25 | { 26 | public function getMatchDefinition(): InlineParserMatch 27 | { 28 | return InlineParserMatch::string('!['); 29 | } 30 | 31 | public function parse(InlineParserContext $inlineContext): bool 32 | { 33 | $cursor = $inlineContext->getCursor(); 34 | $cursor->advanceBy(2); 35 | 36 | $node = new Text('![', ['delim' => true]); 37 | $inlineContext->getContainer()->appendChild($node); 38 | 39 | // Add entry to stack for this opener 40 | $inlineContext->getDelimiterStack()->addBracket($node, $cursor->getPosition(), true); 41 | 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Inline/EntityParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | use League\CommonMark\Util\Html5EntityDecoder; 24 | use League\CommonMark\Util\RegexHelper; 25 | 26 | final class EntityParser implements InlineParserInterface 27 | { 28 | public function getMatchDefinition(): InlineParserMatch 29 | { 30 | return InlineParserMatch::regex(RegexHelper::PARTIAL_ENTITY); 31 | } 32 | 33 | public function parse(InlineParserContext $inlineContext): bool 34 | { 35 | $entity = $inlineContext->getFullMatch(); 36 | 37 | $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength()); 38 | $inlineContext->getContainer()->appendChild(new Text(Html5EntityDecoder::decode($entity))); 39 | 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Inline/EscapableParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Newline; 20 | use League\CommonMark\Node\Inline\Text; 21 | use League\CommonMark\Parser\Inline\InlineParserInterface; 22 | use League\CommonMark\Parser\Inline\InlineParserMatch; 23 | use League\CommonMark\Parser\InlineParserContext; 24 | use League\CommonMark\Util\RegexHelper; 25 | 26 | final class EscapableParser implements InlineParserInterface 27 | { 28 | public function getMatchDefinition(): InlineParserMatch 29 | { 30 | return InlineParserMatch::string('\\'); 31 | } 32 | 33 | public function parse(InlineParserContext $inlineContext): bool 34 | { 35 | $cursor = $inlineContext->getCursor(); 36 | $nextChar = $cursor->peek(); 37 | 38 | if ($nextChar === "\n") { 39 | $cursor->advanceBy(2); 40 | $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK)); 41 | 42 | return true; 43 | } 44 | 45 | if ($nextChar !== null && RegexHelper::isEscapable($nextChar)) { 46 | $cursor->advanceBy(2); 47 | $inlineContext->getContainer()->appendChild(new Text($nextChar)); 48 | 49 | return true; 50 | } 51 | 52 | $cursor->advanceBy(1); 53 | $inlineContext->getContainer()->appendChild(new Text('\\')); 54 | 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Inline/HtmlInlineParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | use League\CommonMark\Util\RegexHelper; 24 | 25 | final class HtmlInlineParser implements InlineParserInterface 26 | { 27 | public function getMatchDefinition(): InlineParserMatch 28 | { 29 | return InlineParserMatch::regex(RegexHelper::PARTIAL_HTMLTAG)->caseSensitive(); 30 | } 31 | 32 | public function parse(InlineParserContext $inlineContext): bool 33 | { 34 | $inline = $inlineContext->getFullMatch(); 35 | 36 | $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength()); 37 | $inlineContext->getContainer()->appendChild(new HtmlInline($inline)); 38 | 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Parser/Inline/OpenBracketParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | 24 | final class OpenBracketParser implements InlineParserInterface 25 | { 26 | public function getMatchDefinition(): InlineParserMatch 27 | { 28 | return InlineParserMatch::string('['); 29 | } 30 | 31 | public function parse(InlineParserContext $inlineContext): bool 32 | { 33 | $inlineContext->getCursor()->advanceBy(1); 34 | $node = new Text('[', ['delim' => true]); 35 | $inlineContext->getContainer()->appendChild($node); 36 | 37 | // Add entry to stack for this opener 38 | $inlineContext->getDelimiterStack()->addBracket($node, $inlineContext->getCursor()->getPosition(), false); 39 | 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Renderer/Block/IndentedCodeRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Renderer\Block; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\HtmlElement; 24 | use League\CommonMark\Util\Xml; 25 | use League\CommonMark\Xml\XmlNodeRendererInterface; 26 | 27 | final class IndentedCodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface 28 | { 29 | /** 30 | * @param IndentedCode $node 31 | * 32 | * {@inheritDoc} 33 | * 34 | * @psalm-suppress MoreSpecificImplementedParamType 35 | */ 36 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 37 | { 38 | IndentedCode::assertInstanceOf($node); 39 | 40 | $attrs = $node->data->get('attributes'); 41 | 42 | return new HtmlElement( 43 | 'pre', 44 | [], 45 | new HtmlElement('code', $attrs, Xml::escape($node->getLiteral())) 46 | ); 47 | } 48 | 49 | public function getXmlTagName(Node $node): string 50 | { 51 | return 'code_block'; 52 | } 53 | 54 | /** 55 | * @return array 56 | */ 57 | public function getXmlAttributes(Node $node): array 58 | { 59 | return []; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Renderer/Block/ThematicBreakRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Renderer\Block; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\HtmlElement; 24 | use League\CommonMark\Xml\XmlNodeRendererInterface; 25 | 26 | final class ThematicBreakRenderer implements NodeRendererInterface, XmlNodeRendererInterface 27 | { 28 | /** 29 | * @param ThematicBreak $node 30 | * 31 | * {@inheritDoc} 32 | * 33 | * @psalm-suppress MoreSpecificImplementedParamType 34 | */ 35 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 36 | { 37 | ThematicBreak::assertInstanceOf($node); 38 | 39 | $attrs = $node->data->get('attributes'); 40 | 41 | return new HtmlElement('hr', $attrs, '', true); 42 | } 43 | 44 | public function getXmlTagName(Node $node): string 45 | { 46 | return 'thematic_break'; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | public function getXmlAttributes(Node $node): array 53 | { 54 | return []; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Renderer/Inline/CodeRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Renderer\Inline; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Inline\Code; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\HtmlElement; 24 | use League\CommonMark\Util\Xml; 25 | use League\CommonMark\Xml\XmlNodeRendererInterface; 26 | 27 | final class CodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface 28 | { 29 | /** 30 | * @param Code $node 31 | * 32 | * {@inheritDoc} 33 | * 34 | * @psalm-suppress MoreSpecificImplementedParamType 35 | */ 36 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 37 | { 38 | Code::assertInstanceOf($node); 39 | 40 | $attrs = $node->data->get('attributes'); 41 | 42 | return new HtmlElement('code', $attrs, Xml::escape($node->getLiteral())); 43 | } 44 | 45 | public function getXmlTagName(Node $node): string 46 | { 47 | return 'code'; 48 | } 49 | 50 | /** 51 | * {@inheritDoc} 52 | */ 53 | public function getXmlAttributes(Node $node): array 54 | { 55 | return []; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Renderer/Inline/EmphasisRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Renderer\Inline; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\HtmlElement; 24 | use League\CommonMark\Xml\XmlNodeRendererInterface; 25 | 26 | final class EmphasisRenderer implements NodeRendererInterface, XmlNodeRendererInterface 27 | { 28 | /** 29 | * @param Emphasis $node 30 | * 31 | * {@inheritDoc} 32 | * 33 | * @psalm-suppress MoreSpecificImplementedParamType 34 | */ 35 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 36 | { 37 | Emphasis::assertInstanceOf($node); 38 | 39 | $attrs = $node->data->get('attributes'); 40 | 41 | return new HtmlElement('em', $attrs, $childRenderer->renderNodes($node->children())); 42 | } 43 | 44 | public function getXmlTagName(Node $node): string 45 | { 46 | return 'emph'; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | public function getXmlAttributes(Node $node): array 53 | { 54 | return []; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/CommonMark/Renderer/Inline/StrongRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\CommonMark\Renderer\Inline; 18 | 19 | use League\CommonMark\Extension\CommonMark\Node\Inline\Strong; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\HtmlElement; 24 | use League\CommonMark\Xml\XmlNodeRendererInterface; 25 | 26 | final class StrongRenderer implements NodeRendererInterface, XmlNodeRendererInterface 27 | { 28 | /** 29 | * @param Strong $node 30 | * 31 | * {@inheritDoc} 32 | * 33 | * @psalm-suppress MoreSpecificImplementedParamType 34 | */ 35 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 36 | { 37 | Strong::assertInstanceOf($node); 38 | 39 | $attrs = $node->data->get('attributes'); 40 | 41 | return new HtmlElement('strong', $attrs, $childRenderer->renderNodes($node->children())); 42 | } 43 | 44 | public function getXmlTagName(Node $node): string 45 | { 46 | return 'strong'; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | public function getXmlAttributes(Node $node): array 53 | { 54 | return []; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/ConfigurableExtensionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension; 15 | 16 | use League\Config\ConfigurationBuilderInterface; 17 | 18 | interface ConfigurableExtensionInterface extends ExtensionInterface 19 | { 20 | public function configureSchema(ConfigurationBuilderInterface $builder): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Extension/DefaultAttributes/DefaultAttributesExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DefaultAttributes; 15 | 16 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 17 | use League\CommonMark\Event\DocumentParsedEvent; 18 | use League\CommonMark\Extension\ConfigurableExtensionInterface; 19 | use League\Config\ConfigurationBuilderInterface; 20 | use Nette\Schema\Expect; 21 | 22 | final class DefaultAttributesExtension implements ConfigurableExtensionInterface 23 | { 24 | public function configureSchema(ConfigurationBuilderInterface $builder): void 25 | { 26 | $builder->addSchema('default_attributes', Expect::arrayOf( 27 | Expect::arrayOf( 28 | Expect::type('string|string[]|bool|callable'), // attribute value(s) 29 | 'string' // attribute name 30 | ), 31 | 'string' // node FQCN 32 | )->default([])); 33 | } 34 | 35 | public function register(EnvironmentBuilderInterface $environment): void 36 | { 37 | $environment->addEventListener(DocumentParsedEvent::class, [new ApplyDefaultAttributesProcessor(), 'onDocumentParsed']); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Event/ConsecutiveDescriptionListMerger.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Event; 15 | 16 | use League\CommonMark\Event\DocumentParsedEvent; 17 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionList; 18 | use League\CommonMark\Node\NodeIterator; 19 | 20 | final class ConsecutiveDescriptionListMerger 21 | { 22 | public function __invoke(DocumentParsedEvent $event): void 23 | { 24 | foreach ($event->getDocument()->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) { 25 | if (! $node instanceof DescriptionList) { 26 | continue; 27 | } 28 | 29 | if (! ($prev = $node->previous()) instanceof DescriptionList) { 30 | continue; 31 | } 32 | 33 | // There's another description list behind this one; merge the current one into that 34 | foreach ($node->children() as $child) { 35 | $prev->appendChild($child); 36 | } 37 | 38 | $node->detach(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Node/Description.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Node; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | use League\CommonMark\Node\Block\TightBlockInterface; 18 | 19 | class Description extends AbstractBlock implements TightBlockInterface 20 | { 21 | private bool $tight; 22 | 23 | public function __construct(bool $tight = false) 24 | { 25 | parent::__construct(); 26 | 27 | $this->tight = $tight; 28 | } 29 | 30 | public function isTight(): bool 31 | { 32 | return $this->tight; 33 | } 34 | 35 | public function setTight(bool $tight): void 36 | { 37 | $this->tight = $tight; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Node/DescriptionList.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Node; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | class DescriptionList extends AbstractBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Node/DescriptionTerm.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Node; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | class DescriptionTerm extends AbstractBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Parser/DescriptionListContinueParser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Parser; 15 | 16 | use League\CommonMark\Extension\DescriptionList\Node\Description; 17 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionList; 18 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm; 19 | use League\CommonMark\Node\Block\AbstractBlock; 20 | use League\CommonMark\Parser\Block\AbstractBlockContinueParser; 21 | use League\CommonMark\Parser\Block\BlockContinue; 22 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 23 | use League\CommonMark\Parser\Cursor; 24 | 25 | final class DescriptionListContinueParser extends AbstractBlockContinueParser 26 | { 27 | private DescriptionList $block; 28 | 29 | public function __construct() 30 | { 31 | $this->block = new DescriptionList(); 32 | } 33 | 34 | public function getBlock(): DescriptionList 35 | { 36 | return $this->block; 37 | } 38 | 39 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 40 | { 41 | return BlockContinue::at($cursor); 42 | } 43 | 44 | public function isContainer(): bool 45 | { 46 | return true; 47 | } 48 | 49 | public function canContain(AbstractBlock $childBlock): bool 50 | { 51 | return $childBlock instanceof DescriptionTerm || $childBlock instanceof Description; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Parser/DescriptionTermContinueParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Parser; 15 | 16 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm; 17 | use League\CommonMark\Parser\Block\AbstractBlockContinueParser; 18 | use League\CommonMark\Parser\Block\BlockContinue; 19 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 20 | use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface; 21 | use League\CommonMark\Parser\Cursor; 22 | use League\CommonMark\Parser\InlineParserEngineInterface; 23 | 24 | final class DescriptionTermContinueParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface 25 | { 26 | private DescriptionTerm $block; 27 | 28 | private string $term; 29 | 30 | public function __construct(string $term) 31 | { 32 | $this->block = new DescriptionTerm(); 33 | $this->term = $term; 34 | } 35 | 36 | public function getBlock(): DescriptionTerm 37 | { 38 | return $this->block; 39 | } 40 | 41 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 42 | { 43 | return BlockContinue::finished(); 44 | } 45 | 46 | public function parseInlines(InlineParserEngineInterface $inlineParser): void 47 | { 48 | if ($this->term !== '') { 49 | $inlineParser->parse($this->term, $this->block); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Renderer/DescriptionListRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Renderer; 15 | 16 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionList; 17 | use League\CommonMark\Node\Node; 18 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 19 | use League\CommonMark\Renderer\NodeRendererInterface; 20 | use League\CommonMark\Util\HtmlElement; 21 | 22 | final class DescriptionListRenderer implements NodeRendererInterface 23 | { 24 | /** 25 | * @param DescriptionList $node 26 | * 27 | * {@inheritDoc} 28 | * 29 | * @psalm-suppress MoreSpecificImplementedParamType 30 | */ 31 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): HtmlElement 32 | { 33 | DescriptionList::assertInstanceOf($node); 34 | 35 | $separator = $childRenderer->getBlockSeparator(); 36 | 37 | return new HtmlElement('dl', [], $separator . $childRenderer->renderNodes($node->children()) . $separator); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Renderer/DescriptionRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Renderer; 15 | 16 | use League\CommonMark\Extension\DescriptionList\Node\Description; 17 | use League\CommonMark\Node\Node; 18 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 19 | use League\CommonMark\Renderer\NodeRendererInterface; 20 | use League\CommonMark\Util\HtmlElement; 21 | 22 | final class DescriptionRenderer implements NodeRendererInterface 23 | { 24 | /** 25 | * @param Description $node 26 | * 27 | * {@inheritDoc} 28 | * 29 | * @psalm-suppress MoreSpecificImplementedParamType 30 | */ 31 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 32 | { 33 | Description::assertInstanceOf($node); 34 | 35 | return new HtmlElement('dd', [], $childRenderer->renderNodes($node->children())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/DescriptionList/Renderer/DescriptionTermRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\DescriptionList\Renderer; 15 | 16 | use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm; 17 | use League\CommonMark\Node\Node; 18 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 19 | use League\CommonMark\Renderer\NodeRendererInterface; 20 | use League\CommonMark\Util\HtmlElement; 21 | 22 | final class DescriptionTermRenderer implements NodeRendererInterface 23 | { 24 | /** 25 | * @param DescriptionTerm $node 26 | * 27 | * {@inheritDoc} 28 | * 29 | * @psalm-suppress MoreSpecificImplementedParamType 30 | */ 31 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 32 | { 33 | DescriptionTerm::assertInstanceOf($node); 34 | 35 | return new HtmlElement('dt', [], $childRenderer->renderNodes($node->children())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/Embed/Bridge/OscaroteroEmbedAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed\Bridge; 15 | 16 | use Embed\Embed as EmbedLib; 17 | use League\CommonMark\Exception\MissingDependencyException; 18 | use League\CommonMark\Extension\Embed\Embed; 19 | use League\CommonMark\Extension\Embed\EmbedAdapterInterface; 20 | 21 | final class OscaroteroEmbedAdapter implements EmbedAdapterInterface 22 | { 23 | private EmbedLib $embedLib; 24 | 25 | public function __construct(?EmbedLib $embed = null) 26 | { 27 | if ($embed === null) { 28 | if (! \class_exists(EmbedLib::class)) { 29 | throw new MissingDependencyException('The embed/embed package is not installed. Please install it with Composer to use this adapter.'); 30 | } 31 | 32 | $embed = new EmbedLib(); 33 | } 34 | 35 | $this->embedLib = $embed; 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function updateEmbeds(array $embeds): void 42 | { 43 | $extractors = $this->embedLib->getMulti(...\array_map(static fn (Embed $embed) => $embed->getUrl(), $embeds)); 44 | foreach ($extractors as $i => $extractor) { 45 | if ($extractor->code !== null) { 46 | $embeds[$i]->setEmbedCode($extractor->code->html); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Extension/Embed/DomainFilteringAdapter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | class DomainFilteringAdapter implements EmbedAdapterInterface 17 | { 18 | private EmbedAdapterInterface $decorated; 19 | 20 | /** @psalm-var non-empty-string */ 21 | private string $regex; 22 | 23 | /** 24 | * @param string[] $allowedDomains 25 | */ 26 | public function __construct(EmbedAdapterInterface $decorated, array $allowedDomains) 27 | { 28 | $this->decorated = $decorated; 29 | $this->regex = self::createRegex($allowedDomains); 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public function updateEmbeds(array $embeds): void 36 | { 37 | $this->decorated->updateEmbeds(\array_values(\array_filter($embeds, function (Embed $embed): bool { 38 | return \preg_match($this->regex, $embed->getUrl()) === 1; 39 | }))); 40 | } 41 | 42 | /** 43 | * @param string[] $allowedDomains 44 | * 45 | * @psalm-return non-empty-string 46 | */ 47 | private static function createRegex(array $allowedDomains): string 48 | { 49 | $allowedDomains = \array_map('preg_quote', $allowedDomains); 50 | 51 | return '/^(?:https?:\/\/)?(?:[^.]+\.)*(' . \implode('|', $allowedDomains) . ')/'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Extension/Embed/Embed.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | final class Embed extends AbstractBlock 19 | { 20 | private string $url; 21 | private ?string $embedCode; 22 | 23 | public function __construct(string $url, ?string $embedCode = null) 24 | { 25 | parent::__construct(); 26 | 27 | $this->url = $url; 28 | $this->embedCode = $embedCode; 29 | } 30 | 31 | public function getUrl(): string 32 | { 33 | return $this->url; 34 | } 35 | 36 | public function setUrl(string $url): void 37 | { 38 | $this->url = $url; 39 | } 40 | 41 | public function getEmbedCode(): ?string 42 | { 43 | return $this->embedCode; 44 | } 45 | 46 | public function setEmbedCode(?string $embedCode): void 47 | { 48 | $this->embedCode = $embedCode; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Extension/Embed/EmbedAdapterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | /** 17 | * Interface for a service which updates the embed code(s) for the given array of embeds 18 | */ 19 | interface EmbedAdapterInterface 20 | { 21 | /** 22 | * @param Embed[] $embeds 23 | */ 24 | public function updateEmbeds(array $embeds): void; 25 | } 26 | -------------------------------------------------------------------------------- /src/Extension/Embed/EmbedParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | use League\CommonMark\Parser\Block\BlockContinue; 18 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 19 | use League\CommonMark\Parser\Cursor; 20 | 21 | class EmbedParser implements BlockContinueParserInterface 22 | { 23 | private Embed $embed; 24 | 25 | public function __construct(string $url) 26 | { 27 | $this->embed = new Embed($url); 28 | } 29 | 30 | public function getBlock(): AbstractBlock 31 | { 32 | return $this->embed; 33 | } 34 | 35 | public function isContainer(): bool 36 | { 37 | return false; 38 | } 39 | 40 | public function canHaveLazyContinuationLines(): bool 41 | { 42 | return false; 43 | } 44 | 45 | public function canContain(AbstractBlock $childBlock): bool 46 | { 47 | return false; 48 | } 49 | 50 | public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue 51 | { 52 | return BlockContinue::none(); 53 | } 54 | 55 | public function addLine(string $line): void 56 | { 57 | } 58 | 59 | public function closeBlock(): void 60 | { 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Extension/Embed/EmbedRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | use League\CommonMark\Node\Node; 17 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 18 | use League\CommonMark\Renderer\NodeRendererInterface; 19 | 20 | class EmbedRenderer implements NodeRendererInterface 21 | { 22 | /** 23 | * @param Embed $node 24 | * 25 | * {@inheritDoc} 26 | * 27 | * @psalm-suppress MoreSpecificImplementedParamType 28 | */ 29 | public function render(Node $node, ChildNodeRendererInterface $childRenderer) 30 | { 31 | Embed::assertInstanceOf($node); 32 | 33 | return $node->getEmbedCode() ?? ''; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Extension/Embed/EmbedStartParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Embed; 15 | 16 | use League\CommonMark\Parser\Block\BlockStart; 17 | use League\CommonMark\Parser\Block\BlockStartParserInterface; 18 | use League\CommonMark\Parser\Cursor; 19 | use League\CommonMark\Parser\MarkdownParserStateInterface; 20 | use League\CommonMark\Util\LinkParserHelper; 21 | 22 | class EmbedStartParser implements BlockStartParserInterface 23 | { 24 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 25 | { 26 | if ($cursor->isIndented() || $parserState->getParagraphContent() !== null || ! ($parserState->getActiveBlockParser()->isContainer())) { 27 | return BlockStart::none(); 28 | } 29 | 30 | // 0-3 leading spaces are okay 31 | $cursor->advanceToNextNonSpaceOrTab(); 32 | 33 | // The line must begin with "https://" 34 | if (! str_starts_with($cursor->getRemainder(), 'https://')) { 35 | return BlockStart::none(); 36 | } 37 | 38 | // A valid link must be found next 39 | if (($dest = LinkParserHelper::parseLinkDestination($cursor)) === null) { 40 | return BlockStart::none(); 41 | } 42 | 43 | // Skip any trailing whitespace 44 | $cursor->advanceToNextNonSpaceOrTab(); 45 | 46 | // We must be at the end of the line; otherwise, this link was not by itself 47 | if (! $cursor->isAtEnd()) { 48 | return BlockStart::none(); 49 | } 50 | 51 | return BlockStart::of(new EmbedParser($dest))->at($cursor); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Extension/ExtensionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension; 18 | 19 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 20 | 21 | interface ExtensionInterface 22 | { 23 | public function register(EnvironmentBuilderInterface $environment): void; 24 | } 25 | -------------------------------------------------------------------------------- /src/Extension/Footnote/Node/Footnote.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Rezo Zero / Ambroise Maupate 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Footnote\Node; 16 | 17 | use League\CommonMark\Node\Block\AbstractBlock; 18 | use League\CommonMark\Reference\ReferenceInterface; 19 | use League\CommonMark\Reference\ReferenceableInterface; 20 | 21 | final class Footnote extends AbstractBlock implements ReferenceableInterface 22 | { 23 | /** @psalm-readonly */ 24 | private ReferenceInterface $reference; 25 | 26 | public function __construct(ReferenceInterface $reference) 27 | { 28 | parent::__construct(); 29 | 30 | $this->reference = $reference; 31 | } 32 | 33 | public function getReference(): ReferenceInterface 34 | { 35 | return $this->reference; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/Footnote/Node/FootnoteBackref.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Rezo Zero / Ambroise Maupate 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Footnote\Node; 16 | 17 | use League\CommonMark\Node\Inline\AbstractInline; 18 | use League\CommonMark\Reference\ReferenceInterface; 19 | use League\CommonMark\Reference\ReferenceableInterface; 20 | 21 | /** 22 | * Link from the footnote on the bottom of the document back to the reference 23 | */ 24 | final class FootnoteBackref extends AbstractInline implements ReferenceableInterface 25 | { 26 | /** @psalm-readonly */ 27 | private ReferenceInterface $reference; 28 | 29 | public function __construct(ReferenceInterface $reference) 30 | { 31 | parent::__construct(); 32 | 33 | $this->reference = $reference; 34 | } 35 | 36 | public function getReference(): ReferenceInterface 37 | { 38 | return $this->reference; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extension/Footnote/Node/FootnoteContainer.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Rezo Zero / Ambroise Maupate 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Footnote\Node; 16 | 17 | use League\CommonMark\Node\Block\AbstractBlock; 18 | 19 | final class FootnoteContainer extends AbstractBlock 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Extension/Footnote/Node/FootnoteRef.php: -------------------------------------------------------------------------------- 1 | 7 | * (c) Rezo Zero / Ambroise Maupate 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | declare(strict_types=1); 14 | 15 | namespace League\CommonMark\Extension\Footnote\Node; 16 | 17 | use League\CommonMark\Node\Inline\AbstractInline; 18 | use League\CommonMark\Reference\ReferenceInterface; 19 | use League\CommonMark\Reference\ReferenceableInterface; 20 | 21 | final class FootnoteRef extends AbstractInline implements ReferenceableInterface 22 | { 23 | private ReferenceInterface $reference; 24 | 25 | /** @psalm-readonly */ 26 | private ?string $content = null; 27 | 28 | /** 29 | * @param array $data 30 | */ 31 | public function __construct(ReferenceInterface $reference, ?string $content = null, array $data = []) 32 | { 33 | parent::__construct(); 34 | 35 | $this->reference = $reference; 36 | $this->content = $content; 37 | 38 | if (\count($data) > 0) { 39 | $this->data->import($data); 40 | } 41 | } 42 | 43 | public function getReference(): ReferenceInterface 44 | { 45 | return $this->reference; 46 | } 47 | 48 | public function setReference(ReferenceInterface $reference): void 49 | { 50 | $this->reference = $reference; 51 | } 52 | 53 | public function getContent(): ?string 54 | { 55 | return $this->content; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Data/FrontMatterDataParserInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Data; 15 | 16 | use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException; 17 | 18 | interface FrontMatterDataParserInterface 19 | { 20 | /** 21 | * @return mixed|null The parsed data (which may be null, if the input represents a null value) 22 | * 23 | * @throws InvalidFrontMatterException if parsing fails 24 | */ 25 | public function parse(string $frontMatter); 26 | } 27 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Data/LibYamlFrontMatterParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Data; 15 | 16 | use League\CommonMark\Exception\MissingDependencyException; 17 | use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException; 18 | 19 | final class LibYamlFrontMatterParser implements FrontMatterDataParserInterface 20 | { 21 | public static function capable(): ?LibYamlFrontMatterParser 22 | { 23 | if (! \extension_loaded('yaml')) { 24 | return null; 25 | } 26 | 27 | return new LibYamlFrontMatterParser(); 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public function parse(string $frontMatter) 34 | { 35 | if (! \extension_loaded('yaml')) { 36 | throw new MissingDependencyException('Failed to parse yaml: "ext-yaml" extension is missing'); 37 | } 38 | 39 | $result = @\yaml_parse($frontMatter); 40 | 41 | if ($result === false) { 42 | throw new InvalidFrontMatterException('Failed to parse front matter'); 43 | } 44 | 45 | return $result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Data/SymfonyYamlFrontMatterParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Data; 15 | 16 | use League\CommonMark\Exception\MissingDependencyException; 17 | use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException; 18 | use Symfony\Component\Yaml\Exception\ParseException; 19 | use Symfony\Component\Yaml\Yaml; 20 | 21 | final class SymfonyYamlFrontMatterParser implements FrontMatterDataParserInterface 22 | { 23 | /** 24 | * {@inheritDoc} 25 | */ 26 | public function parse(string $frontMatter) 27 | { 28 | if (! \class_exists(Yaml::class)) { 29 | throw new MissingDependencyException('Failed to parse yaml: "symfony/yaml" library is missing'); 30 | } 31 | 32 | try { 33 | /** @psalm-suppress ReservedWord */ 34 | return Yaml::parse($frontMatter); 35 | } catch (ParseException $ex) { 36 | throw InvalidFrontMatterException::wrap($ex); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Exception/InvalidFrontMatterException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Exception; 15 | 16 | use League\CommonMark\Exception\CommonMarkException; 17 | 18 | class InvalidFrontMatterException extends \RuntimeException implements CommonMarkException 19 | { 20 | public static function wrap(\Throwable $t): self 21 | { 22 | return new InvalidFrontMatterException('Failed to parse front matter: ' . $t->getMessage(), 0, $t); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/FrontMatterParserInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter; 15 | 16 | use League\CommonMark\Extension\FrontMatter\Input\MarkdownInputWithFrontMatter; 17 | 18 | interface FrontMatterParserInterface 19 | { 20 | public function parse(string $markdownContent): MarkdownInputWithFrontMatter; 21 | } 22 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/FrontMatterProviderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter; 15 | 16 | interface FrontMatterProviderInterface 17 | { 18 | /** 19 | * @return mixed|null 20 | */ 21 | public function getFrontMatter(); 22 | } 23 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Input/MarkdownInputWithFrontMatter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Input; 15 | 16 | use League\CommonMark\Extension\FrontMatter\FrontMatterProviderInterface; 17 | use League\CommonMark\Input\MarkdownInput; 18 | 19 | final class MarkdownInputWithFrontMatter extends MarkdownInput implements FrontMatterProviderInterface 20 | { 21 | /** @var mixed|null */ 22 | private $frontMatter; 23 | 24 | /** 25 | * @param string $content Markdown content without the raw front matter 26 | * @param int $lineOffset Line offset (based on number of front matter lines removed) 27 | * @param mixed|null $frontMatter Parsed front matter 28 | */ 29 | public function __construct(string $content, int $lineOffset = 0, $frontMatter = null) 30 | { 31 | parent::__construct($content, $lineOffset); 32 | 33 | $this->frontMatter = $frontMatter; 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public function getFrontMatter() 40 | { 41 | return $this->frontMatter; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Listener/FrontMatterPostRenderListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Listener; 15 | 16 | use League\CommonMark\Event\DocumentRenderedEvent; 17 | use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter; 18 | 19 | final class FrontMatterPostRenderListener 20 | { 21 | public function __invoke(DocumentRenderedEvent $event): void 22 | { 23 | if ($event->getOutput()->getDocument()->data->get('front_matter', null) === null) { 24 | return; 25 | } 26 | 27 | $frontMatter = $event->getOutput()->getDocument()->data->get('front_matter'); 28 | 29 | $event->replaceOutput(new RenderedContentWithFrontMatter( 30 | $event->getOutput()->getDocument(), 31 | $event->getOutput()->getContent(), 32 | $frontMatter 33 | )); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Listener/FrontMatterPreParser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Listener; 15 | 16 | use League\CommonMark\Event\DocumentPreParsedEvent; 17 | use League\CommonMark\Extension\FrontMatter\FrontMatterParserInterface; 18 | 19 | final class FrontMatterPreParser 20 | { 21 | private FrontMatterParserInterface $parser; 22 | 23 | public function __construct(FrontMatterParserInterface $parser) 24 | { 25 | $this->parser = $parser; 26 | } 27 | 28 | public function __invoke(DocumentPreParsedEvent $event): void 29 | { 30 | $content = $event->getMarkdown()->getContent(); 31 | 32 | $parsed = $this->parser->parse($content); 33 | 34 | $event->getDocument()->data->set('front_matter', $parsed->getFrontMatter()); 35 | $event->replaceMarkdown($parsed); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/FrontMatter/Output/RenderedContentWithFrontMatter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Extension\FrontMatter\Output; 15 | 16 | use League\CommonMark\Extension\FrontMatter\FrontMatterProviderInterface; 17 | use League\CommonMark\Node\Block\Document; 18 | use League\CommonMark\Output\RenderedContent; 19 | 20 | /** 21 | * @psalm-immutable 22 | */ 23 | final class RenderedContentWithFrontMatter extends RenderedContent implements FrontMatterProviderInterface 24 | { 25 | /** 26 | * @var mixed 27 | * 28 | * @psalm-readonly 29 | */ 30 | private $frontMatter; 31 | 32 | /** 33 | * @param Document $document The parsed Document object 34 | * @param string $content The final HTML 35 | * @param mixed|null $frontMatter Any parsed front matter 36 | */ 37 | public function __construct(Document $document, string $content, $frontMatter) 38 | { 39 | parent::__construct($document, $content); 40 | 41 | $this->frontMatter = $frontMatter; 42 | } 43 | 44 | /** 45 | * {@inheritDoc} 46 | */ 47 | public function getFrontMatter() 48 | { 49 | return $this->frontMatter; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Extension/GithubFlavoredMarkdownExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension; 15 | 16 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 17 | use League\CommonMark\Extension\Autolink\AutolinkExtension; 18 | use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; 19 | use League\CommonMark\Extension\Strikethrough\StrikethroughExtension; 20 | use League\CommonMark\Extension\Table\TableExtension; 21 | use League\CommonMark\Extension\TaskList\TaskListExtension; 22 | 23 | final class GithubFlavoredMarkdownExtension implements ExtensionInterface 24 | { 25 | public function register(EnvironmentBuilderInterface $environment): void 26 | { 27 | $environment->addExtension(new AutolinkExtension()); 28 | $environment->addExtension(new DisallowedRawHtmlExtension()); 29 | $environment->addExtension(new StrikethroughExtension()); 30 | $environment->addExtension(new TableExtension()); 31 | $environment->addExtension(new TaskListExtension()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Extension/HeadingPermalink/HeadingPermalink.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\HeadingPermalink; 15 | 16 | use League\CommonMark\Node\Inline\AbstractInline; 17 | 18 | /** 19 | * Represents an anchor link within a heading 20 | */ 21 | final class HeadingPermalink extends AbstractInline 22 | { 23 | /** @psalm-readonly */ 24 | private string $slug; 25 | 26 | public function __construct(string $slug) 27 | { 28 | parent::__construct(); 29 | 30 | $this->slug = $slug; 31 | } 32 | 33 | public function getSlug(): string 34 | { 35 | return $this->slug; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Extension/InlinesOnly/ChildRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\InlinesOnly; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | use League\CommonMark\Node\Node; 18 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 19 | use League\CommonMark\Renderer\NodeRendererInterface; 20 | 21 | /** 22 | * Simply renders child elements as-is, adding newlines as needed. 23 | */ 24 | final class ChildRenderer implements NodeRendererInterface 25 | { 26 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): string 27 | { 28 | $out = $childRenderer->renderNodes($node->children()); 29 | if (! $node instanceof Document) { 30 | $out .= $childRenderer->getBlockSeparator(); 31 | } 32 | 33 | return $out; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Extension/Mention/Generator/CallbackGenerator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Mention\Generator; 15 | 16 | use League\CommonMark\Exception\LogicException; 17 | use League\CommonMark\Extension\Mention\Mention; 18 | use League\CommonMark\Node\Inline\AbstractInline; 19 | 20 | final class CallbackGenerator implements MentionGeneratorInterface 21 | { 22 | /** 23 | * A callback function which sets the URL on the passed mention and returns the mention, return a new AbstractInline based object or null if the mention is not a match 24 | * 25 | * @var callable(Mention): ?AbstractInline 26 | */ 27 | private $callback; 28 | 29 | public function __construct(callable $callback) 30 | { 31 | $this->callback = $callback; 32 | } 33 | 34 | /** 35 | * @throws LogicException 36 | */ 37 | public function generateMention(Mention $mention): ?AbstractInline 38 | { 39 | $result = \call_user_func($this->callback, $mention); 40 | if ($result === null) { 41 | return null; 42 | } 43 | 44 | if ($result instanceof AbstractInline && ! ($result instanceof Mention)) { 45 | return $result; 46 | } 47 | 48 | if ($result instanceof Mention && $result->hasUrl()) { 49 | return $mention; 50 | } 51 | 52 | throw new LogicException('CallbackGenerator callable must set the URL on the passed mention and return the mention, return a new AbstractInline based object or null if the mention is not a match'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Extension/Mention/Generator/MentionGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Mention\Generator; 15 | 16 | use League\CommonMark\Extension\Mention\Mention; 17 | use League\CommonMark\Node\Inline\AbstractInline; 18 | 19 | interface MentionGeneratorInterface 20 | { 21 | public function generateMention(Mention $mention): ?AbstractInline; 22 | } 23 | -------------------------------------------------------------------------------- /src/Extension/Mention/Generator/StringTemplateLinkGenerator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Mention\Generator; 15 | 16 | use League\CommonMark\Extension\Mention\Mention; 17 | use League\CommonMark\Node\Inline\AbstractInline; 18 | 19 | final class StringTemplateLinkGenerator implements MentionGeneratorInterface 20 | { 21 | private string $urlTemplate; 22 | 23 | public function __construct(string $urlTemplate) 24 | { 25 | $this->urlTemplate = $urlTemplate; 26 | } 27 | 28 | public function generateMention(Mention $mention): ?AbstractInline 29 | { 30 | $mention->setUrl(\sprintf($this->urlTemplate, $mention->getIdentifier())); 31 | 32 | return $mention; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Extension/SmartPunct/EllipsesParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\SmartPunct; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | use League\CommonMark\Parser\Inline\InlineParserInterface; 21 | use League\CommonMark\Parser\Inline\InlineParserMatch; 22 | use League\CommonMark\Parser\InlineParserContext; 23 | 24 | final class EllipsesParser implements InlineParserInterface 25 | { 26 | public function getMatchDefinition(): InlineParserMatch 27 | { 28 | return InlineParserMatch::oneOf('...', '. . .'); 29 | } 30 | 31 | public function parse(InlineParserContext $inlineContext): bool 32 | { 33 | $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength()); 34 | $inlineContext->getContainer()->appendChild(new Text('…')); 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Extension/SmartPunct/Quote.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Extension\SmartPunct; 18 | 19 | use League\CommonMark\Node\Inline\AbstractStringContainer; 20 | 21 | final class Quote extends AbstractStringContainer 22 | { 23 | public const DOUBLE_QUOTE = '"'; 24 | public const DOUBLE_QUOTE_OPENER = '“'; 25 | public const DOUBLE_QUOTE_CLOSER = '”'; 26 | 27 | public const SINGLE_QUOTE = "'"; 28 | public const SINGLE_QUOTE_OPENER = '‘'; 29 | public const SINGLE_QUOTE_CLOSER = '’'; 30 | } 31 | -------------------------------------------------------------------------------- /src/Extension/SmartPunct/ReplaceUnpairedQuotesListener.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\SmartPunct; 15 | 16 | use League\CommonMark\Event\DocumentParsedEvent; 17 | use League\CommonMark\Node\Inline\AdjacentTextMerger; 18 | use League\CommonMark\Node\Inline\Text; 19 | use League\CommonMark\Node\Query; 20 | 21 | /** 22 | * Identifies any lingering Quote nodes that were missing pairs and converts them into Text nodes 23 | */ 24 | final class ReplaceUnpairedQuotesListener 25 | { 26 | public function __invoke(DocumentParsedEvent $event): void 27 | { 28 | $query = (new Query())->where(Query::type(Quote::class)); 29 | foreach ($query->findAll($event->getDocument()) as $quote) { 30 | \assert($quote instanceof Quote); 31 | 32 | $literal = $quote->getLiteral(); 33 | if ($literal === Quote::SINGLE_QUOTE) { 34 | $literal = Quote::SINGLE_QUOTE_CLOSER; 35 | } elseif ($literal === Quote::DOUBLE_QUOTE) { 36 | $literal = Quote::DOUBLE_QUOTE_OPENER; 37 | } 38 | 39 | $quote->replaceWith($new = new Text($literal)); 40 | AdjacentTextMerger::mergeWithDirectlyAdjacentNodes($new); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Extension/Strikethrough/Strikethrough.php: -------------------------------------------------------------------------------- 1 | and uAfrica.com (http://uafrica.com) 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Strikethrough; 15 | 16 | use League\CommonMark\Node\Inline\AbstractInline; 17 | use League\CommonMark\Node\Inline\DelimitedInterface; 18 | 19 | final class Strikethrough extends AbstractInline implements DelimitedInterface 20 | { 21 | private string $delimiter; 22 | 23 | public function __construct(string $delimiter = '~~') 24 | { 25 | parent::__construct(); 26 | 27 | $this->delimiter = $delimiter; 28 | } 29 | 30 | public function getOpeningDelimiter(): string 31 | { 32 | return $this->delimiter; 33 | } 34 | 35 | public function getClosingDelimiter(): string 36 | { 37 | return $this->delimiter; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/Strikethrough/StrikethroughExtension.php: -------------------------------------------------------------------------------- 1 | and uAfrica.com (http://uafrica.com) 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Strikethrough; 15 | 16 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 17 | use League\CommonMark\Extension\ExtensionInterface; 18 | 19 | final class StrikethroughExtension implements ExtensionInterface 20 | { 21 | public function register(EnvironmentBuilderInterface $environment): void 22 | { 23 | $environment->addDelimiterProcessor(new StrikethroughDelimiterProcessor()); 24 | $environment->addRenderer(Strikethrough::class, new StrikethroughRenderer()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Extension/Strikethrough/StrikethroughRenderer.php: -------------------------------------------------------------------------------- 1 | and uAfrica.com (http://uafrica.com) 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\Strikethrough; 15 | 16 | use League\CommonMark\Node\Node; 17 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 18 | use League\CommonMark\Renderer\NodeRendererInterface; 19 | use League\CommonMark\Util\HtmlElement; 20 | use League\CommonMark\Xml\XmlNodeRendererInterface; 21 | 22 | final class StrikethroughRenderer implements NodeRendererInterface, XmlNodeRendererInterface 23 | { 24 | /** 25 | * @param Strikethrough $node 26 | * 27 | * {@inheritDoc} 28 | * 29 | * @psalm-suppress MoreSpecificImplementedParamType 30 | */ 31 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 32 | { 33 | Strikethrough::assertInstanceOf($node); 34 | 35 | return new HtmlElement('del', $node->data->get('attributes'), $childRenderer->renderNodes($node->children())); 36 | } 37 | 38 | public function getXmlTagName(Node $node): string 39 | { 40 | return 'strikethrough'; 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | public function getXmlAttributes(Node $node): array 47 | { 48 | return []; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Extension/Table/Table.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Webuni s.r.o. 10 | * (c) Colin O'Dell 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace League\CommonMark\Extension\Table; 17 | 18 | use League\CommonMark\Node\Block\AbstractBlock; 19 | 20 | final class Table extends AbstractBlock 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Extension/Table/TableRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Webuni s.r.o. 10 | * (c) Colin O'Dell 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace League\CommonMark\Extension\Table; 17 | 18 | use League\CommonMark\Node\Node; 19 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 20 | use League\CommonMark\Renderer\NodeRendererInterface; 21 | use League\CommonMark\Util\HtmlElement; 22 | use League\CommonMark\Xml\XmlNodeRendererInterface; 23 | 24 | final class TableRenderer implements NodeRendererInterface, XmlNodeRendererInterface 25 | { 26 | /** 27 | * @param Table $node 28 | * 29 | * {@inheritDoc} 30 | * 31 | * @psalm-suppress MoreSpecificImplementedParamType 32 | */ 33 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 34 | { 35 | Table::assertInstanceOf($node); 36 | 37 | $attrs = $node->data->get('attributes'); 38 | 39 | $separator = $childRenderer->getInnerSeparator(); 40 | 41 | $children = $childRenderer->renderNodes($node->children()); 42 | 43 | return new HtmlElement('table', $attrs, $separator . \trim($children) . $separator); 44 | } 45 | 46 | public function getXmlTagName(Node $node): string 47 | { 48 | return 'table'; 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public function getXmlAttributes(Node $node): array 55 | { 56 | return []; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Extension/Table/TableRow.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Webuni s.r.o. 10 | * (c) Colin O'Dell 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace League\CommonMark\Extension\Table; 17 | 18 | use League\CommonMark\Node\Block\AbstractBlock; 19 | 20 | final class TableRow extends AbstractBlock 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Extension/Table/TableRowRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Webuni s.r.o. 10 | * (c) Colin O'Dell 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace League\CommonMark\Extension\Table; 17 | 18 | use League\CommonMark\Node\Node; 19 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 20 | use League\CommonMark\Renderer\NodeRendererInterface; 21 | use League\CommonMark\Util\HtmlElement; 22 | use League\CommonMark\Xml\XmlNodeRendererInterface; 23 | 24 | final class TableRowRenderer implements NodeRendererInterface, XmlNodeRendererInterface 25 | { 26 | /** 27 | * @param TableRow $node 28 | * 29 | * {@inheritDoc} 30 | * 31 | * @psalm-suppress MoreSpecificImplementedParamType 32 | */ 33 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 34 | { 35 | TableRow::assertInstanceOf($node); 36 | 37 | $attrs = $node->data->get('attributes'); 38 | 39 | $separator = $childRenderer->getInnerSeparator(); 40 | 41 | return new HtmlElement('tr', $attrs, $separator . $childRenderer->renderNodes($node->children()) . $separator); 42 | } 43 | 44 | public function getXmlTagName(Node $node): string 45 | { 46 | return 'table_row'; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | public function getXmlAttributes(Node $node): array 53 | { 54 | return []; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/Table/TableSection.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Webuni s.r.o. 10 | * (c) Colin O'Dell 11 | * 12 | * For the full copyright and license information, please view the LICENSE 13 | * file that was distributed with this source code. 14 | */ 15 | 16 | namespace League\CommonMark\Extension\Table; 17 | 18 | use League\CommonMark\Node\Block\AbstractBlock; 19 | 20 | final class TableSection extends AbstractBlock 21 | { 22 | public const TYPE_HEAD = 'head'; 23 | public const TYPE_BODY = 'body'; 24 | 25 | /** 26 | * @psalm-var self::TYPE_* 27 | * @phpstan-var self::TYPE_* 28 | * 29 | * @psalm-readonly 30 | */ 31 | private string $type; 32 | 33 | /** 34 | * @psalm-param self::TYPE_* $type 35 | * 36 | * @phpstan-param self::TYPE_* $type 37 | */ 38 | public function __construct(string $type = self::TYPE_BODY) 39 | { 40 | parent::__construct(); 41 | 42 | $this->type = $type; 43 | } 44 | 45 | /** 46 | * @psalm-return self::TYPE_* 47 | * 48 | * @phpstan-return self::TYPE_* 49 | */ 50 | public function getType(): string 51 | { 52 | return $this->type; 53 | } 54 | 55 | public function isHead(): bool 56 | { 57 | return $this->type === self::TYPE_HEAD; 58 | } 59 | 60 | public function isBody(): bool 61 | { 62 | return $this->type === self::TYPE_BODY; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/Node/TableOfContents.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents\Node; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock; 17 | 18 | final class TableOfContents extends ListBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/Node/TableOfContentsPlaceholder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents\Node; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | final class TableOfContentsPlaceholder extends AbstractBlock 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/Normalizer/FlatNormalizerStrategy.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents\Normalizer; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\ListItem; 17 | use League\CommonMark\Extension\TableOfContents\Node\TableOfContents; 18 | 19 | final class FlatNormalizerStrategy implements NormalizerStrategyInterface 20 | { 21 | /** @psalm-readonly */ 22 | private TableOfContents $toc; 23 | 24 | public function __construct(TableOfContents $toc) 25 | { 26 | $this->toc = $toc; 27 | } 28 | 29 | public function addItem(int $level, ListItem $listItemToAdd): void 30 | { 31 | $this->toc->appendChild($listItemToAdd); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/Normalizer/NormalizerStrategyInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents\Normalizer; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\ListItem; 17 | 18 | interface NormalizerStrategyInterface 19 | { 20 | public function addItem(int $level, ListItem $listItemToAdd): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/TableOfContentsGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents; 15 | 16 | use League\CommonMark\Extension\TableOfContents\Node\TableOfContents; 17 | use League\CommonMark\Node\Block\Document; 18 | 19 | interface TableOfContentsGeneratorInterface 20 | { 21 | public function generate(Document $document): ?TableOfContents; 22 | } 23 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/TableOfContentsPlaceholderRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents; 15 | 16 | use League\CommonMark\Node\Node; 17 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 18 | use League\CommonMark\Renderer\NodeRendererInterface; 19 | use League\CommonMark\Xml\XmlNodeRendererInterface; 20 | 21 | final class TableOfContentsPlaceholderRenderer implements NodeRendererInterface, XmlNodeRendererInterface 22 | { 23 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): string 24 | { 25 | return ''; 26 | } 27 | 28 | public function getXmlTagName(Node $node): string 29 | { 30 | return 'table_of_contents_placeholder'; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | public function getXmlAttributes(Node $node): array 37 | { 38 | return []; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Extension/TableOfContents/TableOfContentsRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TableOfContents; 15 | 16 | use League\CommonMark\Node\Node; 17 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 18 | use League\CommonMark\Renderer\NodeRendererInterface; 19 | use League\CommonMark\Xml\XmlNodeRendererInterface; 20 | 21 | final class TableOfContentsRenderer implements NodeRendererInterface, XmlNodeRendererInterface 22 | { 23 | /** @var NodeRendererInterface&XmlNodeRendererInterface */ 24 | private $innerRenderer; 25 | 26 | /** 27 | * @psalm-param NodeRendererInterface&XmlNodeRendererInterface $innerRenderer 28 | * 29 | * @phpstan-param NodeRendererInterface&XmlNodeRendererInterface $innerRenderer 30 | */ 31 | public function __construct(NodeRendererInterface $innerRenderer) 32 | { 33 | $this->innerRenderer = $innerRenderer; 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public function render(Node $node, ChildNodeRendererInterface $childRenderer) 40 | { 41 | return $this->innerRenderer->render($node, $childRenderer); 42 | } 43 | 44 | public function getXmlTagName(Node $node): string 45 | { 46 | return 'table_of_contents'; 47 | } 48 | 49 | /** 50 | * @return array 51 | */ 52 | public function getXmlAttributes(Node $node): array 53 | { 54 | return $this->innerRenderer->getXmlAttributes($node); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Extension/TaskList/TaskListExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TaskList; 15 | 16 | use League\CommonMark\Environment\EnvironmentBuilderInterface; 17 | use League\CommonMark\Extension\ExtensionInterface; 18 | 19 | final class TaskListExtension implements ExtensionInterface 20 | { 21 | public function register(EnvironmentBuilderInterface $environment): void 22 | { 23 | $environment->addInlineParser(new TaskListItemMarkerParser(), 35); 24 | $environment->addRenderer(TaskListItemMarker::class, new TaskListItemMarkerRenderer()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Extension/TaskList/TaskListItemMarker.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TaskList; 15 | 16 | use League\CommonMark\Node\Inline\AbstractInline; 17 | 18 | final class TaskListItemMarker extends AbstractInline 19 | { 20 | /** @psalm-readonly-allow-private-mutation */ 21 | private bool $checked; 22 | 23 | public function __construct(bool $isCompleted) 24 | { 25 | parent::__construct(); 26 | 27 | $this->checked = $isCompleted; 28 | } 29 | 30 | public function isChecked(): bool 31 | { 32 | return $this->checked; 33 | } 34 | 35 | public function setChecked(bool $checked): void 36 | { 37 | $this->checked = $checked; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Extension/TaskList/TaskListItemMarkerParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Extension\TaskList; 15 | 16 | use League\CommonMark\Extension\CommonMark\Node\Block\ListItem; 17 | use League\CommonMark\Node\Block\Paragraph; 18 | use League\CommonMark\Parser\Inline\InlineParserInterface; 19 | use League\CommonMark\Parser\Inline\InlineParserMatch; 20 | use League\CommonMark\Parser\InlineParserContext; 21 | 22 | final class TaskListItemMarkerParser implements InlineParserInterface 23 | { 24 | public function getMatchDefinition(): InlineParserMatch 25 | { 26 | return InlineParserMatch::oneOf('[ ]', '[x]'); 27 | } 28 | 29 | public function parse(InlineParserContext $inlineContext): bool 30 | { 31 | $container = $inlineContext->getContainer(); 32 | 33 | // Checkbox must come at the beginning of the first paragraph of the list item 34 | if ($container->hasChildren() || ! ($container instanceof Paragraph && $container->parent() && $container->parent() instanceof ListItem)) { 35 | return false; 36 | } 37 | 38 | $cursor = $inlineContext->getCursor(); 39 | $oldState = $cursor->saveState(); 40 | 41 | $cursor->advanceBy(3); 42 | 43 | if ($cursor->getNextNonSpaceCharacter() === null) { 44 | $cursor->restoreState($oldState); 45 | 46 | return false; 47 | } 48 | 49 | $isChecked = $inlineContext->getFullMatch() !== '[ ]'; 50 | 51 | $container->appendChild(new TaskListItemMarker($isChecked)); 52 | 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/GithubFlavoredMarkdownConverter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark; 15 | 16 | use League\CommonMark\Environment\Environment; 17 | use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; 18 | use League\CommonMark\Extension\GithubFlavoredMarkdownExtension; 19 | 20 | /** 21 | * Converts GitHub Flavored Markdown to HTML. 22 | */ 23 | final class GithubFlavoredMarkdownConverter extends MarkdownConverter 24 | { 25 | /** 26 | * Create a new Markdown converter pre-configured for GFM 27 | * 28 | * @param array $config 29 | */ 30 | public function __construct(array $config = []) 31 | { 32 | $environment = new Environment($config); 33 | $environment->addExtension(new CommonMarkCoreExtension()); 34 | $environment->addExtension(new GithubFlavoredMarkdownExtension()); 35 | 36 | parent::__construct($environment); 37 | } 38 | 39 | public function getEnvironment(): Environment 40 | { 41 | \assert($this->environment instanceof Environment); 42 | 43 | return $this->environment; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Input/MarkdownInputInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Input; 15 | 16 | interface MarkdownInputInterface 17 | { 18 | public function getContent(): string; 19 | 20 | /** 21 | * @return iterable 22 | */ 23 | public function getLines(): iterable; 24 | 25 | public function getLineCount(): int; 26 | } 27 | -------------------------------------------------------------------------------- /src/MarkdownConverterInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark; 15 | 16 | use League\CommonMark\Exception\CommonMarkException; 17 | use League\CommonMark\Output\RenderedContentInterface; 18 | 19 | /** 20 | * Interface for a service which converts Markdown to HTML. 21 | * 22 | * @deprecated since 2.2; use {@link ConverterInterface} instead 23 | */ 24 | interface MarkdownConverterInterface 25 | { 26 | /** 27 | * Converts Markdown to HTML. 28 | * 29 | * @deprecated since 2.2; use {@link ConverterInterface::convert()} instead 30 | * 31 | * @throws CommonMarkException 32 | */ 33 | public function convertToHtml(string $markdown): RenderedContentInterface; 34 | } 35 | -------------------------------------------------------------------------------- /src/Node/Block/AbstractBlock.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Block; 18 | 19 | use League\CommonMark\Exception\InvalidArgumentException; 20 | use League\CommonMark\Node\Node; 21 | 22 | /** 23 | * Block-level element 24 | * 25 | * @method parent() ?AbstractBlock 26 | */ 27 | abstract class AbstractBlock extends Node 28 | { 29 | protected ?int $startLine = null; 30 | 31 | protected ?int $endLine = null; 32 | 33 | protected function setParent(?Node $node = null): void 34 | { 35 | if ($node && ! $node instanceof self) { 36 | throw new InvalidArgumentException('Parent of block must also be block (cannot be inline)'); 37 | } 38 | 39 | parent::setParent($node); 40 | } 41 | 42 | public function setStartLine(?int $startLine): void 43 | { 44 | $this->startLine = $startLine; 45 | if ($this->endLine === null) { 46 | $this->endLine = $startLine; 47 | } 48 | } 49 | 50 | public function getStartLine(): ?int 51 | { 52 | return $this->startLine; 53 | } 54 | 55 | public function setEndLine(?int $endLine): void 56 | { 57 | $this->endLine = $endLine; 58 | } 59 | 60 | public function getEndLine(): ?int 61 | { 62 | return $this->endLine; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Node/Block/Document.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Block; 18 | 19 | use League\CommonMark\Parser\Cursor; 20 | use League\CommonMark\Reference\ReferenceMap; 21 | use League\CommonMark\Reference\ReferenceMapInterface; 22 | 23 | class Document extends AbstractBlock 24 | { 25 | /** @psalm-readonly */ 26 | protected ReferenceMapInterface $referenceMap; 27 | 28 | public function __construct(?ReferenceMapInterface $referenceMap = null) 29 | { 30 | parent::__construct(); 31 | 32 | $this->setStartLine(1); 33 | 34 | $this->referenceMap = $referenceMap ?? new ReferenceMap(); 35 | } 36 | 37 | public function getReferenceMap(): ReferenceMapInterface 38 | { 39 | return $this->referenceMap; 40 | } 41 | 42 | public function canContain(AbstractBlock $block): bool 43 | { 44 | return true; 45 | } 46 | 47 | public function isCode(): bool 48 | { 49 | return false; 50 | } 51 | 52 | public function matchesNextLine(Cursor $cursor): bool 53 | { 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Node/Block/Paragraph.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Block; 18 | 19 | class Paragraph extends AbstractBlock 20 | { 21 | /** @internal */ 22 | public bool $onlyContainsLinkReferenceDefinitions = false; 23 | } 24 | -------------------------------------------------------------------------------- /src/Node/Block/TightBlockInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node\Block; 15 | 16 | interface TightBlockInterface 17 | { 18 | public function isTight(): bool; 19 | 20 | public function setTight(bool $tight): void; 21 | } 22 | -------------------------------------------------------------------------------- /src/Node/Inline/AbstractInline.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\Node; 20 | 21 | abstract class AbstractInline extends Node 22 | { 23 | } 24 | -------------------------------------------------------------------------------- /src/Node/Inline/AbstractStringContainer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Inline; 18 | 19 | use League\CommonMark\Node\StringContainerInterface; 20 | 21 | abstract class AbstractStringContainer extends AbstractInline implements StringContainerInterface 22 | { 23 | protected string $literal = ''; 24 | 25 | /** 26 | * @param array $data 27 | */ 28 | public function __construct(string $contents = '', array $data = []) 29 | { 30 | parent::__construct(); 31 | 32 | $this->literal = $contents; 33 | if (\count($data) > 0) { 34 | $this->data->import($data); 35 | } 36 | } 37 | 38 | public function getLiteral(): string 39 | { 40 | return $this->literal; 41 | } 42 | 43 | public function setLiteral(string $literal): void 44 | { 45 | $this->literal = $literal; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Node/Inline/DelimitedInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node\Inline; 15 | 16 | interface DelimitedInterface 17 | { 18 | public function getOpeningDelimiter(): string; 19 | 20 | public function getClosingDelimiter(): string; 21 | } 22 | -------------------------------------------------------------------------------- /src/Node/Inline/Newline.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Inline; 18 | 19 | final class Newline extends AbstractInline 20 | { 21 | // Any changes to these constants should be reflected in .phpstorm.meta.php 22 | public const HARDBREAK = 0; 23 | public const SOFTBREAK = 1; 24 | 25 | /** @psalm-readonly */ 26 | private int $type; 27 | 28 | public function __construct(int $breakType = self::HARDBREAK) 29 | { 30 | parent::__construct(); 31 | 32 | $this->type = $breakType; 33 | } 34 | 35 | /** @psalm-immutable */ 36 | public function getType(): int 37 | { 38 | return $this->type; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Node/Inline/Text.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node\Inline; 18 | 19 | final class Text extends AbstractStringContainer 20 | { 21 | public function append(string $literal): void 22 | { 23 | $this->literal .= $literal; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Node/NodeIterator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | /** 19 | * @implements \IteratorAggregate 20 | */ 21 | final class NodeIterator implements \IteratorAggregate 22 | { 23 | public const FLAG_BLOCKS_ONLY = 1; 24 | 25 | private Node $node; 26 | private bool $blocksOnly; 27 | 28 | public function __construct(Node $node, int $flags = 0) 29 | { 30 | $this->node = $node; 31 | $this->blocksOnly = ($flags & self::FLAG_BLOCKS_ONLY) === self::FLAG_BLOCKS_ONLY; 32 | } 33 | 34 | /** 35 | * @return \Generator 36 | */ 37 | public function getIterator(): \Generator 38 | { 39 | $stack = [$this->node]; 40 | $index = 0; 41 | 42 | while ($stack) { 43 | $node = \array_pop($stack); 44 | 45 | yield $index++ => $node; 46 | 47 | // Push all children onto the stack in reverse order 48 | $child = $node->lastChild(); 49 | while ($child !== null) { 50 | if (! $this->blocksOnly || $child instanceof AbstractBlock) { 51 | $stack[] = $child; 52 | } 53 | 54 | $child = $child->previous(); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Node/NodeWalkerEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node; 18 | 19 | final class NodeWalkerEvent 20 | { 21 | /** @psalm-readonly */ 22 | private Node $node; 23 | 24 | /** @psalm-readonly */ 25 | private bool $isEntering; 26 | 27 | public function __construct(Node $node, bool $isEntering = true) 28 | { 29 | $this->node = $node; 30 | $this->isEntering = $isEntering; 31 | } 32 | 33 | public function getNode(): Node 34 | { 35 | return $this->node; 36 | } 37 | 38 | public function isEntering(): bool 39 | { 40 | return $this->isEntering; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Node/Query/AndExpr.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node\Query; 15 | 16 | use League\CommonMark\Node\Node; 17 | 18 | /** 19 | * @internal 20 | */ 21 | final class AndExpr implements ExpressionInterface 22 | { 23 | /** 24 | * @var callable[] 25 | * @psalm-var list 26 | */ 27 | private array $conditions; 28 | 29 | /** 30 | * @psalm-param callable(Node): bool $expressions 31 | */ 32 | public function __construct(callable ...$expressions) 33 | { 34 | $this->conditions = \array_values($expressions); 35 | } 36 | 37 | /** 38 | * @param callable(Node): bool $expression 39 | */ 40 | public function add(callable $expression): void 41 | { 42 | $this->conditions[] = $expression; 43 | } 44 | 45 | public function __invoke(Node $node): bool 46 | { 47 | foreach ($this->conditions as $condition) { 48 | if (! $condition($node)) { 49 | return false; 50 | } 51 | } 52 | 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Node/Query/ExpressionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node\Query; 15 | 16 | use League\CommonMark\Node\Node; 17 | 18 | interface ExpressionInterface 19 | { 20 | public function __invoke(Node $node): bool; 21 | } 22 | -------------------------------------------------------------------------------- /src/Node/Query/OrExpr.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node\Query; 15 | 16 | use League\CommonMark\Node\Node; 17 | 18 | /** 19 | * @internal 20 | */ 21 | final class OrExpr implements ExpressionInterface 22 | { 23 | /** 24 | * @var callable[] 25 | * @psalm-var list 26 | */ 27 | private array $conditions; 28 | 29 | /** 30 | * @psalm-param callable(Node): bool $expressions 31 | */ 32 | public function __construct(callable ...$expressions) 33 | { 34 | $this->conditions = \array_values($expressions); 35 | } 36 | 37 | /** 38 | * @param callable(Node): bool $expression 39 | */ 40 | public function add(callable $expression): void 41 | { 42 | $this->conditions[] = $expression; 43 | } 44 | 45 | public function __invoke(Node $node): bool 46 | { 47 | foreach ($this->conditions as $condition) { 48 | if ($condition($node)) { 49 | return true; 50 | } 51 | } 52 | 53 | return false; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Node/RawMarkupContainerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node; 15 | 16 | /** 17 | * Interface for a node which contains raw, unprocessed markup (like HTML) 18 | */ 19 | interface RawMarkupContainerInterface extends StringContainerInterface 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/Node/StringContainerHelper.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Node; 15 | 16 | final class StringContainerHelper 17 | { 18 | /** 19 | * Extract text literals from all descendant nodes 20 | * 21 | * @param Node $node Parent node 22 | * @param array $excludeTypes Optional list of node class types to exclude 23 | * 24 | * @return string Concatenated literals 25 | */ 26 | public static function getChildText(Node $node, array $excludeTypes = []): string 27 | { 28 | $text = ''; 29 | 30 | foreach ($node->iterator() as $child) { 31 | if ($child instanceof StringContainerInterface && ! self::isOneOf($child, $excludeTypes)) { 32 | $text .= $child->getLiteral(); 33 | } 34 | } 35 | 36 | return $text; 37 | } 38 | 39 | /** 40 | * @param string[] $classesOrInterfacesToCheck 41 | * 42 | * @psalm-pure 43 | */ 44 | private static function isOneOf(object $object, array $classesOrInterfacesToCheck): bool 45 | { 46 | foreach ($classesOrInterfacesToCheck as $type) { 47 | if ($object instanceof $type) { 48 | return true; 49 | } 50 | } 51 | 52 | return false; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Node/StringContainerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Node; 18 | 19 | /** 20 | * Interface for a node which directly contains line(s) of text 21 | */ 22 | interface StringContainerInterface 23 | { 24 | public function setLiteral(string $literal): void; 25 | 26 | public function getLiteral(): string; 27 | } 28 | -------------------------------------------------------------------------------- /src/Normalizer/TextNormalizer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Normalizer; 15 | 16 | /*** 17 | * Normalize text input using the steps given by the CommonMark spec to normalize labels 18 | * 19 | * @see https://spec.commonmark.org/0.29/#matches 20 | * 21 | * @psalm-immutable 22 | */ 23 | final class TextNormalizer implements TextNormalizerInterface 24 | { 25 | /** 26 | * {@inheritDoc} 27 | * 28 | * @psalm-pure 29 | */ 30 | public function normalize(string $text, array $context = []): string 31 | { 32 | // Collapse internal whitespace to single space and remove 33 | // leading/trailing whitespace 34 | $text = \preg_replace('/[ \t\r\n]+/', ' ', \trim($text)); 35 | \assert(\is_string($text)); 36 | 37 | // Is it strictly ASCII? If so, we can use strtolower() instead (faster) 38 | if (\mb_check_encoding($text, 'ASCII')) { 39 | return \strtolower($text); 40 | } 41 | 42 | return \mb_convert_case($text, \MB_CASE_FOLD, 'UTF-8'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Normalizer/TextNormalizerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Normalizer; 15 | 16 | /** 17 | * Creates a normalized version of the given input text 18 | */ 19 | interface TextNormalizerInterface 20 | { 21 | /** 22 | * @param string $text The text to normalize 23 | * @param array $context Additional context about the text being normalized (optional) 24 | * 25 | * $context may include (but is not required to include) the following: 26 | * - `prefix` - A string prefix to prepend to each normalized result 27 | * - `length` - The requested maximum length 28 | * - `node` - The node we're normalizing text for 29 | * 30 | * Implementations do not have to use or respect any information within that $context 31 | */ 32 | public function normalize(string $text, array $context = []): string; 33 | } 34 | -------------------------------------------------------------------------------- /src/Normalizer/UniqueSlugNormalizer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Normalizer; 15 | 16 | // phpcs:disable Squiz.Strings.DoubleQuoteUsage.ContainsVar 17 | final class UniqueSlugNormalizer implements UniqueSlugNormalizerInterface 18 | { 19 | private TextNormalizerInterface $innerNormalizer; 20 | /** @var array */ 21 | private array $alreadyUsed = []; 22 | 23 | public function __construct(TextNormalizerInterface $innerNormalizer) 24 | { 25 | $this->innerNormalizer = $innerNormalizer; 26 | } 27 | 28 | public function clearHistory(): void 29 | { 30 | $this->alreadyUsed = []; 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | * 36 | * @psalm-allow-private-mutation 37 | */ 38 | public function normalize(string $text, array $context = []): string 39 | { 40 | $normalized = $this->innerNormalizer->normalize($text, $context); 41 | 42 | // If it's not unique, add an incremental number to the end until we get a unique version 43 | if (\array_key_exists($normalized, $this->alreadyUsed)) { 44 | $suffix = 0; 45 | do { 46 | ++$suffix; 47 | } while (\array_key_exists("$normalized-$suffix", $this->alreadyUsed)); 48 | 49 | $normalized = "$normalized-$suffix"; 50 | } 51 | 52 | $this->alreadyUsed[$normalized] = true; 53 | 54 | return $normalized; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Normalizer/UniqueSlugNormalizerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Normalizer; 15 | 16 | interface UniqueSlugNormalizerInterface extends TextNormalizerInterface 17 | { 18 | public const DISABLED = false; 19 | public const PER_ENVIRONMENT = 'environment'; 20 | public const PER_DOCUMENT = 'document'; 21 | 22 | /** 23 | * Called by the Environment whenever the configured scope changes 24 | * 25 | * Currently, this will only be called PER_DOCUMENT. 26 | */ 27 | public function clearHistory(): void; 28 | } 29 | -------------------------------------------------------------------------------- /src/Output/RenderedContent.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Output; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | 18 | class RenderedContent implements RenderedContentInterface, \Stringable 19 | { 20 | /** @psalm-readonly */ 21 | private Document $document; 22 | 23 | /** @psalm-readonly */ 24 | private string $content; 25 | 26 | public function __construct(Document $document, string $content) 27 | { 28 | $this->document = $document; 29 | $this->content = $content; 30 | } 31 | 32 | public function getDocument(): Document 33 | { 34 | return $this->document; 35 | } 36 | 37 | public function getContent(): string 38 | { 39 | return $this->content; 40 | } 41 | 42 | /** 43 | * @psalm-mutation-free 44 | */ 45 | public function __toString(): string 46 | { 47 | return $this->content; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Output/RenderedContentInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Output; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | 18 | interface RenderedContentInterface extends \Stringable 19 | { 20 | /** 21 | * @psalm-mutation-free 22 | */ 23 | public function getDocument(): Document; 24 | 25 | /** 26 | * @psalm-mutation-free 27 | */ 28 | public function getContent(): string; 29 | } 30 | -------------------------------------------------------------------------------- /src/Parser/Block/AbstractBlockContinueParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | /** 19 | * Base class for a block parser 20 | * 21 | * Slightly more convenient to extend from vs. implementing the interface 22 | */ 23 | abstract class AbstractBlockContinueParser implements BlockContinueParserInterface 24 | { 25 | public function isContainer(): bool 26 | { 27 | return false; 28 | } 29 | 30 | public function canHaveLazyContinuationLines(): bool 31 | { 32 | return false; 33 | } 34 | 35 | public function canContain(AbstractBlock $childBlock): bool 36 | { 37 | return false; 38 | } 39 | 40 | public function addLine(string $line): void 41 | { 42 | } 43 | 44 | public function closeBlock(): void 45 | { 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Parser/Block/BlockContinue.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Cursor; 17 | use League\CommonMark\Parser\CursorState; 18 | 19 | /** 20 | * Result object for continuing parsing of a block; see static methods for constructors. 21 | * 22 | * @psalm-immutable 23 | */ 24 | final class BlockContinue 25 | { 26 | /** @psalm-readonly */ 27 | private ?CursorState $cursorState = null; 28 | 29 | /** @psalm-readonly */ 30 | private bool $finalize; 31 | 32 | private function __construct(?CursorState $cursorState = null, bool $finalize = false) 33 | { 34 | $this->cursorState = $cursorState; 35 | $this->finalize = $finalize; 36 | } 37 | 38 | public function getCursorState(): ?CursorState 39 | { 40 | return $this->cursorState; 41 | } 42 | 43 | public function isFinalize(): bool 44 | { 45 | return $this->finalize; 46 | } 47 | 48 | /** 49 | * Signal that we cannot continue here 50 | * 51 | * @return null 52 | */ 53 | public static function none(): ?self 54 | { 55 | return null; 56 | } 57 | 58 | /** 59 | * Signal that we're continuing at the given position 60 | */ 61 | public static function at(Cursor $cursor): self 62 | { 63 | return new self($cursor->saveState(), false); 64 | } 65 | 66 | /** 67 | * Signal that we want to finalize and close the block 68 | */ 69 | public static function finished(): self 70 | { 71 | return new self(null, true); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Parser/Block/BlockContinueParserWithInlinesInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\InlineParserEngineInterface; 17 | 18 | interface BlockContinueParserWithInlinesInterface extends BlockContinueParserInterface 19 | { 20 | /** 21 | * Parse any inlines inside of the current block 22 | */ 23 | public function parseInlines(InlineParserEngineInterface $inlineParser): void; 24 | } 25 | -------------------------------------------------------------------------------- /src/Parser/Block/BlockStartParserInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Cursor; 17 | use League\CommonMark\Parser\MarkdownParserStateInterface; 18 | 19 | /** 20 | * Interface for a block parser which identifies block starts. 21 | */ 22 | interface BlockStartParserInterface 23 | { 24 | /** 25 | * Check whether we should handle the block at the current position 26 | * 27 | * @param Cursor $cursor A cloned copy of the cursor at the current parsing location 28 | * @param MarkdownParserStateInterface $parserState Additional information about the state of the Markdown parser 29 | * 30 | * @return BlockStart|null The BlockStart that has been identified, or null if the block doesn't match here 31 | */ 32 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart; 33 | } 34 | -------------------------------------------------------------------------------- /src/Parser/Block/SkipLinesStartingWithLettersParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Block; 15 | 16 | use League\CommonMark\Parser\Cursor; 17 | use League\CommonMark\Parser\MarkdownParserStateInterface; 18 | use League\CommonMark\Util\RegexHelper; 19 | 20 | /** 21 | * @internal 22 | * 23 | * This "parser" is actually a performance optimization. 24 | * 25 | * Most lines in a typical Markdown document probably won't match a block start. This is especially true for lines starting 26 | * with letters - nothing in the core CommonMark spec or our supported extensions will match those lines as blocks. Therefore, 27 | * if we can identify those lines and skip block start parsing, we can optimize performance by ~10%. 28 | * 29 | * Previously this optimization was hard-coded in the MarkdownParser but did not allow users to override this behavior. 30 | * By implementing this optimization as a block parser instead, users wanting custom blocks starting with letters 31 | * can instead register their block parser with a higher priority to ensure their parser is always called first. 32 | */ 33 | final class SkipLinesStartingWithLettersParser implements BlockStartParserInterface 34 | { 35 | public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart 36 | { 37 | if (! $cursor->isIndented() && RegexHelper::isLetter($cursor->getNextNonSpaceCharacter())) { 38 | $cursor->advanceToNextNonSpaceOrTab(); 39 | 40 | return BlockStart::abort(); 41 | } 42 | 43 | return BlockStart::none(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Parser/CursorState.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | /** 17 | * Encapsulates the current state of a cursor in case you need to rollback later. 18 | * 19 | * WARNING: Do not attempt to use this class for ANYTHING except for 20 | * type hinting and passing this object back into restoreState(). 21 | * The constructor, methods, and inner contents may change in any 22 | * future release without warning! 23 | * 24 | * @internal 25 | * 26 | * @psalm-immutable 27 | */ 28 | final class CursorState 29 | { 30 | /** 31 | * @var array 32 | * 33 | * @psalm-readonly 34 | */ 35 | private array $state; 36 | 37 | /** 38 | * @internal 39 | * 40 | * @param array $state 41 | */ 42 | public function __construct(array $state) 43 | { 44 | $this->state = $state; 45 | } 46 | 47 | /** 48 | * @internal 49 | * 50 | * @return array 51 | */ 52 | public function toArray(): array 53 | { 54 | return $this->state; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Parser/Inline/InlineParserInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser\Inline; 15 | 16 | use League\CommonMark\Parser\InlineParserContext; 17 | 18 | interface InlineParserInterface 19 | { 20 | public function getMatchDefinition(): InlineParserMatch; 21 | 22 | public function parse(InlineParserContext $inlineContext): bool; 23 | } 24 | -------------------------------------------------------------------------------- /src/Parser/Inline/NewlineParser.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Parser\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Newline; 20 | use League\CommonMark\Node\Inline\Text; 21 | use League\CommonMark\Parser\InlineParserContext; 22 | 23 | final class NewlineParser implements InlineParserInterface 24 | { 25 | public function getMatchDefinition(): InlineParserMatch 26 | { 27 | return InlineParserMatch::regex('\\n'); 28 | } 29 | 30 | public function parse(InlineParserContext $inlineContext): bool 31 | { 32 | $inlineContext->getCursor()->advanceBy(1); 33 | 34 | // Check previous inline for trailing spaces 35 | $spaces = 0; 36 | $lastInline = $inlineContext->getContainer()->lastChild(); 37 | if ($lastInline instanceof Text) { 38 | $trimmed = \rtrim($lastInline->getLiteral(), ' '); 39 | $spaces = \strlen($lastInline->getLiteral()) - \strlen($trimmed); 40 | if ($spaces) { 41 | $lastInline->setLiteral($trimmed); 42 | } 43 | } 44 | 45 | if ($spaces >= 2) { 46 | $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK)); 47 | } else { 48 | $inlineContext->getContainer()->appendChild(new Newline(Newline::SOFTBREAK)); 49 | } 50 | 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Parser/InlineParserEngineInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | use League\CommonMark\Node\Block\AbstractBlock; 17 | 18 | /** 19 | * Parser for inline content (text, links, emphasized text, etc). 20 | */ 21 | interface InlineParserEngineInterface 22 | { 23 | /** 24 | * Parse the given contents as inlines and insert them into the given block 25 | */ 26 | public function parse(string $contents, AbstractBlock $block): void; 27 | } 28 | -------------------------------------------------------------------------------- /src/Parser/MarkdownParserInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | use League\CommonMark\Exception\CommonMarkException; 17 | use League\CommonMark\Node\Block\Document; 18 | 19 | interface MarkdownParserInterface 20 | { 21 | /** 22 | * @throws CommonMarkException 23 | */ 24 | public function parse(string $input): Document; 25 | } 26 | -------------------------------------------------------------------------------- /src/Parser/MarkdownParserState.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 17 | use League\CommonMark\Parser\Block\ParagraphParser; 18 | 19 | /** 20 | * @internal You should rely on the interface instead 21 | */ 22 | final class MarkdownParserState implements MarkdownParserStateInterface 23 | { 24 | /** @psalm-readonly */ 25 | private BlockContinueParserInterface $activeBlockParser; 26 | 27 | /** @psalm-readonly */ 28 | private BlockContinueParserInterface $lastMatchedBlockParser; 29 | 30 | public function __construct(BlockContinueParserInterface $activeBlockParser, BlockContinueParserInterface $lastMatchedBlockParser) 31 | { 32 | $this->activeBlockParser = $activeBlockParser; 33 | $this->lastMatchedBlockParser = $lastMatchedBlockParser; 34 | } 35 | 36 | public function getActiveBlockParser(): BlockContinueParserInterface 37 | { 38 | return $this->activeBlockParser; 39 | } 40 | 41 | public function getLastMatchedBlockParser(): BlockContinueParserInterface 42 | { 43 | return $this->lastMatchedBlockParser; 44 | } 45 | 46 | public function getParagraphContent(): ?string 47 | { 48 | if (! $this->lastMatchedBlockParser instanceof ParagraphParser) { 49 | return null; 50 | } 51 | 52 | $paragraphParser = $this->lastMatchedBlockParser; 53 | $content = $paragraphParser->getContentString(); 54 | 55 | return $content === '' ? null : $content; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Parser/MarkdownParserStateInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | use League\CommonMark\Parser\Block\BlockContinueParserInterface; 17 | 18 | interface MarkdownParserStateInterface 19 | { 20 | /** 21 | * Returns the deepest open block parser 22 | */ 23 | public function getActiveBlockParser(): BlockContinueParserInterface; 24 | 25 | /** 26 | * Open block parser that was last matched during the continue phase. This is different from the currently active 27 | * block parser, as an unmatched block is only closed when a new block is started. 28 | */ 29 | public function getLastMatchedBlockParser(): BlockContinueParserInterface; 30 | 31 | /** 32 | * Returns the current content of the paragraph if the matched block is a paragraph. The content can be multiple 33 | * lines separated by newlines. 34 | */ 35 | public function getParagraphContent(): ?string; 36 | } 37 | -------------------------------------------------------------------------------- /src/Parser/ParserLogicException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Parser; 15 | 16 | use League\CommonMark\Exception\CommonMarkException; 17 | 18 | class ParserLogicException extends \LogicException implements CommonMarkException 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Reference/MemoryLimitedReferenceMap.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Reference; 15 | 16 | final class MemoryLimitedReferenceMap implements ReferenceMapInterface 17 | { 18 | private ReferenceMapInterface $decorated; 19 | 20 | private const MINIMUM_SIZE = 100_000; 21 | 22 | private int $remaining; 23 | 24 | public function __construct(ReferenceMapInterface $decorated, int $maxSize) 25 | { 26 | $this->decorated = $decorated; 27 | $this->remaining = \max(self::MINIMUM_SIZE, $maxSize); 28 | } 29 | 30 | public function add(ReferenceInterface $reference): void 31 | { 32 | $this->decorated->add($reference); 33 | } 34 | 35 | public function contains(string $label): bool 36 | { 37 | return $this->decorated->contains($label); 38 | } 39 | 40 | public function get(string $label): ?ReferenceInterface 41 | { 42 | $reference = $this->decorated->get($label); 43 | if ($reference === null) { 44 | return null; 45 | } 46 | 47 | // Check for expansion limit 48 | $this->remaining -= \strlen($reference->getDestination()) + \strlen($reference->getTitle()); 49 | if ($this->remaining < 0) { 50 | return null; 51 | } 52 | 53 | return $reference; 54 | } 55 | 56 | /** 57 | * @return \Traversable 58 | */ 59 | public function getIterator(): \Traversable 60 | { 61 | return $this->decorated->getIterator(); 62 | } 63 | 64 | public function count(): int 65 | { 66 | return $this->decorated->count(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Reference/Reference.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Reference; 18 | 19 | /** 20 | * @psalm-immutable 21 | */ 22 | final class Reference implements ReferenceInterface 23 | { 24 | /** @psalm-readonly */ 25 | private string $label; 26 | 27 | /** @psalm-readonly */ 28 | private string $destination; 29 | 30 | /** @psalm-readonly */ 31 | private string $title; 32 | 33 | public function __construct(string $label, string $destination, string $title) 34 | { 35 | $this->label = $label; 36 | $this->destination = $destination; 37 | $this->title = $title; 38 | } 39 | 40 | public function getLabel(): string 41 | { 42 | return $this->label; 43 | } 44 | 45 | public function getDestination(): string 46 | { 47 | return $this->destination; 48 | } 49 | 50 | public function getTitle(): string 51 | { 52 | return $this->title; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Reference/ReferenceInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Reference; 18 | 19 | /** 20 | * Link reference 21 | */ 22 | interface ReferenceInterface 23 | { 24 | public function getLabel(): string; 25 | 26 | public function getDestination(): string; 27 | 28 | public function getTitle(): string; 29 | } 30 | -------------------------------------------------------------------------------- /src/Reference/ReferenceMapInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Reference; 18 | 19 | /** 20 | * A collection of references 21 | * 22 | * @phpstan-extends \IteratorAggregate 23 | */ 24 | interface ReferenceMapInterface extends \IteratorAggregate, \Countable 25 | { 26 | public function add(ReferenceInterface $reference): void; 27 | 28 | public function contains(string $label): bool; 29 | 30 | public function get(string $label): ?ReferenceInterface; 31 | } 32 | -------------------------------------------------------------------------------- /src/Reference/ReferenceableInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace League\CommonMark\Reference; 15 | 16 | interface ReferenceableInterface 17 | { 18 | public function getReference(): ReferenceInterface; 19 | } 20 | -------------------------------------------------------------------------------- /src/Renderer/Block/DocumentRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Renderer\Block; 18 | 19 | use League\CommonMark\Node\Block\Document; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Xml\XmlNodeRendererInterface; 24 | 25 | final class DocumentRenderer implements NodeRendererInterface, XmlNodeRendererInterface 26 | { 27 | /** 28 | * @param Document $node 29 | * 30 | * {@inheritDoc} 31 | * 32 | * @psalm-suppress MoreSpecificImplementedParamType 33 | */ 34 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): string 35 | { 36 | Document::assertInstanceOf($node); 37 | 38 | $wholeDoc = $childRenderer->renderNodes($node->children()); 39 | 40 | return $wholeDoc === '' ? '' : $wholeDoc . "\n"; 41 | } 42 | 43 | public function getXmlTagName(Node $node): string 44 | { 45 | return 'document'; 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | public function getXmlAttributes(Node $node): array 52 | { 53 | return [ 54 | 'xmlns' => 'http://commonmark.org/xml/1.0', 55 | ]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Renderer/ChildNodeRendererInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Node\Node; 17 | 18 | /** 19 | * Renders multiple nodes by delegating to the individual node renderers and adding spacing where needed 20 | */ 21 | interface ChildNodeRendererInterface 22 | { 23 | /** 24 | * @param Node[] $nodes 25 | */ 26 | public function renderNodes(iterable $nodes): string; 27 | 28 | public function getBlockSeparator(): string; 29 | 30 | public function getInnerSeparator(): string; 31 | } 32 | -------------------------------------------------------------------------------- /src/Renderer/DocumentRendererInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | use League\CommonMark\Output\RenderedContentInterface; 18 | 19 | /** 20 | * Renders a parsed Document AST 21 | */ 22 | interface DocumentRendererInterface extends MarkdownRendererInterface 23 | { 24 | /** 25 | * Render the given Document node (and all of its children) 26 | */ 27 | public function renderDocument(Document $document): RenderedContentInterface; 28 | } 29 | -------------------------------------------------------------------------------- /src/Renderer/HtmlDecorator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Node\Node; 17 | use League\CommonMark\Util\HtmlElement; 18 | 19 | final class HtmlDecorator implements NodeRendererInterface 20 | { 21 | private NodeRendererInterface $inner; 22 | private string $tag; 23 | /** @var array */ 24 | private array $attributes; 25 | private bool $selfClosing; 26 | 27 | /** 28 | * @param array $attributes 29 | */ 30 | public function __construct(NodeRendererInterface $inner, string $tag, array $attributes = [], bool $selfClosing = false) 31 | { 32 | $this->inner = $inner; 33 | $this->tag = $tag; 34 | $this->attributes = $attributes; 35 | $this->selfClosing = $selfClosing; 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function render(Node $node, ChildNodeRendererInterface $childRenderer) 42 | { 43 | return new HtmlElement($this->tag, $this->attributes, $this->inner->render($node, $childRenderer), $this->selfClosing); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Renderer/Inline/TextRenderer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Renderer\Inline; 18 | 19 | use League\CommonMark\Node\Inline\Text; 20 | use League\CommonMark\Node\Node; 21 | use League\CommonMark\Renderer\ChildNodeRendererInterface; 22 | use League\CommonMark\Renderer\NodeRendererInterface; 23 | use League\CommonMark\Util\Xml; 24 | use League\CommonMark\Xml\XmlNodeRendererInterface; 25 | 26 | final class TextRenderer implements NodeRendererInterface, XmlNodeRendererInterface 27 | { 28 | /** 29 | * @param Text $node 30 | * 31 | * {@inheritDoc} 32 | * 33 | * @psalm-suppress MoreSpecificImplementedParamType 34 | */ 35 | public function render(Node $node, ChildNodeRendererInterface $childRenderer): string 36 | { 37 | Text::assertInstanceOf($node); 38 | 39 | return Xml::escape($node->getLiteral()); 40 | } 41 | 42 | public function getXmlTagName(Node $node): string 43 | { 44 | return 'text'; 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public function getXmlAttributes(Node $node): array 51 | { 52 | return []; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Renderer/MarkdownRendererInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Node\Block\Document; 17 | use League\CommonMark\Output\RenderedContentInterface; 18 | 19 | /** 20 | * Renders a parsed Document AST 21 | * 22 | * @deprecated since 2.3; use {@link DocumentRendererInterface} instead 23 | */ 24 | interface MarkdownRendererInterface 25 | { 26 | /** 27 | * Render the given Document node (and all of its children) 28 | */ 29 | public function renderDocument(Document $document): RenderedContentInterface; 30 | } 31 | -------------------------------------------------------------------------------- /src/Renderer/NoMatchingRendererException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Exception\LogicException; 17 | 18 | class NoMatchingRendererException extends LogicException 19 | { 20 | } 21 | -------------------------------------------------------------------------------- /src/Renderer/NodeRendererInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Renderer; 15 | 16 | use League\CommonMark\Exception\InvalidArgumentException; 17 | use League\CommonMark\Node\Node; 18 | 19 | interface NodeRendererInterface 20 | { 21 | /** 22 | * @return \Stringable|string|null 23 | * 24 | * @throws InvalidArgumentException if the wrong type of Node is provided 25 | */ 26 | public function render(Node $node, ChildNodeRendererInterface $childRenderer); 27 | } 28 | -------------------------------------------------------------------------------- /src/Util/HtmlFilter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Util; 15 | 16 | use League\CommonMark\Exception\InvalidArgumentException; 17 | 18 | /** 19 | * @psalm-immutable 20 | */ 21 | final class HtmlFilter 22 | { 23 | // Return the entire string as-is 24 | public const ALLOW = 'allow'; 25 | // Escape the entire string so any HTML/JS won't be interpreted as such 26 | public const ESCAPE = 'escape'; 27 | // Return an empty string 28 | public const STRIP = 'strip'; 29 | 30 | /** 31 | * Runs the given HTML through the given filter 32 | * 33 | * @param string $html HTML input to be filtered 34 | * @param string $filter One of the HtmlFilter constants 35 | * 36 | * @return string Filtered HTML 37 | * 38 | * @throws InvalidArgumentException when an invalid $filter is given 39 | * 40 | * @psalm-pure 41 | */ 42 | public static function filter(string $html, string $filter): string 43 | { 44 | switch ($filter) { 45 | case self::STRIP: 46 | return ''; 47 | case self::ESCAPE: 48 | return \htmlspecialchars($html, \ENT_NOQUOTES); 49 | case self::ALLOW: 50 | return $html; 51 | default: 52 | throw new InvalidArgumentException(\sprintf('Invalid filter provided: "%s"', $filter)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Util/PrioritizedList.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Util; 18 | 19 | /** 20 | * @internal 21 | * 22 | * @phpstan-template T 23 | * @phpstan-implements \IteratorAggregate 24 | */ 25 | final class PrioritizedList implements \IteratorAggregate 26 | { 27 | /** 28 | * @var array> 29 | * @phpstan-var array> 30 | */ 31 | private array $list = []; 32 | 33 | /** 34 | * @var \Traversable|null 35 | * @phpstan-var \Traversable|null 36 | */ 37 | private ?\Traversable $optimized = null; 38 | 39 | /** 40 | * @param mixed $item 41 | * 42 | * @phpstan-param T $item 43 | */ 44 | public function add($item, int $priority): void 45 | { 46 | $this->list[$priority][] = $item; 47 | $this->optimized = null; 48 | } 49 | 50 | /** 51 | * @return \Traversable 52 | * 53 | * @phpstan-return \Traversable 54 | */ 55 | #[\ReturnTypeWillChange] 56 | public function getIterator(): \Traversable 57 | { 58 | if ($this->optimized === null) { 59 | \krsort($this->list); 60 | 61 | $sorted = []; 62 | foreach ($this->list as $group) { 63 | foreach ($group as $item) { 64 | $sorted[] = $item; 65 | } 66 | } 67 | 68 | $this->optimized = new \ArrayIterator($sorted); 69 | } 70 | 71 | return $this->optimized; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Util/Xml.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 11 | * - (c) John MacFarlane 12 | * 13 | * For the full copyright and license information, please view the LICENSE 14 | * file that was distributed with this source code. 15 | */ 16 | 17 | namespace League\CommonMark\Util; 18 | 19 | /** 20 | * Utility class for handling/generating XML and HTML 21 | * 22 | * @psalm-immutable 23 | */ 24 | final class Xml 25 | { 26 | /** 27 | * @psalm-pure 28 | */ 29 | public static function escape(string $string): string 30 | { 31 | return \str_replace(['&', '<', '>', '"'], ['&', '<', '>', '"'], $string); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Xml/MarkdownToXmlConverter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Xml; 15 | 16 | use League\CommonMark\ConverterInterface; 17 | use League\CommonMark\Environment\EnvironmentInterface; 18 | use League\CommonMark\Exception\CommonMarkException; 19 | use League\CommonMark\Output\RenderedContentInterface; 20 | use League\CommonMark\Parser\MarkdownParser; 21 | use League\CommonMark\Parser\MarkdownParserInterface; 22 | use League\CommonMark\Renderer\DocumentRendererInterface; 23 | 24 | final class MarkdownToXmlConverter implements ConverterInterface 25 | { 26 | /** @psalm-readonly */ 27 | private MarkdownParserInterface $parser; 28 | 29 | /** @psalm-readonly */ 30 | private DocumentRendererInterface $renderer; 31 | 32 | public function __construct(EnvironmentInterface $environment) 33 | { 34 | $this->parser = new MarkdownParser($environment); 35 | $this->renderer = new XmlRenderer($environment); 36 | } 37 | 38 | /** 39 | * Converts Markdown to XML 40 | * 41 | * @throws CommonMarkException 42 | */ 43 | public function convert(string $input): RenderedContentInterface 44 | { 45 | return $this->renderer->renderDocument($this->parser->parse($input)); 46 | } 47 | 48 | /** 49 | * Converts CommonMark to HTML. 50 | * 51 | * @see MarkdownToXmlConverter::convert() 52 | * 53 | * @throws CommonMarkException 54 | */ 55 | public function __invoke(string $input): RenderedContentInterface 56 | { 57 | return $this->convert($input); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Xml/XmlNodeRendererInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace League\CommonMark\Xml; 15 | 16 | use League\CommonMark\Node\Node; 17 | 18 | interface XmlNodeRendererInterface 19 | { 20 | public function getXmlTagName(Node $node): string; 21 | 22 | /** 23 | * @return array 24 | * 25 | * @psalm-return array 26 | */ 27 | public function getXmlAttributes(Node $node): array; 28 | } 29 | --------------------------------------------------------------------------------