将原始HTML插入Markdown

6

我需要将原始HTML插入Markdown文档中,以原样放置在输出中;具体而言,我试图确保文档的某些部分被包含在容器中(例如<div><aside><section>),而不会破坏其中内容的格式。

但是,如果我采用显而易见的方法:

<aside>

## My heading
Some text, some text, some more text.

</aside>

然后Markdown处理器生成了这个无效的HTML:

<p><aside></p>
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
<p></aside></p>

如果我尝试将添加的标签与要处理的文本进行对比,例如:

<aside>
## My heading
Some text, some text, some more text.
</aside>

然后基于实际格式化的内容会发生一些不同的事情,但是它们都不正确 - 并且<aside>仍然会被包裹在一个<p>中。

如果我使用<div>,那么所有内容都将被呈现为纯文本。

有没有一种方法可以指示Markdown处理器按照字面意思处理一段输入文本,而不做任何处理?

我目前正在使用Hoedown(通过Misaka),它支持许多现代Markdown扩展,但如果有更现代的Markdown引擎能够更好地处理这个问题,我肯定会考虑切换到它。


@JohnHennig 我会更改标题,尽管你建议的并不是我认为的改进。不幸的是,我无法切换Markdown实现,但您有没有任何不会出现这种情况的示例? - fluffy
有几个问题虽然不是严格的重复,但它们的答案可能会有所帮助:将MarkDown元素分组到DIV元素或自定义HTML标记中markdown=“1”在p标签内无法正常工作 - Waylan
此外,<aside> 是 HTML 5 中的一个新标签,在旧的 Markdown 实现中不支持该标签,因为这些实现是在该标签引入之前创建的。由于 Hoedown 在4-5年前就已经被放弃,我怀疑它从未添加对 <aside> 的支持作为“块级”标签。<div> 的行为与我上面链接的两个其他问题中描述的一样。 - Waylan
@Waylan,我的总体请求更多地是关于能够将原始HTML放入基于Markdown的文档中,而无需Markdown处理器决定将其包装在多余的<p>标签中。如果这导致切换到不同的Markdown处理器有很好的理由,那么我将考虑将其作为解决方案的一部分,尽管这并不是最理想的。 - fluffy
1
经过考虑您的反馈,我认为我更好地理解了您的问题,并添加了全面的答案。希望我理解得没错。 - Waylan
显示剩余4条评论
1个回答

10
安全的做法是使用以下内容:
<div class="aside">
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</div>

然而,实际情况比这更加复杂,有许多可能的答案。适用于您的答案取决于您使用的Markdown实现,因为它们之间存在微妙的差异。让我们从参考实现(markdown.pl)和原始规则开始,因为这是Hoedown声称遵循的规则(见下文)。

旧版Markdown

许多旧解析器最初是在从HTML4转换到XHTML1期间开发的,它们对原始HTML的处理方式和处理方法反映了这一点。也就是说,一些解析器在最近几年已进行更新,以支持新的HTML特性。但是,参考实现已经超过十年没有更新了,所以这是一个很好的起点。通常,如果您能够在参考实现中使某些内容正常工作,则它将在任何实现中正常工作,因此让我们专注于此。

规则首先描述块级HTML标记的处理方式,然后将跨度级别的行为描述为特例。但是,在代码中,事情恰恰相反。跨度级别的行为是默认值,块级别的行为是特殊情况。

当使用跨度级别标记时,您希望结果包装在<p>标记中。例如,foo <i>bar</i> baz应该产生<p>foo <i>bar</i> baz</p>。因此,为了避免在<p>标记中包含原始HTML,规则要求满足一组非常特定的条件:

唯一的限制是块级HTML元素(例如<div><table><pre><p>等)必须由空行与周围内容分隔开,块的开始和结束标记不应缩进使用制表符或空格。Markdown足够智能,不会在HTML块级标记周围添加额外的(不需要的)<p>标记。有3个要求:
  1. 原始HTML块必须以已知的块级标记开头。如前所述,在旧实现中,这些标记必须是HTML4 / XHTML1规范中的有效块级标记。在HTML5中最近引入的任何内容可能无法在各种实现之间一致地工作。
  2. 开放标签必须在空白行或文档开头之前,并且关闭标签必须在空白行或文档结尾之后。
  3. 开放标记必须以行的第一个字符开头。任何缩进都将导致解析器无法识别文本块为块级原始HTML。
最后,规则说明如下:

请注意,Markdown格式语法不会在块级HTML标记内处理。例如,您不能在HTML块内使用Markdown样式的*emphasis*

请注意,这与跨度级HTML不同:

与块级HTML标记不同,Markdown语法在跨度级标记内处理。

