Markdown 是一种用于编写结构化文档的纯文本格式,其基础是电子邮件和 Usenet 帖子中使用的格式约定。它由 John Gruber 于 2004 年开发,他用 Perl 编写了第一个 Markdown 转 HTML 转换器,并很快在网站中得到广泛应用。到 2014 年,已经有数十种不同语言的实现。其中一些扩展了基础 Markdown 语法,增加了脚注、定义列表、表格和其他构造的约定,还有一些不仅支持输出为 HTML,还支持 LaTeX 及其他多种格式。
John Gruber 的 Markdown 语法权威描述并没有明确定义其语法。以下是一些它未能解答的问题示例:
子列表需要多少缩进?规范指出续行段落需要缩进四个空格,但对于子列表的描述并不明确。自然会想到它们也必须缩进四个空格,但 Markdown.pl 并不要求这样做。这绝非“边缘情况”,实现之间在此问题上的分歧往往会导致用户在实际文档中遇到意外。(参见 John Gruber 的这条评论。)
引用块或标题前是否需要空行?大多数实现不需要空行。然而,这可能在硬换行文本中导致意外结果,并导致解析歧义(注意,有些实现将标题放在引用块内,而另一些则不然)。(John Gruber 也曾明确表示 支持要求使用空行。)
缩进代码块前是否需要空行?(Markdown.pl 要求它,但文档中并未提及,且有些实现并不要求。)
paragraph
code?确定列表项何时被 <p> 标签包裹的确切规则是什么?列表可以部分“松散”部分“紧凑”吗?我们该如何处理这样的列表?
1. one
2. two
3. three
或者这个?
1. one
- a
- b
2. two
(John Gruber 在这里有一些相关的评论。)
列表标记可以缩进吗?有序列表标记可以右对齐吗?
8. item 1
9. item 2
10. item 2a这是一个包含水平分割线的列表项的列表,还是由水平分割线分隔的两个列表?
* a
* * * * *
* b当列表标记从数字变为圆点时,我们是有两个列表还是一个?(Markdown 语法描述暗示是两个,但 perl 脚本和许多其他实现会生成一个。)
1. fee
2. fie
- foe
- fum行内结构标记的优先级规则是什么?例如,以下是有效的链接,还是代码片段拥有更高优先级?
[a backtick (`)](/url) and [another backtick (`)](/url).强调和加粗标记的优先级规则是什么?例如,以下应如何解析?
*foo *bar* baz*块级结构和行内级结构之间的优先级规则是什么?例如,以下应如何解析?
- `a long code span can contain a hyphen like this
- and it can screw things up`列表项中可以包含标题吗?(Markdown.pl 不允许这样做,但在引用块中可以出现标题。)
- # Heading链接引用可以在块引用或列表项内定义吗?
> Blockquote [foo].
>
> [foo]: /url如果有多个同名引用定义,哪个具有优先权?
[foo]: /url1
[foo]: /url2
[foo][]在没有规范的情况下,早期的实现者咨询了 Markdown.pl 来解决这些歧义。但 Markdown.pl 存在很多缺陷,在许多情况下会产生明显糟糕的结果,因此它不能令人满意地替代规范。
由于没有明确的规范,各实现之间出现了相当大的分歧。结果,用户经常惊讶地发现,在某个系统(例如 github wiki)上以某种方式渲染的文档,在另一个系统(例如使用 pandoc 转换为 docbook)上渲染的效果却不同。更糟糕的是,由于 Markdown 中没有什么会被视为“语法错误”,这种差异往往无法立即被发现。
本文档试图明确地规范 Markdown 语法。它包含了许多 Markdown 和 HTML 并排显示的示例。这些示例旨在兼作一致性测试。随附的脚本 spec_tests.py 可用于对任何 Markdown 程序运行这些测试:
python test/spec_tests.py --spec spec.txt --program PROGRAM
由于本文档描述了如何将 Markdown 解析为抽象语法树,因此使用抽象语法树的抽象表示而非 HTML 更有意义。但 HTML 能够表示我们需要区分的结构,并且为测试选择 HTML 使得在不编写抽象语法树渲染器的情况下对实现进行测试成为可能。
本文档是由一个名为 spec.txt 的文本文件生成的,该文件以 Markdown 编写,并针对对比测试进行了一些小的扩展。脚本 spec2md.pl 可用于将 spec.txt 转换为 Pandoc Markdown,进而转换为其他格式。
在示例中,使用 → 字符来表示制表符。
行 (line) 是由零个或多个 字符 组成的序列,后跟一个行结束符(CR、LF 或 CRLF)或文件结束符。
一个 字符 是一个 Unicode 码位。本规范未指定编码;它将行视为由字符而非字节组成。符合规范的解析器可能受限于特定编码。
行中的制表符被扩展为空格,制表位为 4 个字符
行结束符会被替换为换行符 (LF)。
不包含任何字符的行,或仅包含空格(在 tab 展开后)的行,称为 空行。
出于安全原因,符合规范的解析器必须剔除或替换 Unicode 字符 U+0000。
我们可以将文档视为一系列 块——即段落、块引用、列表、标题、分割线和代码块等结构元素。块可以包含其他块,也可以包含 行内 内容:单词、空格、链接、强调文本、图像和行内代码。
块结构的指示符总是优先于行内结构的指示符。所以,例如,以下是一个包含两项的列表,而不是一个包含代码片段的项的列表:
这意味着解析可以分为两步进行:第一,辨别文档的块结构;第二,解析段落、标题和其他块结构中的文本行以确定行内结构。第二步需要仅在第一步结束时才能获得的关于链接引用定义的信息。请注意,第一步需要按顺序处理行,而第二步可以并行化,因为一个块元素的行内解析不会影响其他任何块元素的行内解析。
我们可以将块分为两类:可以包含其他块的容器块,以及不可包含其他块的叶子块。
本节描述了构成 Markdown 文档的不同种类的叶子块。
一行由 0-3 个缩进空格开头,后跟三个或更多匹配的 -, _ 或 * 字符(每个字符后可选跟任意数量的空格),构成一条 水平分割线。
错误的字符
字符不足
允许一到三个空格的缩进
四个空格太多了
可以使用超过三个字符
字符之间允许有空格
末尾允许有空格
然而,行中不能出现其他任何字符
要求所有的非空格字符必须相同。因此,这不是一条水平分割线
水平分割线前后不需要空行
水平分割线可以中断段落
如果一行连字符符合上述水平分割线的条件,同时也能被解读为 Setext 标题 的下划线,则解读为 Setext 标题 的优先级更高。因此,举例来说,这是一个 Setext 标题,而不是一个段落后跟一条水平分割线
当一行同时可能被解读为水平分割线和列表项时,优先选择水平分割线
如果你想在列表项中使用水平分割线,请使用不同的项目符号
ATX 标题 由一串解析为行内内容的字符组成,位于开头 1–6 个未转义的 # 字符序列和可选的末尾任意数量的 # 字符序列之间。开头的 # 序列后不能直接紧跟非空格字符。可选的结尾 # 序列前必须有一个空格,并且其后只能包含空格。开头的 # 字符可以缩进 0-3 个空格。标题的原始内容在解析为行内内容之前,会剥离首尾的空格。标题级别等于开头 # 序列中 # 字符的数量。
简单标题
# foo
## foo
### foo
#### foo
##### foo
###### foo
<h1>foo</h1>
<h2>foo</h2>
<h3>foo</h3>
<h4>foo</h4>
<h5>foo</h5>
<h6>foo</h6>
超过六个 # 字符不是标题
# 字符和标题内容之间需要一个空格。请注意,许多实现目前不需要该空格。然而,原始的 ATX 实现 要求有空格,且这有助于防止类似以下内容被解析为标题
这不是标题,因为第一个 # 被转义了
内容按行内元素解析
解析行内内容时,首尾空格会被忽略
允许 1 到 3 个空格的缩进
四个空格太多了
结束的 # 字符序列是可选的
它不需要与起始序列长度相同
结束序列后允许有空格
后跟非空格字符的 # 字符序列不是结尾序列,而是计入标题内容的一部分
结束序列前必须有一个空格
反斜杠转义的 # 字符不计为结束序列的一部分
ATX 标题不需要由空行与周围内容分隔,且它们可以中断段落
ATX 标题可以是空的
Setext 标题 由一行文本(至少包含一个非空格字符,且缩进不超过 3 个空格)以及紧随其后的 Setext 标题下划线 组成。该文本行必须是那种如果不跟随 Setext 标题下划线就会被解读为段落一部分的行:它不能是代码块、标题、引用块、水平分割线或列表。Setext 标题下划线 是一系列 = 字符或一系列 - 字符,缩进不超过 3 个空格,并可带任意数量的尾随空格。如果使用 = 字符,则该标题为一级标题;如果使用 - 字符,则为二级标题。标题的内容是将第一行解析为 Markdown 行内内容后的结果。
通常,Setext 标题前后不需要空行。但是,它不能中断段落,因此当 Setext 标题出现在段落之后时,它们之间需要一个空行。
简单示例
Foo *bar*
=========
Foo *bar*
---------
<h1>Foo <em>bar</em></h1>
<h2>Foo <em>bar</em></h2>
下划线可以是任意长度
标题内容最多可以缩进三个空格,且不必与下划线对齐
四个空格缩进太多了
Setext 标题下划线最多可以缩进三个空格,且末尾可以有空格
四个空格太多了
Setext 标题下划线不能包含内部空格
内容行中的尾随空格不会导致换行
结尾的反斜杠也不会导致换行
由于块结构的指示符优先于行内结构的指示符,以下是 Setext 标题
`Foo
----
`
<a title="a lot
---
of dashes"/>
<h2>`Foo</h2>
<p>`</p>
<h2><a title="a lot</h2>
<p>of dashes"/></p>
Setext 标题下划线不能是列表项或块引用中的 懒惰续行
Setext 标题不能中断段落
但通常前后不需要空行
Setext 标题不能为空
Setext 标题文本行不能被解释为除段落以外的块结构。因此,这些例子中的破折号行被解释为水平分割线
如果你想要一个以 > foo 作为字面文本的标题,可以使用反斜杠转义
缩进代码块 由一个或多个被空行分隔的 缩进块 组成。缩进块 是一系列非空行,每一行都缩进四个或更多空格。缩进代码块不能中断段落,因此如果它出现在段落之前或之后,必须有一个中间空行。代码块的内容是各行的字面内容(包括尾随换行符),减去四个空格的缩进。缩进代码块没有任何属性。
内容为字面文本,不会被解析为 Markdown
这里我们有三个由空行分隔的片段
超过四个空格的任何初始空格都将包含在内容中,即使在内部空行中也是如此
缩进代码块不能打断段落。(这允许悬挂缩进等。)
然而,任何前导空格少于四个的非空行都会立即结束代码块。所以一个段落可能会紧接在缩进代码之后
缩进代码可以紧接在其他类型的块之前和之后
# Header
foo
Header
------
foo
----
<h1>Header</h1>
<pre><code>foo
</code></pre>
<h2>Header</h2>
<pre><code>foo
</code></pre>
<hr />
第一行可以缩进超过四个空格
缩进代码块之前或之后的空行不包含在其中
尾随空格包含在代码块的内容中
一个 代码围栏是一个至少由三个连续的反引号 (`) 或波浪号 (~) 组成的序列。(波浪号和反引号不能混用。)一个 围栏代码块以缩进不超过三个空格的代码围栏开头。
包含开头围栏的行可以在围栏后包含一些可选文本;该文本会去除首尾空格,被称为 信息字符串 (info string)。信息字符串不能包含任何反引号字符。(设置此限制的原因是,否则某些行内代码可能会被错误地解读为围栏代码块的开头。)
代码块的内容由所有后续行组成,直到出现与代码块开头相同类型(反引号或波浪号)且至少与起始代码围栏一样长的关闭 代码围栏。如果起始代码围栏缩进了 N 个空格,则每行内容会去除最多 N 个空格的缩进(如果存在)。(如果内容行没有缩进,则保持不变。如果缩进少于 N 个空格,则去除所有缩进。)
关闭代码围栏最多可以缩进三个空格,并且后面只能跟空格,这些空格会被忽略。如果达到了包含块(或文档)的末尾且未找到关闭代码围栏,则代码块包含起始代码围栏之后到包含块(或文档)末尾的所有行。(另一种规范会在未找到关闭代码围栏时要求回溯。但这会使解析效率大打折扣,且这里描述的行为似乎并没有什么真正的弊端。)
围栏代码块可以打断段落,且不需要之前或之后有空行。
代码围栏内的内容被视为字面文本,不会被解析为行内内容。信息字符串的第一个单词通常用于指定代码示例的语言,并渲染在 code 标签的 class 属性中。然而,本规范并未强制要求对信息字符串进行任何特定处理。
这是一个带有反引号的简单示例
带有波浪号的情况
关闭代码围栏必须使用与起始围栏相同的字符
关闭代码围栏必须至少与起始围栏一样长
未关闭的代码块会在文档末尾处关闭
代码块的内容可以全为空行
代码块可以为空
围栏可以缩进。如果起始围栏缩进,则内容行中的相应起始缩进将被移除(如果存在)
四个空格缩进会产生缩进代码块
关闭围栏可以缩进 0-3 个空格,且其缩进无需与起始围栏匹配
这不是关闭围栏,因为它缩进了 4 个空格
代码围栏(起始和关闭)不能包含内部空格
围栏代码块可以打断段落,且可以紧接着段落,中间不需要空行
其他块也可以出现在围栏代码块之前和之后,且中间无需空行
可以在起始代码围栏之后提供一个 信息字符串。首尾空格将被去除,第一个单词(加上 language- 前缀)将用作包含 pre 元素内的 code 元素的 class 属性值。
```ruby
def foo(x)
return 3
end
```
<pre><code class="language-ruby">def foo(x)
return 3
end
</code></pre>
~~~~ ruby startline=3 $%@#$
def foo(x)
return 3
end
~~~~~~~
<pre><code class="language-ruby">def foo(x)
return 3
end
</code></pre>
反引号代码块的信息字符串不能包含反引号
结尾的围栏代码块不能包含信息字符串
HTML 块标签 是指标签名为下列之一(不区分大小写)的 开始标签 或 结束标签:article、header、aside、hgroup、blockquote、hr、iframe、body、li、map、button、object、canvas、ol、caption、output、col、p、colgroup、pre、dd、progress、div、section、dl、table、td、dt、tbody、embed、textarea、fieldset、tfoot、figcaption、th、figure、thead、footer、tr、form、ul、h1、h2、h3、h4、h5、h6、video、script、style。
一个 HTML 块 以 HTML 块标签、HTML 注释、处理指令、声明 或 CDATA 节 开始。它在遇到 空行 或输入结束时结束。起始行最多可以缩进三个空格,后续行可以有任意缩进。HTML 块的内容被解释为原始 HTML,不会在 HTML 输出中进行转义。
一些简单的例子
<table>
<tr>
<td>
hi
</td>
</tr>
</table>
okay.
<table>
<tr>
<td>
hi
</td>
</tr>
</table>
<p>okay.</p>
这里我们有两个 HTML 块,中间有一个 Markdown 段落
在以下例子中,看起来像 Markdown 代码块的内容实际上是 HTML 块的一部分,该块一直持续到遇到空行或文档结束
注释
处理指令
CDATA
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then
{
return 1;
}
else
{
return 0;
}
}
]]>
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then
{
return 1;
}
else
{
return 0;
}
}
]]>
起始标签可以缩进 1-3 个空格,但不能缩进 4 个
HTML 块可以中断段落,且前面不需要空行。
然而,后续始终需要一个空行,除非在文档末尾
不完整的 HTML 块标签也可以开启一个 HTML 块
此规则与 John Gruber 原来的 Markdown 语法规范不同,后者指出:
唯一的限制是块级 HTML 元素(例如
<div>,<table>,<pre>,<p>等)必须与周围内容用空行分隔,且该块的开始和结束标签不应使用制表符或空格缩进。
在某些方面,Gruber 的规则比这里给出的规则更严格:
事实上,大多数 Markdown 实现(包括 Gruber 自己的一些 Perl 实现)并未施加这些限制。
然而,在有一个方面,Gruber 的规则比这里给出的规则更宽松,因为它允许 HTML 块内出现空行。这里禁止它们有两个原因。首先,它消除了解析平衡标签的需要,这很昂贵,并且如果未找到匹配的结束标签,可能需要从文档末尾回溯。其次,它提供了一种在 HTML 标签内包含 Markdown 内容的非常简单且灵活的方法:只需使用空行将 Markdown 与 HTML 分隔开。
比较:
一些 Markdown 实现采用了如果开始标签具有 markdown=1 属性就将标签内内容解释为文本的约定。上述规则似乎是一种更简单、更优雅的实现相同表达能力的方法,解析起来也简单得多。
主要潜在的缺点是人们无法再 100% 可靠地将 HTML 块粘贴到 Markdown 文档中。但是,在大多数情况下这可以正常工作,因为 HTML 中的空行通常后跟 HTML 块标签。例如:
此外,空行通常是不必要的,可以删除。特例是在 <pre> 标签内;在这里,可以用 实体替换空行。
因此,使用新规则并不会造成实质性的表达能力损失。
链接引用定义 由一个缩进不超过三个空格的 链接标签、一个冒号 (:)、可选的空白(包括最多一个换行符)、一个 链接目标、可选的空白(包括最多一个换行符)以及一个可选的 链接标题(如果存在,必须与 链接目标 由空格分隔)组成。该行之后不得再出现任何非空格字符。
链接引用定义 不对应文档的结构元素。相反,它定义了一个标签,该标签可在文档其他地方的 引用链接 和引用样式 图像 中使用。链接引用定义 可以出现在使用它们的链接之前或之后。
[Foo*bar\]]:my_(url) 'title (with parens)'
[Foo*bar\]]
<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
[Foo bar]:
<my url>
'title'
[Foo bar]
<p><a href="my%20url" title="title">Foo bar</a></p>
标题可以省略
链接目标不能省略
链接可以出现在其对应的定义之前
如果有多个匹配的定义,第一个具有优先权
如链接一节所述,标签匹配是不区分大小写的(参见 matches)。
这是一个没有对应链接的链接引用定义。它对文档没有任何贡献。
这不是一个链接引用定义,因为标题后面还有非空格字符
这不是链接引用定义,因为它缩进了四个空格
[foo]: /url "title"
[foo]
<pre><code>[foo]: /url "title"
</code></pre>
<p>[foo]</p>
这不是一个链接引用定义,因为它出现在代码块内部
链接引用定义不能中断段落。
然而,它可以直接跟在其他块元素(如标题和水平分割线)之后,且后面不需要空行。
# [Foo]
[foo]: /url
> bar
<h1><a href="/url">Foo</a></h1>
<blockquote>
<p>bar</p>
</blockquote>
多个 链接引用 可以一个接一个地出现,中间无需空行。
[foo]: /foo-url "foo"
[bar]: /bar-url
"bar"
[baz]: /baz-url
[foo],
[bar],
[baz]
<p><a href="/foo-url" title="foo">foo</a>,
<a href="/bar-url" title="bar">bar</a>,
<a href="/baz-url">baz</a></p>
链接引用定义可以出现在块容器(如列表和引用块)内部。它们影响整个文档,而不仅仅是定义它们的那个容器。
不能被解读为其他类型块的一系列非空行构成一个 段落。段落内容是将段落原始内容解析为行内内容后的结果。段落的原始内容通过连接各行并移除初始和最终空格形成。
一个有两个段落的简单示例
段落可以包含多行,但不能包含空行
段落之间的多个空行无效
前导空格会被忽略
第一行之后的各行可以有任意数量的缩进,因为缩进代码块不能中断段落。
然而,第一行的缩进最多只能有三个空格,否则会触发缩进代码块
行尾空格在行内解析前会被剔除,因此以两个或更多空格结尾的段落不会以 硬换行 结尾
块级元素之间的空行会被忽略,但在确定列表是紧凑还是松散时起作用。
文档开头和结尾的空行也会被忽略。
容器块 是指将其他块作为其内容的块。容器块有两种基本类型:引用块 和 列表项。列表 是 列表项 的元容器。
我们以递归方式定义容器块的语法。定义的一般形式是
如果 X 是一系列块,那么以某种方式转换 X 的结果就是类型为 Y 的容器,其内容为这些块。
因此,我们通过解释如何从内容中 生成 块引用或列表项来定义它们。这足以定义语法,尽管它没有提供 解析 这些结构的具体方案。(方案在下方的 解析策略 一节中提供。)
引用块标记由 0-3 个初始缩进空格,加上 (a) 字符 > 及其后的一个空格,或 (b) 一个字符 > 且其后没有空格组成。
以下规则定义了 引用块
基本情况。 如果一行字符串 Ls 构成一系列块 Bs,那么在 Ls 中每一行的开头添加一个 引用块标记,其结果就是包含 Bs 的 引用块。
惰性 (Laziness)。 如果一行字符串 Ls 构成一个内容为 Bs 的 引用块,那么如果下一行的非空格字符位于 引用块标记 之后,且属于 段落延续文本,则从这些行中删除初始的 引用块标记 后,其结果仍为内容为 Bs 的引用块。段落延续文本 是指将被解析为段落内容的一部分,但未出现在段落开头的文本。
除此之外,任何内容都不算作 引用块。
这是一个简单的例子
可以省略 > 字符后的空格
> 字符可以缩进 1-3 个空格
四个空格会产生一个代码块
惰性匹配条款允许我们在段落延续行之前省略 >
引用块可以包含一些惰性匹配行和一些非惰性匹配行
懒惰仅适用于段落的续行。包含指示块结构的字符或缩进的行不能是懒惰的。
> - foo
- bar
<blockquote>
<ul>
<li>foo</li>
</ul>
</blockquote>
<ul>
<li>bar</li>
</ul>
> foo
bar
<blockquote>
<pre><code>foo
</code></pre>
</blockquote>
<pre><code>bar
</code></pre>
> ```
foo
```
<blockquote>
<pre><code></code></pre>
</blockquote>
<p>foo</p>
<pre><code></code></pre>
引用块可以为空
引用块可以有初始或最终空行
空行总是会分隔引用块
(大多数当前的 Markdown 实现,包括 John Gruber 最初的 Markdown.pl,会将此示例解析为一个包含两个段落的单个引用块。但让作者决定想要两个还是一个引用块似乎更好。)
连续性意味着如果我们把这些引用块放在一起,我们就会得到一个单一的引用块
要获得包含两个段落的引用块,请使用
引用块可以中断段落
通常情况下,引用块前后不需要空行
> aaa
***
> bbb
<blockquote>
<p>aaa</p>
</blockquote>
<hr />
<blockquote>
<p>bbb</p>
</blockquote>
然而,由于惰性,在引用块和随后的段落之间需要一个空行
惰性规则的一个结果是,在嵌套引用块的延续行上可以省略任意数量的初始 >
> > > foo
bar
<blockquote>
<blockquote>
<blockquote>
<p>foo
bar</p>
</blockquote>
</blockquote>
</blockquote>
>>> foo
> bar
>>baz
<blockquote>
<blockquote>
<blockquote>
<p>foo
bar
baz</p>
</blockquote>
</blockquote>
</blockquote>
当在引用块中包含缩进代码块时,请记住引用块标记包括 > 及其后的一个空格。因此 > 之后需要五个空格
> code
> not code
<blockquote>
<pre><code>code
</code></pre>
</blockquote>
<blockquote>
<p>not code</p>
</blockquote>
无序列表标记是 -, + 或 * 字符。
一个 有序列表标记 是一个或多个数字 (0-9) 的序列,后跟 . 字符或 ) 字符。
以下规则定义了 列表项
例如,设 Ls 为以下几行
A paragraph
with two lines.
indented code
> A block quote.
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
设 M 为标记 1.,N = 2。规则 #1 表明以下内容是一个起始编号为 1 的有序列表项,其内容与 Ls 相同
1. A paragraph
with two lines.
indented code
> A block quote.
<ol>
<li>
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
</li>
</ol>
最重要的一点是,列表标记后的文本位置决定了后续列表项块所需的缩进量。如果列表标记占用两个空格,且列表标记与下一个非空格字符之间有三个空格,则后续块必须缩进五个空格才能归属于该列表项。
以下是一些显示内容必须缩进多少才能置于列表项之下的示例
人们很容易从列的角度去思考:延续块必须至少缩进到列表标记后第一个非空格字符的列位置。然而,这并不完全准确。列表标记后的空格决定了所需的相对缩进量。此缩进所到达的列取决于列表项如何嵌入到其他结构中,如本例所示
> > 1. one
>>
>> two
<blockquote>
<blockquote>
<ol>
<li>
<p>one</p>
<p>two</p>
</li>
</ol>
</blockquote>
</blockquote>
这里 two 出现在与列表标记 1. 相同的列中,但实际上包含在列表项中,因为在最后一个包含的块引用标记之后有足够的缩进。
反之亦然。在以下示例中,单词 two 出现在列表项初始文本 one 的远右侧,但它不被视为列表项的一部分,因为它在引用块标记之后的缩进不足
>>- one
>>
> > two
<blockquote>
<blockquote>
<ul>
<li>one</li>
</ul>
<p>two</p>
</blockquote>
</blockquote>
列表项不能包含被超过一个空行分隔的块。因此,两个空行将结束一个列表,除非这两个空行包含在围栏代码块中。
- foo
bar
- foo
bar
- ```
foo
bar
```
<ul>
<li>
<p>foo</p>
<p>bar</p>
</li>
<li>
<p>foo</p>
</li>
</ul>
<p>bar</p>
<ul>
<li>
<pre><code>foo
bar
</code></pre>
</li>
</ul>
列表项可以包含任何类型的块
1. foo
```
bar
```
baz
> bam
<ol>
<li>
<p>foo</p>
<pre><code>bar
</code></pre>
<p>baz</p>
<blockquote>
<p>bam</p>
</blockquote>
</li>
</ol>
缩进代码块需要缩进到列表项中包含文本区域边缘之外的四个空格。在下一种情况下,即为 6 个空格
在这种情况下为 11 个空格
如果列表项中的第一个块是缩进代码块,则根据规则 #2,其内容必须在列表标记之后缩进一个空格
indented code
paragraph
more code
<pre><code>indented code
</code></pre>
<p>paragraph</p>
<pre><code>more code
</code></pre>
1. indented code
paragraph
more code
<ol>
<li>
<pre><code>indented code
</code></pre>
<p>paragraph</p>
<pre><code>more code
</code></pre>
</li>
</ol>
注意,额外的空格缩进会被解释为代码块内的空格
1. indented code
paragraph
more code
<ol>
<li>
<pre><code> indented code
</code></pre>
<p>paragraph</p>
<pre><code>more code
</code></pre>
</li>
</ol>
请注意,规则 #1 和 #2 仅适用于两种情况:(a) 包含在列表项中的行以非空格字符开头的情况,以及 (b) 以缩进代码块开头的情况。在如下情况中,即第一个块以三个空格缩进开始,规则不允许我们通过缩进整体并添加列表标记来形成列表项
这不是一个显著的限制,因为当一个块以 1-3 个空格缩进开头时,缩进总是可以移除而不改变解释,从而允许应用规则 #1。所以,在上述情况下
缩进一个空格
1. A paragraph
with two lines.
indented code
> A block quote.
<ol>
<li>
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
</li>
</ol>
缩进两个空格
1. A paragraph
with two lines.
indented code
> A block quote.
<ol>
<li>
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
</li>
</ol>
缩进三个空格
1. A paragraph
with two lines.
indented code
> A block quote.
<ol>
<li>
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
</li>
</ol>
四个空格缩进产生一个代码块
1. A paragraph
with two lines.
indented code
> A block quote.
<pre><code>1. A paragraph
with two lines.
indented code
> A block quote.
</code></pre>
这是一个带有惰性延续行的示例
1. A paragraph
with two lines.
indented code
> A block quote.
<ol>
<li>
<p>A paragraph
with two lines.</p>
<pre><code>indented code
</code></pre>
<blockquote>
<p>A block quote.</p>
</blockquote>
</li>
</ol>
缩进可以被部分删除
这些示例显示了惰性匹配如何在嵌套结构中工作
> 1. > Blockquote
continued here.
<blockquote>
<ol>
<li>
<blockquote>
<p>Blockquote
continued here.</p>
</blockquote>
</li>
</ol>
</blockquote>
> 1. > Blockquote
> continued here.
<blockquote>
<ol>
<li>
<blockquote>
<p>Blockquote
continued here.</p>
</blockquote>
</li>
</ol>
</blockquote>
子列表的规则遵循上述通用规则。子列表必须缩进与段落包含在列表项中所需的相同数量的空格。
因此,在这种情况下我们需要两个空格的缩进
- foo
- bar
- baz
<ul>
<li>foo
<ul>
<li>bar
<ul>
<li>baz</li>
</ul>
</li>
</ul>
</li>
</ul>
一个空格是不够的
这里我们需要四个,因为列表标记更宽
三个空格是不够的
列表可以是列表项中的第一个块
1. - 2. foo
<ol>
<li>
<ul>
<li>
<ol start="2">
<li>foo</li>
</ol>
</li>
</ul>
</li>
</ol>
列表项可以为空
列表项可以包含标题
John Gruber 的 Markdown 规范中关于列表项的内容如下:
“列表标记通常从左边距开始,但最多可以缩进三个空格。列表标记必须后跟一个或多个空格或一个制表符。”
“为了让列表看起来美观,你可以用悬挂缩进包装项目……但如果你不想这样做,也没必要。”
“列表项可以由多个段落组成。列表项中的每个后续段落必须缩进四个空格或一个制表符。”
“缩进后续段落的每一行看起来很不错,但同样,Markdown 会允许你变得懒惰。”
“要在列表项中放入引用块,引用块的 > 分隔符需要缩进。”
“要在列表项中放入代码块,代码块需要缩进两次——8 个空格或两个制表符。”
这些规则指定列表项下的段落必须缩进四个空格(据推测是从左边距,而不是列表标记的开头,但这未说明),并且列表项下的代码必须缩进八个空格而不是通常的四个。它们还说引用块必须缩进,但没有说明缩进多少;然而,给出的示例有四个空格的缩进。虽然没有提到其他类型的块级内容,但推断列表项下的所有块元素(包括其他列表)都必须缩进四个空格当然是合理的。这一原则被称为四空格规则。
四空格规则清晰且有原则,如果参考实现 Markdown.pl 遵循它,它可能会成为标准。然而,Markdown.pl 至少在外部层级上允许段落和子列表以仅两个空格的缩进开始。更糟糕的是,其行为不一致:外部列表的子列表需要两个空格缩进,但该子列表的子列表却需要三个空格。因此,Markdown 的不同实现为确定列表项下的内容开发了截然不同的规则并不奇怪。(例如,Pandoc 和 python-Markdown 坚持 Gruber 的语法描述和四空格规则,而 discount、redcarpet、marked、PHP Markdown 等则更紧密地遵循 Markdown.pl 的行为。)
不幸的是,考虑到实现之间的分歧,没有办法给出一个保证不会破坏现有文档的列表项规范。然而,此处给出的规范应该能正确处理使用四空格规则或更宽容的 Markdown.pl 行为格式化的列表,前提是它们的布局方式对于人类阅读来说是自然的。
这里的策略是让列表标记的宽度和缩进决定块归属于列表项所需的缩进,而不是设置一个固定且随意的数字。作者可以将列表项的主体视为一个整体,为了容纳列表标记(以及列表标记上的任何缩进),将其向右缩进足够的距离。(惰性规则 #4 随后允许延续行在需要时取消缩进。)
我们认为此规则优于任何要求从边距固定缩进级别的规则。四空格规则很清晰但不自然。以下内容:
- foo
bar
- baz
被解析为两个列表并伴随一个插入的段落,
<ul>
<li>foo</li>
</ul>
<p>bar</p>
<ul>
<li>baz</li>
</ul>
如四空格规则所要求的那样,而不是一个单一的列表,这非常不直观。
<ul>
<li>
<p>foo</p>
<p>bar</p>
<ul>
<li>baz</li>
</ul>
</li>
</ul>
四个空格的选择是任意的。它可以被学习,但不太可能被猜到,而且经常让初学者受挫。
采用双空格规则有帮助吗?问题在于,这样的规则,加上允许列表标记初始缩进 1-3 个空格的规则,允许缩进少于原始列表标记的文本包含在列表项中。例如,Markdown.pl 将:
- one
two
解析为单个列表项,其中 two 是延续段落
<ul>
<li>
<p>one</p>
<p>two</p>
</li>
</ul>
同样
> - one
>
> two
作为
<blockquote>
<ul>
<li>
<p>one</p>
<p>two</p>
</li>
</ul>
</blockquote>
这是极其不直观的。
我们不需要从边距进行固定缩进,而是可以要求从列表标记(其本身可能已缩进)进行固定缩进(比如两个空格,甚至一个空格)。该提案将消除上述最后一个异常。与上述规范不同,它会将以下内容视为带有子段落的列表项,即使段落 bar 的缩进程度没有第一段 foo 那么大:
10. foo
bar
可以说这段文本读起来确实像一个列表项,而 bar 是一个子段落,这可能对该提案有利。然而,按照这个提案,缩进代码在列表标记后必须缩进六个空格。这将破坏许多现有的 Markdown,它们具有以下模式:
1. foo
indented code
其中代码缩进八个空格。相比之下,上述规范将按预期解析此文本,因为代码块的缩进是从 foo 的开头开始测量的。
唯一需要特殊处理的情况是以缩进代码开头的列表项。在这种情况下需要多少缩进,因为我们没有“第一段”来测量?规则 #2 只是规定,在这种情况下,我们要求列表标记缩进一个空格(然后是缩进代码通常的四个空格)。在列表标记加上其初始缩进占用四个空格的情况下(一种常见情况),这将符合四空格规则,但在其他情况下则会分歧。
列表 是一个或多个 相同类型 的列表项序列。列表项之间可以用单个 空行 分隔,但两个空行会结束所有包含列表。
如果两个列表项以相同类型的列表标记开头,则它们属于同一类型。如果 (a) 它们是使用相同字符 (-, + 或 *) 的无序列表标记,或 (b) 它们是具有相同分隔符 (. 或 )) 的有序列表编号,则两个列表标记属于同一类型。
如果列表由有序列表标记开头,则列表为有序列表;如果由无序列表标记开头,则为无序列表。
有序列表的起始编号由其初始列表项的列表编号决定。后续列表项的编号将被忽略。
如果列表的任何组成列表项之间由空行分隔,或者任何组成列表项直接包含两个中间带有空行的块级元素,则该列表为 松散列表 (loose)。否则,列表为 紧凑列表 (tight)。(HTML 输出的区别在于,松散列表中的段落会被包裹在 <p> 标签中,而紧凑列表中的段落则不会。)
更改无序或有序列表分隔符会开始一个新的列表
1. foo
2. bar
3) baz
<ol>
<li>foo</li>
<li>bar</li>
</ol>
<ol start="3">
<li>baz</li>
</ol>
在 CommonMark 中,列表可以中断段落。也就是说,段落和随后的列表之间不需要空行
Markdown.pl 不允许这样做,因为它担心会在硬换行中触发带有数字的列表
The number of windows in my house is
14. The number of doors is 6.
<p>The number of windows in my house is</p>
<ol start="14">
<li>The number of doors is 6.</li>
</ol>
奇怪的是,Markdown.pl 确实允许引用块中断段落,尽管同样的考虑可能适用。我们认为这两种情况应该同样处理。以下是允许列表中断段落的两个原因
首先,人们在没有空行的情况下开始列表是很自然的,这并不罕见
I need to buy
- new shoes
- a coat
- a plane ticket
其次,我们被一种
一致性原则 (principle of uniformity):如果一段文本具有某种含义,那么当它被放入列表项时,它将继续保持相同的含义。
(实际上,列表项 的规范预设了这一点。) 该原则暗示,如果
* I need to buy
- new shoes
- a coat
- a plane ticket
是一个包含段落后跟嵌套子列表的列表项(正如所有 Markdown 实现所同意的那样,尽管由于列表是“紧凑的”,段落可能在渲染时没有 <p> 标签),那么
I need to buy
- new shoes
- a coat
- a plane ticket
它本身应该是一个段落后跟一个嵌套子列表。
我们对统一原则的坚持使我们倾向于认为有两种连贯的方案
在所有列表和引用块之前要求空行,包括作为列表项内部子列表出现的列表。
在这些地方都不要求空行。
reStructuredText 采取了第一种方法,这很有道理。但第二种方法似乎与 Markdown 既定的实践更加一致。
项之间可以有空行,但两个空行结束列表
- foo
- bar
- baz
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
</li>
</ul>
<ul>
<li>baz</li>
</ul>
正如上面关于 列表项 的章节中所述,列表项 内部 块之间的两个空行也会结束一个列表
事实上,两个空行将结束所有包含的列表
- foo
- bar
- baz
bim
<ul>
<li>foo
<ul>
<li>bar
<ul>
<li>baz</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code> bim
</code></pre>
因此,两个空行可用于分隔相同类型的连续列表,或将列表与本应被解析为最终列表项的子段落的缩进代码块分开
- foo
- bar
- baz
- bim
<ul>
<li>foo</li>
<li>bar</li>
</ul>
<ul>
<li>baz</li>
<li>bim</li>
</ul>
- foo
notcode
- foo
code
<ul>
<li>
<p>foo</p>
<p>notcode</p>
</li>
<li>
<p>foo</p>
</li>
</ul>
<pre><code>code
</code></pre>
列表项不需要缩进到同一级别。以下列表项将被视为同一列表级别的项目,因为没有一个缩进足够多而属于前一个列表项
- a
- b
- c
- d
- e
- f
- g
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
<li>f</li>
<li>g</li>
</ul>
这是一个松散列表,因为两个列表项之间有一个空行
这也是,第二个项为空
这些是松散列表,即使项之间没有空格,因为其中一个项直接包含两个中间有空行的块级元素
- a
- b
c
- d
<ul>
<li>
<p>a</p>
</li>
<li>
<p>b</p>
<p>c</p>
</li>
<li>
<p>d</p>
</li>
</ul>
- a
- b
[ref]: /url
- d
<ul>
<li>
<p>a</p>
</li>
<li>
<p>b</p>
</li>
<li>
<p>d</p>
</li>
</ul>
这是一个紧凑列表,因为空行在代码块中
- a
- ```
b
```
- c
<ul>
<li>a</li>
<li>
<pre><code>b
</code></pre>
</li>
<li>c</li>
</ul>
这是一个紧凑列表,因为空行位于子列表的两个段落之间。因此子列表是松散的,而外部列表是紧凑的
这个列表是紧凑的,因为空行在引用块内
这个列表是紧凑的,因为连续的块元素没有被空行分隔
- a
> b
```
c
```
- d
<ul>
<li>a
<blockquote>
<p>b</p>
</blockquote>
<pre><code>c
</code></pre>
</li>
<li>d</li>
</ul>
单段落列表是紧凑的
这里外部列表是松散的,内部列表是紧凑的
- a
- b
- c
- d
- e
- f
<ul>
<li>
<p>a</p>
<ul>
<li>b</li>
<li>c</li>
</ul>
</li>
<li>
<p>d</p>
<ul>
<li>e</li>
<li>f</li>
</ul>
</li>
</ul>
行内元素是从字符流的开头到结尾(从左到右,在从左到右的语言中)依次解析的。例如:
hi 被解析为代码,末尾的反引号作为字面量反引号保留。
任何 ASCII 标点字符都可以使用反斜杠转义
\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
<p>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</p>
其他字符前的反斜杠被视为字面量反斜杠
转义字符被视为普通字符,不具有其通常的 Markdown 含义
\*not emphasized*
\<br/> not a tag
\[not a link](/foo)
\`not code`
1\. not a list
\* not a list
\# not a header
\[foo]: /url "not a reference"
<p>*not emphasized*
<br/> not a tag
[not a link](/foo)
`not code`
1. not a list
* not a list
# not a header
[foo]: /url "not a reference"</p>
如果反斜杠本身被转义,则随后的字符不被转义
行尾的反斜杠是 硬换行
反斜杠转义在代码块、代码片段、自动链接或原生 HTML 中不起作用
<http://example.com?find=\*>
<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
但它们在所有其他上下文中都有效,包括 URL 和链接标题、链接引用,以及 围栏代码块 中的信息字符串
为了使该标准尽可能与 HTML 无关,在任何上下文中的所有有效 HTML 实体都会被识别并转换为 Unicode 字符,然后再存储到 AST 中。
这使得针对 HTML 输出的实现可以在生成 HTML 时轻松地转义实体,并简化了针对其他语言的实现的编写工作,因为这些实现只需要处理 Unicode 字符,而无需感知 HTML 实体。
命名实体 由 & + 任意有效 HTML5 实体名称 + ; 组成。下列文档 被用作有效实体名称及其对应码位的权威来源。
针对 HTML 的兼容实现不需要生成所有现有的有效命名实体,除了 " (")、& (&)、< (<) 和 > (>),出于安全原因,这些实体始终必须以实体形式编写。
& © Æ Ď ¾ ℋ ⅆ ∲
<p> & © Æ Ď ¾ ℋ ⅆ ∲</p>
十进制实体 由 &# + 1-8 位阿拉伯数字串 + ; 组成。同样,这些实体需要被识别并转换为对应的 UTF8 码位。无效的 Unicode 码位将写入为“未知码位”字符 (0xFFFD)
十六进制实体 由 &# + X 或 x + 1-8 个十六进制数字的字符串 + ; 组成。它们也会被解析并在 AST 中转换为对应的 UTF8 值。
这里是一些非实体引用
  &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
