如何创建扩展以解析 thephpleague/commonmark 的多行

How to create an extension to parse multiple line for thephpleague/commonmark

全部,

我正在尝试创建一个扩展(我相信是为了扩展 AbstractBlockParser),它会找到一个开始标记,将后面的行移动到一个块中,然后在找到结束标记时停止(在它自己的最后一行)。

在查看提供的示例时,很难弄清楚它们是如何构建由多行组成的块的,例如使用代码围栏,文档不包含这种情况。

列表解析器代码似乎显示正在将 ListItems 添加到 ListBlock,但它如何知道何时停止?

降价示例

{{ Object ID
Any markdown goes here.
Some more
  * List 1
  * List 2
}}

输出可能是:

<div class="object">
    <span>Object ID</span>
    <p>Any markdown goes here.
    Some more</p>
    <ul>
        <li>List 1</li>
        <li>List 2</li>
    </ul>
</div>

诀窍是您的块应该始终具有 canContain()matchesNextLine() 方法 return true; - 这些将确保后续行始终作为子块添加。 (查看 FencedCodeListBlock 实现。)

下面是一些应该有效的代码:

ObjectBlock.php:

class ObjectBlock extends AbstractBlock
{
    private $objectId;

    public function __construct($objectId)
    {
        $this->objectId = $objectId;
    }

    public function getObjectId()
    {
        return $this->objectId;
    }

    public function canContain(AbstractBlock $block)
    {
        return true;
    }

    public function acceptsLines()
    {
        return false;
    }

    public function isCode()
    {
        return false;
    }

    public function matchesNextLine(Cursor $cursor)
    {
        return true;
    }
}

ObjectParser.php:

class ObjectParser extends AbstractBlockParser
{
    public function parse(ContextInterface $context, Cursor $cursor)
    {
        // Look for the starting syntax
        if ($cursor->match('/^{{ /')) {
            $id = $cursor->getRemainder();
            $cursor->advanceToEnd();

            $context->addBlock(new ObjectBlock($id));

            return true;
        // Look for the ending syntax
        } elseif ($cursor->match('/^}} +$/')) {
            // TODO: I don't know if this is the best approach, but it should work
            // Basically, we're going to locate a parent ObjectBlock in the AST...
            $container = $context->getContainer();
            while ($container) {
                if ($container instanceof ObjectBlock) {
                    $cursor->advanceToEnd();

                    // Found it!  Now we'll close everything up to (and including) it
                    $context->getBlockCloser()->setLastMatchedContainer($container->parent());
                    $context->getBlockCloser()->closeUnmatchedBlocks();
                    $context->setBlocksParsed(true);

                    return true;
                }

                $container = $container->parent();
            }
        }

        return false;
    }
}

对象渲染器:

class ObjectRenderer implements BlockRendererInterface
{
    public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false)
    {
        $span = sprintf('<span>%s</span>', $block->getObjectId());
        $contents = $htmlRenderer->renderBlocks($block->children());

        return new HtmlElement('div', ['class' => 'object'],
            $span . $contents
        );
    }
}

免责声明:虽然我是这个库的作者,但特定的逻辑(容器、提示和块关闭)大部分是从 JS 版本中按原样派生的,我只理解其中的大约 75% - 只是足以让我的叉子继续工作并找出有效的方法:)