在这种情况下,<span>foo *bar*</span>会得到<p><span>foo <em>bar</em></span></p>的结果,而<div>foo *bar*<div>会得到<div>foo *bar*</div>的结果。请注意,在第一个示例中,虽然Markdown语法(*bar*)被处理了,但整个内容被包装在<p>标签中。相反,在第二个示例中,Markdown语法(*bar*)没有被处理,但该块未被包装在<p>标签中。因此,任何包含在块级原始HTML中的内容必须全部使用原始HTML。

因此,让我们将这些规则应用于您的示例:

<div>
<aside>
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</aside>
</div>

使用<div>标签可以被老版本的实现所识别。内容都是原始HTML,因为它不会被处理成Markdown。Babelmark显示这在所有实现中都可行。

当然,同时使用<aside><div>标签是多余的,因此您可以简单地使用一个带有适当类的<div>标签:

<div class="aside">
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</div>

正如Babelmark 所示,这也适用于任何地方。

如果您使用的实现已经添加了对HTML5块级标签的支持,您可以直接使用<aside>标签:

<aside>
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</aside>

当然,我们仍需要使用所有原始HTML。正如Babelmark 所示, 这在大多数但不是所有实现中都有效。

扩展Markdown

多年来,许多Markdown实现已经添加了非标准的扩展语法,以添加额外的功能。因为明显的原因,许多用户希望能够在原始HTML块中处理Markdown语法。因此,多年前,PHP Markdown Extra 引入markdown="1" 的变通方法,并被许多实现复制。但是,大多数支持扩展的实现需要显式启用扩展。它不是默认启用的。

如果您使用的实现支持扩展,并且已启用该扩展,则可以使用以下内容(如果支持较新的HTML5标签):

<aside markdown="1">
## My heading
Some text, some text, some more text.
</aside>

或者这样写(如果不支持HTML5标签):

<div markdown="1">
<aside>
## My heading
Some text, some text, some more text.
</aside>
</div>

或者...

<div class="aside" markdown="1">
## My heading
Some text, some text, some more text.
</div>

Commonmark

有些人对实现不一致感到沮丧,于是开始定义一个严格的规范,这就是所谓的Commonmark spec。然而,讽刺的是,该规范自己承认违反了原始实现的一些非常明确定义的规则,这只会增加更多的不一致性。其中最糟糕的问题之一是原始 HTML 处理。

只要您的原始 HTML 块不包含任何空行,Commonmark 将以与老式 Markdown 实现相同的方式处理您的块。然而,一旦您引入空行,任何接下来的内容都将被解析为Markdown。

此外,Commonmark 规范清楚地定义了哪些标签被视为块级标签。恰好,<aside> 是在标签列表中的。

因此,如果您使用符合 Commonmark 的实现,以下内容将起作用:

<aside>

## My heading
Some text, some text, some more text.

</aside>

请注意,<aside>标签紧接着一个空行,这告诉解析器将标签后面的任何内容视为Markdown。正如Babelmark 演示的那样,这适用于Commonmark实现,但不适用于老式实现。

Hoedown

Hoedown专门声称“完全符合”“官方Markdown v1.0.0和v1.0.3测试套件”。请注意,这些是老式参考实现的测试套件,而不是较新的Commonmark规范。在这种情况下,我们可以假设处理原始HTML块内Markdown的Commonmark技巧将无法起作用。当然,您可以尝试并确保其可行性。

Hoedown还声称具有“可选支持几个(非官方)Markdown扩展”。但是,没有可用扩展的综合列表,也没有任何有关如何启用它们的说明。我没有安装该工具,但也许有命令行中的说明?如果您能找到一种启用markdown="1"扩展的方法,那么您就可以使用该技巧在原始HTML块中进行Markdown处理。

然而,如果没有明确的文档证明,我认为Hoedown是一种老派的实现。我还注意到repo中的html_block_names.gperf文件未列出aside作为已知的HTML块级标记。因此,我们可以假设任何原始HTML块都必须包含在该文件中列出的24个标记之一中。
考虑到上述情况,我们可以安全地假设以下是从Hoedown获得所需结果的唯一确定方法:
<div>
<aside>
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</aside>
</div>

或者...

<div class="aside">
<h2>My heading</h2>
<p>Some text, some text, some more text.</p>
</div>

非常感谢您提供如此全面的答案。我会查看Hoedown是否支持markdown="1",但我不认为它支持。更换Markdown实现是可能的,但不在我尝试解决特定问题的时间范围内。 - fluffy
至少在我启用的扩展中,markdown="1"不仅不能使其实际处理<div>内部的Markdown,而且Hoedown仍会剥离markdown伪属性,因此我无法自行修复它!真是两全其美的最糟糕情况。 - fluffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接