<p>&nbsp &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;</p>
尽管 HTML5 确实接受某些没有末尾分号的实体(如 ©),但此处它们不被识别为实体,因为这会使语法变得过于模棱两可
不在 HTML5 命名实体列表中的字符串也不会被识别为实体
实体在代码片段或代码块之外的任何上下文中均被识别,包括原生 HTML、URL、链接标题 和 围栏代码块 的信息字符串
[foo](/föö "föö")
<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
[foo]
[foo]: /föö "föö"
<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
实体在代码 span 和代码块中被视为字面文本
反引号字符串是指一串包含一个或多个反引号字符(`)的字符串,且其前后均无反引号。
代码片段 以反引号字符串开头,并以等长的反引号字符串结尾。代码片段的内容是两个反引号字符串之间的字符,去除首尾的空格和换行符,并将连续的空格和换行符压缩为单个空格。
这是一个简单的代码跨度
这里使用了两个反引号,因为代码中包含一个反引号。此示例还演示了如何去除前导和尾随空格。
此示例展示了去除前导和尾随空格的原因。
换行符被视为与空格相同
内部的空格和换行符被压缩为单个空格,就像浏览器所做的那样
问:既然浏览器无论如何都会折叠空格,为什么不直接保留它们呢?答:因为我们可能需要面向非 HTML 格式,我们不应依赖特定于 HTML 的渲染假设。
(现有的实现对内部空格和换行符的处理方式各不相同。包括 Markdown.pl 和 showdown 在内的一些实现会将内部换行符转换为 <br /> 标签。但这给那些喜欢硬换行段落的人带来了困难,因为代码片段中间的换行会导致输出中出现意想不到的换行。其他实现则只是按原样保留内部空格,如果目标仅是 HTML,这没有问题。)
请注意,反斜杠转义在代码跨度中不起作用。所有反斜杠均被视为字面字符。
反斜杠转义永远是不需要的,因为人们总是可以选择包含 n 个反引号字符的字符串作为定界符,而代码本身不包含任何恰好为 n 个反引号的字符串。
代码跨度的反引号优先级高于除了 HTML 标签和自动链接之外的任何行内结构。因此,例如,这不会被解析为强调文本,因为第二个 * 是代码跨度的一部分。
且这也不会被解析为链接。
但这是一个链接
这是一个 HTML 标签
当反引号字符串没有匹配的反引号字符串闭合时,我们得到的只是字面意义上的反引号。
John Gruber 原版的 Markdown 语法描述 说道:
Markdown 将星号(
*)和下划线(_)视为强调的指示符。被单个*或_包围的文本将被 HTML<em>标签包裹;双星号或双下划线将被 HTML<strong>标签包裹。
这对于大多数用户来说足够了,但这些规则在处理嵌套强调时留下了许多不确定性。原版 Markdown.pl 测试套件清楚地表明,三重 *** 和 ___ 定界符可以用于强强调,并且大多数实现也都允许以下模式。
***strong emph***
***strong** in emph*
***emph* in strong**
**in strong *emph***
*in emph **strong***
以下模式支持度较低,但意图明确且很有用(特别是在参考文献条目等上下文中)。
*emph *with emph* in it*
**strong **with strong** in it**
许多实现还限制了词内强调仅能使用 * 形式,以避免在包含内部下划线的单词中产生不必要的强调。(最好将其放在代码跨度中,但用户往往不会这样做。)
internal emphasis: foo*bar*baz
no emphasis: foo_bar_baz
以下规则捕获了所有这些模式,同时允许不进行回溯的高效解析策略
单个 * 字符 可以开启强调,当且仅当其后没有空白字符。
单个 _ 字符 可以开启强调,当且仅当其后没有空白字符,且其前不是 ASCII 字母数字字符。
单个 * 字符 可以关闭强调,当且仅当其前没有空白字符。
单个 _ 字符 可以关闭强调,当且仅当其前没有空白字符,且其后不是 ASCII 字母数字字符。
双 ** 可以开启加粗强调,当且仅当其后没有空白字符。
双 __ 可以开启加粗强调,当且仅当其后没有空白字符,且其前不是 ASCII 字母数字字符。
双 ** 可以关闭加粗强调,当且仅当其前没有空白字符。
双 __ 可以关闭加粗强调,当且仅当其前没有空白字符,且其后不是 ASCII 字母数字字符。
强调以 可以开启强调 的定界符开始,并以 可以关闭强调 的定界符结束,且使用与开启定界符相同的字符(_ 或 *)。开启定界符和关闭定界符之间必须存在非空的行内序列;这些形成了强调行内的内容。
强强调以 可以开启强强调 的定界符开始,并以 可以关闭强强调 的定界符结束,且使用与开启定界符相同的字符(_ 或 *)。开启定界符和关闭定界符之间必须存在非空的行内序列;这些形成了强强调行内的内容。
除非经过反斜杠转义,否则字面意义上的 * 字符不能出现在 * 定界的强调或 ** 定界的强强调的开头或结尾。
除非经过反斜杠转义,否则字面意义上的 _ 字符不能出现在 _ 定界的强调或 __ 定界的强强调的开头或结尾。
在上述 1–12 条规则与多种解析结果兼容的情况下,以下原则用于解决歧义。
应尽可能减少嵌套数量。因此,例如,解释为 <strong>...</strong> 总是优先于 <em><em>...</em></em>。
解释为 <strong><em>...</em></strong> 总是优先于 <em><strong>..</strong></em>。
当两个潜在的强调或加粗强调范围重叠(即第二个在第一个结束之前开始,并在第一个结束之后结束)时,优先选择第一个。因此,例如 *foo _bar* baz_ 被解析为 <em>foo _bar</em> baz_ 而不是 *foo <em>bar* baz</em>。出于同样的原因,**foo*bar** 被解析为 <em><em>foo</em>bar</em>* 而不是 <strong>foo*bar</strong>。
当存在具有相同关闭定界符的两个潜在强调或加粗强调范围时,优先选择较短的(后开启的那个)。因此,例如 **foo **bar baz** 被解析为 **foo <strong>bar baz</strong> 而不是 <strong>foo **bar baz</strong>。
行内代码跨度、链接、图像和 HTML 标签的结合比强调更紧密。因此,当在包含这些元素与不包含这些元素的解释之间进行选择时,前者总是胜出。因此,例如,*[foo*](bar) 被解析为 *<a href="bar">foo*</a> 而不是 <em>[foo</em>](bar)。
这些规则可以通过一系列示例来说明。
规则 1
这不是强调,因为开头的 * 后面有空格
允许使用 * 进行词内强调。
规则 2
这不是强调,因为开头的 * 后面有空格
不允许在 ASCII 单词内部使用 _ 进行强调
但允许在非 ASCII 单词内部使用
规则 3
这不是强调,因为关闭的 * 前面有空格。
允许使用 * 进行词内强调。
规则 4
这不是强调,因为关闭的 _ 前面有空格。
词内强调
规则 5
这不是强强调,因为开启定界符后跟有空格。
允许使用 ** 进行词内强强调。
规则 6
这不是强强调,因为开启定界符后跟有空格。
单词内强调示例
规则 7
这不是强强调,因为关闭定界符前面有空格。
(根据规则 11,它也不能被解释为强调的 *foo bar *。)
词内强调
规则 8
这不是强强调,因为关闭定界符前面有空格。
单词内加粗强调示例
规则 9
任何非空的行内元素序列都可以作为强调跨度的内容。
特别是,强调和强强调可以嵌套在强调内。
但请注意:
区别在于,在上述情况中,内部定界符 可以关闭强调,而在有空格的情况下,它们不能。
注意,在以下情况中我们无法得到强强调,因为开启定界符在 bar 之前的第一个 * 处即被关闭。
支持无限级的嵌套。
*foo **bar *baz* bim** bop*
<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>
不存在空强调或空强强调。
规则 10
任何非空的行内元素序列都可以作为强强调跨度的内容。
特别是,强调和强强调可以嵌套在强强调内。
但请注意:
区别在于,在上述情况中,内部定界符 可以关闭强调,而在有空格的情况下,它们不能。
支持无限级的嵌套。
**foo *bar **baz**
bim* bop**
<p><strong>foo <em>bar <strong>baz</strong>
bim</em> bop</strong></p>
不存在空强调或空强强调。
规则 11
请注意,当定界符不均匀匹配时,规则 11 确定多余的字面 * 字符将出现在强调之外,而不是之内。
规则 12
请注意,当定界符不均匀匹配时,规则 12 确定多余的字面 _ 字符将出现在强调之外,而不是之内。
规则 13 意味着,如果您希望强调直接嵌套在强调内,则必须使用不同的定界符。
然而,在不切换定界符的情况下,在加粗强调内进行加粗强调是可能的
规则 13 可应用于任意长度的定界符序列。
规则 14
规则 15
规则 16
规则 17
**a<http://foo.bar?q=**>
<p>**a<a href="http://foo.bar?q=**">http://foo.bar?q=**</a></p>
__a<http://foo.bar?q=__>
<p>__a<a href="http://foo.bar?q=__">http://foo.bar?q=__</a></p>
链接包含 链接文本(可见文本)、目标(作为链接目标的 URI)以及可选的 链接标题。Markdown 中有两种基本的链接类型。在 行内链接 中,目标和标题紧跟在链接文本之后。在 引用链接 中,目标和标题在文档的其他地方定义。
链接文本由方括号([ 和 ])括起来的零个或多个行内元素序列组成。适用以下规则:
链接在任何嵌套级别都不能包含其他链接。
方括号仅在以下情况下允许出现在 链接文本 中:(a) 它们经过反斜杠转义,或 (b) 它们作为匹配的一对括号出现,包含一个开括号 [、零个或多个行内元素,以及一个闭括号 ]。
反引号 代码 span、自动链接 和原始 HTML 标签 比链接文本中的方括号结合得更紧密。因此,例如,[foo`]` 不能作为链接文本,因为第二个 ] 是代码 span 的一部分。
链接文本中的方括号结合比 强调和强强调 的标记更紧密。因此,例如,*[foo*](url) 是一个链接。
链接目标由以下两者之一组成:
开启 < 和闭合 > 之间零个或多个字符的序列,不包含换行符或未转义的 < 或 > 字符,或者
一个非空的字符序列,不包含 ASCII 空格或控制字符,仅在满足以下情况时包含括号:(a) 它们被反斜杠转义,或 (b) 它们是平衡的未转义括号对的一部分,且该对本身不在另一个平衡的未转义括号对中。
链接标题由以下两者之一组成:
直双引号字符(")之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 " 字符,或者
直单引号字符(')之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 ' 字符,或者
匹配的圆括号((...))之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 ) 字符。
行内链接 由 链接文本 后跟一个左括号 (、可选的空白、可选的 链接目标、可选的与链接目标由空格分隔的 链接标题、可选的空白以及一个右括号 ) 组成。链接文本由 链接文本 中包含的行内元素组成(排除包围的方括号)。链接的 URI 由链接目标组成,排除包围的 <...>(如果存在),反斜杠转义如上所述生效。链接标题由链接标题组成,排除其包围定界符,反斜杠转义如上所述生效。
这是一个简单的行内链接:
标题可以省略
标题和目标均可省略。
如果目标包含空格,必须将其括在尖括号中。
目标不能包含换行符,即使使用尖括号也是如此。
在不进行转义的情况下,允许使用一层平衡的圆括号。
但是,如果圆括号内有圆括号,则需要进行转义或使用 <...> 形式。
圆括号和其他符号也可以按照 Markdown 中的惯例进行转义。
URL 转义在目标内部应保持原样,因为所有 URL 转义字符也是有效的 URL 字符。目标中的 HTML 实体将像往常一样解析为它们的 UTF-8 码位,并在写入 HTML 时可选地进行 URL 转义。
请注意,由于标题通常可以被解析为目标,如果您尝试省略目标并保留标题,则会得到意外结果。
标题可以使用单引号、双引号或圆括号。
[link](/url "title")
[link](/url 'title')
[link](/url (title))
<p><a href="/url" title="title">link</a>
<a href="/url" title="title">link</a>
<a href="/url" title="title">link</a></p>
反斜杠转义和实体可以在标题中使用
除非经过转义,否则不允许嵌套平衡的引号。
[link](/url "title "and" title")
<p>[link](/url "title "and" title")</p>
但可以通过使用不同的引号类型轻松解决此问题。
[link](/url 'title "and" title')
<p><a href="/url" title="title "and" title">link</a></p>
(注:Markdown.pl 确实允许在双引号标题中使用双引号,其测试套件包含一个证明此点的测试。但很难看出这种额外复杂性带来的合理性,因为已经有多种方式——反斜杠转义、实体或为封闭标题使用不同的引号类型——来编写包含双引号的标题。Markdown.pl 对标题的处理还有许多其他奇怪的特性。例如,它允许在行内链接中使用单引号标题,但不支持引用链接。而且,在引用链接中,它允许标题以 " 开始并以 ) 结束,这在行内链接中是不允许的。Markdown.pl 1.0.1 甚至允许没有结束引号的标题,尽管 1.0.2b8 不允许。采用一个简单、合理的规则,在行内链接和链接引用定义中以同样方式工作,显然更为可取。)
目标和标题周围允许有空白
但链接文本和随后的圆括号之间不允许使用。
链接文本可以包含平衡的括号,但不能包含不平衡的括号,除非它们经过转义。
链接文本可以包含行内内容。
[link *foo **bar** `#`*](/uri)
<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
但是,链接不得包含其他链接,无论嵌套层级如何。
[foo *[bar [baz](/uri)](/uri)*](/uri)
<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>
这些情况说明了链接文本分组优于强调分组的优先级。
这些情况说明了 HTML 标签、代码跨度和自动链接优于链接分组的优先级。
[foo<http://example.com?search=](uri)>
<p>[foo<a href="http://example.com?search=%5D(uri)">http://example.com?search=](uri)</a></p>
完整引用链接 由 链接文本、可选的空白以及一个与文档中其他地方的 链接引用定义 匹配 的 链接标签 组成。
一个 链接标签 以左括号 ([) 开始,并以第一个未被反斜杠转义的右括号 (]) 结束。链接标签 中不允许出现未转义的方括号字符。链接标签内的方括号字符数最多为 999 个。
一个标签 匹配 另一个标签,当且仅当它们的规范形式相等。要规范化一个标签,请执行 Unicode 大小写折叠 (case fold) 并将连续的内部空白压缩为单个空格。如果存在多个匹配的引用链接定义,则使用文档中首先出现的那个。(在这种情况下,发出警告是可取的。)
第一个链接标签的内容被解析为行内元素,用作链接的文本。链接的 URI 和标题由匹配的 链接引用定义 提供。
这是一个简单的例子
链接文本可以包含平衡的括号,但不能包含不平衡的括号,除非它们经过转义。
链接文本可以包含行内内容。
[link *foo **bar** `#`*][ref]
[ref]: /uri
<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
[][ref]
[ref]: /uri
<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
但是,链接不得包含其他链接,无论嵌套层级如何。
[foo [bar](/uri)][ref]
[ref]: /uri
<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>
[foo *bar [baz][ref]*][ref]
[ref]: /uri
<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>
(在上述示例中,我们有两个 快捷引用链接,而不是一个 完整引用链接。)
以下情况说明了链接文本分组优于强调分组的优先级。
这些情况说明了 HTML 标签、代码跨度和自动链接优于链接分组的优先级。
[foo<http://example.com?search=][ref]>
[ref]: /uri
<p>[foo<a href="http://example.com?search=%5D%5Bref%5D">http://example.com?search=][ref]</a></p>
匹配时大小写不敏感。
使用 Unicode 大小写折叠。
[Толпой][Толпой] is a Russian word.
[ТОЛПОЙ]: /url
<p><a href="/url">Толпой</a> is a Russian word.</p>
为了确定匹配,连续的内部空白被视为一个空格
当存在多个匹配的 链接引用定义 时,使用第一个。
请注意,匹配是在规范化字符串上执行的,而不是在解析的行内内容上。因此,以下内容不匹配,即使标签定义了等效的行内内容。
链接标签 不能包含括号,除非它们经过反斜杠转义。
折叠引用链接 由与文档中其他地方的 链接引用定义 匹配 的 链接标签、可选的空白以及字符串 [] 组成。第一个链接标签的内容被解析为行内元素,用作链接文本。链接的 URI 和标题由匹配的引用链接定义提供。因此,[foo][] 等同于 [foo][foo]。
[*foo* bar][]
[*foo* bar]: /url "title"
<p><a href="/url" title="title"><em>foo</em> bar</a></p>
链接标签不区分大小写。
与完整引用链接一样,两组方括号之间允许有空白
快捷引用链接由一个 链接标签 组成,该标签 匹配 文档其他位置的 链接引用定义,且后面没有 [] 或其他链接标签。第一个链接标签的内容被解析为行内元素,用作链接的文本。链接的 URI 和标题由匹配的链接引用定义提供。因此,[foo] 等同于 [foo][]。
[*foo* bar]
[*foo* bar]: /url "title"
<p><a href="/url" title="title"><em>foo</em> bar</a></p>
[[*foo* bar]]
[*foo* bar]: /url "title"
<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
链接标签不区分大小写。
链接文本后的空格应予以保留。
如果您只想使用方括号文本,可以对左方括号进行反斜杠转义以避免链接。
请注意,这是一个链接,因为链接标签以第一个随后的闭合方括号结束。
出于同样的原因,这也是一个链接
完整引用优先于快捷引用。
在以下情况中,[bar][baz] 被解析为引用,[foo] 被解析为普通文本。
然而在这里,[foo][bar] 被解析为引用,因为定义了 [bar]。
[foo][bar][baz]
[baz]: /url1
[bar]: /url2
<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
在这里 [foo] 没有被解析为快捷引用,因为它后面紧跟一个链接标签(即使 [bar] 没有定义)。
图像语法类似于链接语法,但有一点不同。我们拥有的不是 链接文本,而是 图像描述。其规则与 链接文本 相同,除了 (a) 图像描述以 ![ 开头而不是 [,以及 (b) 图像描述可以包含链接。图像描述将行内元素作为其内容。当图像渲染为 HTML 时,这通常用作图像的 alt 属性。
![foo *bar*]
[foo *bar*]: train.jpg "train & tracks"
<p><img src="train.jpg" alt="foo bar" title="train & tracks" /></p>
尽管本规范关注的是解析而不是渲染,但建议在渲染为 HTML 时,仅使用 图像描述 的纯字符串内容。请注意,在上面的示例中,alt 属性的值是 foo bar,而不是 foo [bar](/url) 或 foo <a href="/url">bar</a>。只有纯字符串内容被渲染,不带任何格式。
![foo *bar*][]
[foo *bar*]: train.jpg "train & tracks"
<p><img src="train.jpg" alt="foo bar" title="train & tracks" /></p>
![foo *bar*][foobar]
[FOOBAR]: train.jpg "train & tracks"
<p><img src="train.jpg" alt="foo bar" title="train & tracks" /></p>
My 
<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
引用式:
折叠式:
![*foo* bar][]
[*foo* bar]: /url "title"
<p><img src="/url" alt="foo bar" title="title" /></p>
标签不区分大小写。
与完整引用链接一样,两组方括号之间允许有空白
快捷式:
![*foo* bar]
[*foo* bar]: /url "title"
<p><img src="/url" alt="foo bar" title="title" /></p>
请注意,链接标签不能包含未转义的括号。
链接标签不区分大小写。
如果您只想使用方括号文本,可以对开启的 ! 和 [ 进行反斜杠转义。
如果您想在字面意义上的 ! 之后使用链接,请对 ! 进行反斜杠转义。
自动链接是位于 < 和 > 之间的绝对 URI 和电子邮件地址。它们被解析为链接,并以该 URL 或电子邮件地址作为链接标签。
URI 自动链接由 < 紧随一个不包含 < 的 绝对 URI,再紧随 > 组成。它被解析为指向该 URI 的链接,并以 URI 作为链接标签。
绝对 URI 在这些目的下由一个 方案 后跟一个冒号 (:),再后跟零个或多个除 ASCII 空白字符、控制字符、< 和 > 以外的字符组成。如果 URI 包含这些字符,则必须使用百分号编码(例如 %20 表示空格)。
识别以下 方案(不区分大小写):coap, doi, javascript, aaa, aaas, about, acap, cap, cid, crid, data, dav, dict, dns, file, ftp, geo, go, gopher, h323, http, https, iax, icap, im, imap, info, ipp, iris, iris.beep, iris.xpc, iris.xpcs, iris.lwz, ldap, mailto, mid, msrp, msrps, mtqp, mupdate, news, nfs, ni, nih, nntp, opaquelocktoken, pop, pres, rtsp, service, session, shttp, sieve, sip, sips, sms, snmp, soap.beep, soap.beeps, tag, tel, telnet, tftp, thismessage, tn3270, tip, tv, urn, vemmi, ws, wss, xcon, xcon-userid, xmlrpc.beep, xmlrpc.beeps, xmpp, z39.50r, z39.50s, adiumxtra, afp, afs, aim, apt, attachment, aw, beshare, bitcoin, bolo, callto, chrome, chrome-extension, com-eventbrite-attendee, content, cvs, dlna-playsingle, dlna-playcontainer, dtn, dvb, ed2k, facetime, feed, finger, fish, gg, git, gizmoproject, gtalk, hcp, icon, ipn, irc, irc6, ircs, itms, jar, jms, keyparc, lastfm, ldaps, magnet, maps, market, message, mms, ms-help, msnim, mumble, mvn, notes, oid, palm, paparazzi, platform, proxy, psyc, query, res, resource, rmi, rsync, rtmp, secondlife, sftp, sgn, skype, smb, soldat, spotify, ssh, steam, svn, teamspeak, things, udp, unreal, ut2004, ventrilo, view-source, webcal, wtai, wyciwyg, xfire, xri, ymsgr。
以下是一些有效的自动链接:
<http://foo.bar.baz?q=hello&id=22&boolean>
<p><a href="http://foo.bar.baz?q=hello&id=22&boolean">http://foo.bar.baz?q=hello&id=22&boolean</a></p>
<irc://foo.bar:2233/baz>
<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
大写字母也可以。
自动链接中不允许出现空格。
一个 电子邮件自动链接 由 <,后跟 电子邮件地址,后跟 > 组成。链接标签为该电子邮件地址,URL 为 mailto: 后跟该电子邮件地址。
电子邮件地址 在这些目的下是指任何匹配 HTML5 规范中非规范性正则 的内容
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
电子邮件自动链接示例:
<foo@bar.example.com>
<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
<foo+special@Bar.baz-bar0.com>
<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
这些不是自动链接:
< 和 > 之间看起来像 HTML 标签的文本会被解析为原始 HTML 标签,并将直接在 HTML 中渲染而不会进行转义。标签和属性名称不限于当前的 HTML 标签,因此可以使用自定义标签(甚至可以例如使用 DocBook 标签)。
这是标签语法:
一个 标签名称 由一个 ASCII 字母开头,后跟零个或多个 ASCII 字母或数字。
属性名称由 ASCII 字母、_ 或 : 开头,后跟零个或多个 ASCII 字母、数字、_、.、: 或 -。(注意:这是受限于 ASCII 的 XML 规范。HTML5 则更宽松。)
属性值规范 由可选的空白、一个 = 字符、可选的空白和一个 属性值 组成。
一个 属性值 由 无引号属性值、单引号属性值 或 双引号属性值 组成。
无引号属性值是一个非空字符串,不包括空格、"、'、=、<、> 或 `。
单引号属性值由 '、零个或多个不包含 ' 的字符,以及一个最后的 ' 组成。
双引号属性值由 "、零个或多个不包含 " 的字符,以及一个最后的 " 组成。
开始标签 由一个 < 字符、一个 标签名、零个或多个 属性、可选的空白、一个可选的 / 字符和一个 > 字符组成。
结束标签 由字符串 </、一个 标签名、可选的空白和字符 > 组成。
HTML 注释 由字符串 <!--、不包含字符串 -- 的字符字符串以及字符串 --> 组成。
一个 处理指令 (processing instruction) 由字符串 <?、一段不包含字符串 ?> 的字符序列,以及字符串 ?> 组成。
声明 由字符串 <!、一个由一个或多个大写 ASCII 字母组成的名称、空白、不包含字符 > 的字符字符串以及字符 > 组成。
一个 CDATA 部分 (CDATA section) 由字符串 <![CDATA[、一段不包含字符串 ]]> 的字符序列,以及字符串 ]]> 组成。
HTML 标签 由 开始标签、结束标签、HTML 注释、处理指令、元素类型声明 或 CDATA 节 组成。
以下是一些简单的开始标签
空元素
允许存在空白
带有属性
<a foo="bar" bam = 'baz <em>"</em>'
_boolean zoop:33=zoop:33 />
<p><a foo="bar" bam = 'baz <em>"</em>'
_boolean zoop:33=zoop:33 /></p>
非法标签名称,不会被解析为 HTML
非法属性名称
非法属性值
非法空白
缺少空白
结束标签
结束标签中的非法属性
注释
foo <!-- this is a
comment - with hyphen -->
<p>foo <!-- this is a
comment - with hyphen --></p>
foo <!-- not a comment -- two hyphens -->
<p>foo <!-- not a comment -- two hyphens --></p>
处理指令
声明
CDATA 部分
实体在 HTML 属性中会被保留
反斜杠转义在 HTML 属性中无效
一个在代码块或 HTML 标签之外的换行,如果其前面有两个或更多空格,且不位于块的末尾,则被解析为 硬换行(在 HTML 中渲染为 <br /> 标签)。
作为一种更显眼的替代方案,可以在换行符之前使用反斜杠,而不是使用两个空格
可以使用超过两个的空格
下一行开头的空格会被忽略
换行符可以出现在强调、链接以及其他允许内联内容的结构中
换行符不会出现在代码行内
或者 HTML 标签内
硬换行用于分隔块内的内联内容。两种硬换行语法在段落或其他块元素的末尾均无效
一个常规换行(不在代码片段或 HTML 标签内)且前面没有两个或更多空格的换行被解析为软换行。(软换行在 HTML 中可以渲染为换行符或空格。在浏览器中结果相同。此处的示例中将使用换行符。)
行末和下一行开头的空格会被移除
符合标准的解析器可以将 HTML 中的软换行渲染为换行符或空格。
渲染器也可以提供一个选项,将软换行渲染为硬换行。
上述规则未定义的任何字符将被解析为普通文本内容。
内部空格会被逐字保留
解析分为两个阶段
第一阶段,消耗输入行并构建文档的块结构——即将其划分为段落、块引用、列表项等。文本被分配给这些块,但不进行解析。链接引用定义会被解析并构建一个链接映射表。
在第二阶段,段落和标题的原始文本内容被解析为一系列 Markdown 行内元素(字符串、代码 span、链接、强调等),并使用第一阶段构建的链接引用映射。
在处理的每个点,文档都被表示为由块组成的树。树的根是 document 块。document 可以拥有任意数量的其他块作为子节点。这些子节点依次又可以拥有其他块作为子节点。块的最后一个子节点通常被视为开放的,意味着后续的输入行可以改变其内容。(未开放的块即为已关闭的。)例如,这是一个可能的文档树,箭头标记了开放的块
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
list_item
paragraph
"Qui *quodsi iracundia*"
-> list_item
-> paragraph
"aliquando id"
每一行被处理的文本都会对这棵树产生影响。通过分析该行,根据其内容,文档可以通过以下一种或多种方式进行变更
一旦某一行以这种方式被合并到树中,它就可以被丢弃,因此输入可以以流的形式读取。
通过考虑上述树是如何由四行 Markdown 生成的,我们可以看出其工作原理
> Lorem ipsum dolor
sit amet.
> - Qui *quodsi iracundia*
> - aliquando id
起初,我们的文档模型仅仅是
-> document
第一行文本,
> Lorem ipsum dolor
导致一个 block_quote 块作为我们开放的 document 块的子节点被创建,而一个 paragraph 块作为 block_quote 的子节点。然后文本被添加到最后一个开放块,即 paragraph 中。
-> document
-> block_quote
-> paragraph
"Lorem ipsum dolor"
下一行,
sit amet.
是开放 paragraph 的“惰性延续”,因此它被添加到段落的文本中。
-> document
-> block_quote
-> paragraph
"Lorem ipsum dolor\nsit amet."
第三行,
> - Qui *quodsi iracundia*
导致 paragraph 块被关闭,一个新的 list 块作为 block_quote 的子节点被打开。一个 list_item 也作为 list 的子节点被添加,而一个 paragraph 作为 list_item 的子节点。然后文本被添加到新的 paragraph 中。
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
-> list_item
-> paragraph
"Qui *quodsi iracundia*"
第四行,
> - aliquando id
导致 list_item(及其子节点 paragraph)被关闭,一个新的 list_item 作为 list 的子节点被打开。一个 paragraph 被添加为新 list_item 的子节点,用于包含文本。这样我们就得到了最终的树。
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
list_item
paragraph
"Qui *quodsi iracundia*"
-> list_item
-> paragraph
"aliquando id"
一旦所有输入都被解析完毕,所有的开放块都会被关闭。
然后我们“遍历树”,访问每个节点,并将段落和标题的原始字符串内容解析为行内元素。此时我们已经看到了所有的链接引用定义,因此可以在遍历过程中解析引用链接。
document
block_quote
paragraph
str "Lorem ipsum dolor"
softbreak
str "sit amet."
list (type=bullet tight=true bullet_char=-)
list_item
paragraph
str "Qui "
emph
str "quodsi iracundia"
list_item
paragraph
str "aliquando id"
请注意第一个段落中的换行符如何被解析为 softbreak,以及第一个列表项中的星号如何变为 emph。
给定适当的渲染器,文档可以渲染为 HTML 或任何其他格式。