Markdown 是一种用于编写结构化文档的纯文本格式,其基础是电子邮件和 Usenet 帖子中用于表示格式的惯例。它由 John Gruber 于 2004 年开发,他用 Perl 编写了第一个 Markdown 转 HTML 的转换器,随后该格式迅速普及。在接下来的十年里,许多编程语言中开发出了数十种实现。其中一些通过引入脚注、表格和其他文档元素的惯例扩展了最初的 Markdown 语法。另一些则允许将 Markdown 文档渲染为 HTML 以外的格式。Reddit、StackOverflow 和 GitHub 等网站拥有数以百万计的 Markdown 用户。此外,Markdown 开始在 Web 之外得到应用,用于撰写书籍、文章、幻灯片、信件和讲义。
与其他许多通常更容易编写的轻量级标记语法相比,Markdown 的独特之处在于其可读性。正如 Gruber 所写:
Markdown 格式语法的最高设计目标是使其尽可能地易读。其理念是:Markdown 格式的文档应当能以纯文本形式原样发布,而看起来不像被标记语言或格式指令所污染过。(http://daringfireball.net/projects/markdown/)
通过将 AsciiDoc 的示例与同等的 Markdown 示例进行比较,可以说明这一点。这是 AsciiDoc 手册中的一个示例:
1. List item one.
+
List item one continued with a second paragraph followed by an
Indented block.
+
.................
$ ls *.sh
$ mv *.sh ~/tmp
.................
+
List item continued with a third paragraph.
2. List item two continued with an open block.
+
--
This paragraph is part of the preceding list item.
a. This list is nested and does not require explicit item
continuation.
+
This paragraph is part of the preceding list item.
b. List item b.
This paragraph belongs to item two of the outer list.
--
这是 Markdown 中的等效版本:
1. List item one.
List item one continued with a second paragraph followed by an
Indented block.
$ ls *.sh
$ mv *.sh ~/tmp
List item continued with a third paragraph.
2. List item two continued with an open block.
This paragraph is part of the preceding list item.
1. This list is nested and does not require explicit item continuation.
This paragraph is part of the preceding list item.
2. List item b.
This paragraph belongs to item two of the outer list.
可以说,AsciiDoc 版本更容易编写,你不需要担心缩进问题。但 Markdown 版本要易读得多。列表项的嵌套在源码中清晰可见,而不仅仅是在处理后的文档中。
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
列表项可以为空吗?
* a
*
* b
链接引用可以在块引用或列表项内定义吗?
> 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 编写,并针对并排测试进行了微小的扩展。脚本 tools/makespec.py 可用于将 spec.txt 转换为 HTML 或 CommonMark(然后可以将其转换为其他格式)。
在示例中,使用 → 字符来表示制表符。
任何字符序列都是有效的 CommonMark 文档。
字符是一个 Unicode 码位。虽然某些码位(例如组合变音符号)在直观意义上不对应于字符,但为了本规范的目的,所有码位都计为字符。
本规范未指定编码;它认为行是由字符而非字节组成的。一致的解析器可能仅限于某种特定的编码。
行是零个或多个除换行符 (U+000A) 或回车符 (U+000D) 之外的字符组成的序列,后跟一个行结束符或文件结束符。
行结束符是换行符 (U+000A)、未跟换行符的回车符 (U+000D),或者回车符及其后的换行符。
不包含字符的行,或仅包含空格 (U+0020) 或制表符 (U+0009) 的行,称为空行。
本规范将使用以下字符类定义:
空白字符是空格 (U+0020)、制表符 (U+0009)、换行符 (U+000A)、行制表符 (U+000B)、换页符 (U+000C) 或回车符 (U+000D)。
Unicode 空白字符是指 Unicode Zs 类中的任何码点,或者制表符 (U+0009)、回车符 (U+000D)、换行符 (U+000A) 或换页符 (U+000C)。
Unicode 空白是一个或多个Unicode 空白字符的序列。
空格即 U+0020。
ASCII 标点字符是 !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, 或 ~。
标点字符是指 ASCII 标点字符或 Unicode 类 Pc、Pd、Pe、Pf、Pi、Po 或 Ps 中的任何字符。
行中的制表符不会扩展为空格。然而,在定义块结构的空白环境中,制表符的行为就像被 4 个字符宽度的制表位替换为多个空格一样。
因此,例如,在缩进代码块中可以使用制表符代替四个空格。(注意,内部制表符会按原样传递,不会扩展为空格。)
在下面的示例中,列表项的续行段落使用制表符缩进;这与使用四个空格缩进的效果完全相同:
通常,块引用起始处的 > 后可以跟一个可选的空格,该空格不被视为内容的一部分。在以下情况下,> 后面跟着一个制表符,它被视为已展开为空格。由于其中一个空格被视为分隔符的一部分,foo 被认为是在块引用上下文中缩进了六个空格,因此我们得到一个以两个空格开头的缩进代码块。
- foo
- bar
→ - baz
<ul>
<li>foo
<ul>
<li>bar
<ul>
<li>baz</li>
</ul>
</li>
</ul>
</li>
</ul>
出于安全考虑,Unicode 字符 U+0000 必须替换为 REPLACEMENT CHARACTER (U+FFFD)。
我们可以将文档视为一系列的 区块(blocks)—— 即段落、块引用、列表、标题、分隔线和代码块等结构元素。有些区块(如块引用和列表项)包含其他区块;而另一些(如标题和段落)包含 内联(inline)内容——如文本、链接、加粗文本、图像、代码等。
块结构的指示符总是优先于行内结构的指示符。所以,例如,以下是一个包含两项的列表,而不是一个包含代码片段的项的列表:
这意味着解析可以分为两步进行:首先,识别文档的块结构;其次,对段落、标题和其他块构造内的文本行进行行内结构解析。第二步需要仅在第一步结束时才可用的链接引用定义信息。注意,第一步需要按顺序处理行,而第二步可以并行化,因为一个块元素的行内解析不会影响任何其他块的行内解析。
我们可以将块分为两种类型:容器块(可以包含其他块)和叶子块(不能包含其他块)。
本节描述了构成 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 标题下划线,它们将被解释为段落:它们不能被解释为代码围栏、ATX 标题、块引用、主题分隔符、列表项或 HTML 块。
一个 Setext 标题下划线是一个由 = 字符或 - 字符组成的序列,缩进不超过 3 个空格,并带有任意数量的尾随空格。如果一行包含单个 - 的字符且可以被解释为一个空的 列表项,则应将其解释为该列表项,而不是 Setext 标题下划线。
如果 Setext 标题下划线中使用 = 字符,则该标题为一级标题;如果使用 - 字符,则为二级标题。标题的内容是将前面的文本行解析为 CommonMark 行内内容的结果。
通常,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 标题不能为空
Setext 标题文本行不能被解释为除段落以外的块构造。所以,这些示例中的虚线行被解释为主题分割线
如果你想要一个以 > foo 为字面文本的标题,可以使用反斜杠转义
兼容性说明:大多数现有的 Markdown 实现不允许 Setext 标题的文本跨越多行。但对于如何解释以下内容,还没有达成共识:
Foo
bar
---
baz
人们可以找到四种不同的解释:
我们认为第 4 种解释最自然,并且第 4 种解释通过允许跨行标题增加了 CommonMark 的表达能力。希望使用第 1 种解释的作者可以在第一个段落后加一个空行
希望使用第 2 种解释的作者可以在主题分割线周围加上空行,
或者使用无法计为 Setext 标题下划线的主题分割线,例如
希望使用第 3 种解释的作者可以使用反斜杠转义
一个 缩进代码块由一个或多个以空行分隔的缩进代码片段组成。一个缩进代码片段是连续的非空行序列,每一行至少缩进四个空格。代码块的内容是行的字面内容,包括结尾的行结束符,减去四个空格的缩进。缩进代码块没有信息字符串。
缩进代码块不能打断段落,因此段落和其后的缩进代码块之间必须有一个空行。(但是,代码块和其后的段落之间不需要空行。)
如果对缩进的解释存在歧义,既可以解释为代码块,也可以解释为属于列表项的材料,那么列表项的解释优先
代码块的内容是字面文本,不会被解析为 Markdown
这里我们有三个由空行分隔的片段
超过四个空格的任何初始空格都将包含在内容中,即使在内部空行中也是如此
缩进代码块不能打断段落。(这允许悬挂缩进等。)
然而,任何前导空格少于四个的非空行都会立即结束代码块。所以一个段落可能会紧接在缩进代码之后
缩进代码可以紧接在其他类型的块之前和之后
# Heading
foo
Heading
------
foo
----
<h1>Heading</h1>
<pre><code>foo
</code></pre>
<h2>Heading</h2>
<pre><code>foo
</code></pre>
<hr />
第一行可以缩进超过四个空格
缩进代码块之前或之后的空行不包含在其中
尾随空格包含在代码块的内容中
一个 代码围栏是一个至少由三个连续的反引号 (`) 或波浪号 (~) 组成的序列。(波浪号和反引号不能混用。)一个 围栏代码块以缩进不超过三个空格的代码围栏开头。
带有起始代码围栏的行可以可选地包含跟随在代码围栏之后的一些文本;这些文本会去除首尾空格,被称为 信息字符串。信息字符串不能包含任何反引号字符。(该限制的原因是,否则一些行内代码会被错误地解释为围栏代码块的开头。)
代码块的内容由所有后续行组成,直到出现与代码块开头相同类型(反引号或波浪号)且至少与起始代码围栏一样长的关闭 代码围栏。如果起始代码围栏缩进了 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 块是一组被视为原生 HTML(且不会在 HTML 输出中转义)的行。
HTML 块有七种类型,可以通过它们的开始和结束条件来定义。块以符合开始条件的行(可选缩进最多三个空格)开始。它以随后符合匹配的结束条件的第一行结束,或者如果未遇到符合结束条件的行,则以文档或其它容器块的最后一行结束。如果第一行同时满足开始条件和结束条件,则该块将仅包含这一行。
开始条件:行以字符串 <script, <pre, 或 <style 开头(不区分大小写),后跟空白、字符串 > 或行尾。
结束条件:行包含结束标签 </script>, </pre>, 或 </style>(不区分大小写;无需与开始标签匹配)。
开始条件:行以字符串 <!-- 开头。
结束条件:行包含字符串 -->。
开始条件:行以字符串 <? 开头。
结束条件:行包含字符串 ?>。
开始条件:行以字符串 <! 开头,后跟一个大写 ASCII 字母。
结束条件:行包含字符 >。
开始条件:行以字符串 <![CDATA[ 开头。
结束条件:行包含字符串 ]]>。
开始条件:行以字符串 < 或 </ 开头,后跟以下字符串之一(不区分大小写):address, article, aside, base, basefont, blockquote, body, caption, center, col, colgroup, dd, details, dialog, dir, div, dl, dt, fieldset, figcaption, figure, footer, form, frame, frameset, h1, h2, h3, h4, h5, h6, head, header, hr, html, iframe, legend, li, link, main, menu, menuitem, meta, nav, noframes, ol, optgroup, option, p, param, section, source, summary, table, tbody, td, tfoot, th, thead, title, tr, track, ul,后跟空白字符、行尾、字符串 > 或字符串 />。
结束条件:行后跟一个空行。
开始条件:行以一个完整的开始标签或结束标签开头(除了 script, style, 或 pre 以外的任何标签名),后跟仅空白或行尾。
结束条件:行后跟一个空行。
除第 7 类外,所有类型的 HTML 块都可以打断段落。第 7 类块不能打断段落。(该限制旨在防止将包裹的段落中的长标签错误解释为开始 HTML 块。)
下面是一些简单示例。这里是一些基本的第 6 类 HTML 块
<table>
<tr>
<td>
hi
</td>
</tr>
</table>
okay.
<table>
<tr>
<td>
hi
</td>
</tr>
</table>
<p>okay.</p>
块也可以以结束标签开头
这里我们有两个 HTML 块,中间有一个 Markdown 段落
第一行的标签可以是部分的,只要它在有空白的地方被分割即可
开始标签不需要关闭
部分标签甚至不需要完整(垃圾进,垃圾出)
初始标签甚至不需要是有效标签,只要它以标签的样子开始即可
在第 6 类块中,初始标签不需要在单独的一行上
直到下一个空行或文档末尾的所有内容都包含在 HTML 块中。因此,在以下示例中,看起来像 Markdown 代码块的内容实际上是 HTML 块的一部分,该块将持续到空行或到达文档末尾
要以 不在 (6) 中的块级标签列表中的标签开始 HTML 块,必须将该标签放在第一行的单独一行上(并且它必须是完整的)
在第 7 类块中,标签名可以是任何东西
这些规则旨在让我们能够处理既可以作为块级标签也可以作为行内级标签的标签。<del> 标签就是一个很好的例子。我们可以用三种不同的方式用 <del> 标签包裹内容。在这种情况下,我们得到了一个原生 HTML 块,因为 <del> 标签单独占一行
在这种情况下,我们得到了一个仅包含 <del> 标签的原生 HTML 块(因为它以随后的空行结束)。所以内容会被解释为 CommonMark
最后,在这种情况下,<del> 标签被解释为 CommonMark 段落 内部 的 原生 HTML。(因为标签没有单独占一行,我们得到的是行内 HTML 而不是 HTML 块。)
设计为包含字面内容(script, style, pre)、注释、处理指令和声明的 HTML 标签的处理方式略有不同。这些块不是在第一个空行处结束,而是在包含相应结束标签的第一行结束。因此,这些块可以包含空行
pre 标签(第 1 类)
<pre language="haskell"><code>
import Text.HTML.TagSoup
main :: IO ()
main = print $ parseTags tags
</code></pre>
okay
<pre language="haskell"><code>
import Text.HTML.TagSoup
main :: IO ()
main = print $ parseTags tags
</code></pre>
<p>okay</p>
script 标签(第 1 类)
<script type="text/javascript">
// JavaScript example
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
okay
<script type="text/javascript">
// JavaScript example
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
<p>okay</p>
style 标签(第 1 类)
<style
type="text/css">
h1 {color:red;}
p {color:blue;}
</style>
okay
<style
type="text/css">
h1 {color:red;}
p {color:blue;}
</style>
<p>okay</p>
如果没有匹配的结束标签,该块将在文档(或包含的块引用或列表项)末尾结束
结束标签可以出现在与开始标签同一行上
注意,最后一行中结束标签之后的任何内容都将包含在 HTML 块中
注释(第 2 类)
处理指令(第 3 类)
声明(第 4 类)
CDATA(第 5 类)
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then {
return 1;
} else {
return 0;
}
}
]]>
okay
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then {
return 1;
} else {
return 0;
}
}
]]>
<p>okay</p>
起始标签可以缩进 1-3 个空格,但不能缩进 4 个
第 1–6 类的 HTML 块可以打断段落,且不需要之前有空行。
但是,随后的空行是必须的,除非是在文档末尾,且除上述第 1–5 类块外
第 7 类 HTML 块不能打断段落
此规则与 John Gruber 原来的 Markdown 语法规范不同,后者指出:
唯一的限制是块级 HTML 元素(例如
<div>,<table>,<pre>,<p>等)必须与周围内容用空行分隔,且该块的开始和结束标签不应使用制表符或空格缩进。
在某些方面,Gruber 的规则比这里给出的规则更严格:
大多数 Markdown 实现(包括 Gruber 自己的一些实现)并不遵循所有这些限制。
然而,在有一个方面,Gruber 的规则比这里给出的规则更宽松,因为它允许 HTML 块内出现空行。这里禁止它们有两个原因。首先,它消除了解析平衡标签的需要,这很昂贵,并且如果未找到匹配的结束标签,可能需要从文档末尾回溯。其次,它提供了一种在 HTML 标签内包含 Markdown 内容的非常简单且灵活的方法:只需使用空行将 Markdown 与 HTML 分隔开。
比较:
一些 Markdown 实现采用了如果开始标签具有 markdown=1 属性就将标签内内容解释为文本的约定。上述规则似乎是一种更简单、更优雅的实现相同表达能力的方法,解析起来也简单得多。
主要潜在的缺点是人们无法再 100% 可靠地将 HTML 块粘贴到 Markdown 文档中。但是,在大多数情况下这可以正常工作,因为 HTML 中的空行通常后跟 HTML 块标签。例如:
但是,如果内部标签缩进 且 被空格分隔,则会出现问题,因为那样它们会被解释为缩进代码块
<table>
<tr>
<td>
Hi
</td>
</tr>
</table>
<table>
<tr>
<pre><code><td>
Hi
</td>
</code></pre>
</tr>
</table>
幸运的是,空行通常是不必要的,可以删除。例外情况是在 <pre> 标签内,但如上所述,以 <pre> 开始的原生 HTML 块 可以 包含空行。
一个 链接引用定义由一个链接标签组成,缩进不超过三个空格,后跟一个冒号 (:),可选的空白(包括最多一个行结束符),一个链接目标,可选的空白(包括最多一个行结束符),以及一个可选的链接标题,如果存在,必须用空白与链接目标分隔。该行不能再出现其他非空白字符。
一个 链接引用定义不对应于文档的结构元素。相反,它定义了一个可在文档中其他地方的引用链接和引用式图像中使用的标签。链接引用定义可以出现在使用它们的链接之前或之后。
[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%20url>
'title'
[Foo bar]
<p><a href="my%20url" title="title">Foo bar</a></p>
标题可以跨越多行
[foo]: /url '
title
line1
line2
'
[foo]
<p><a href="/url" title="
title
line1
line2
">foo</a></p>
然而,它不能包含空行
[foo]: /url 'title
with blank line'
[foo]
<p>[foo]: /url 'title</p>
<p>with blank line'</p>
<p>[foo]</p>
标题可以省略
链接目标不能省略
标题和目标都可以包含反斜杠转义和字面反斜杠
[foo]: /url\bar\*baz "foo\"bar\baz"
[foo]
<p><a href="/url%5Cbar*baz" title="foo"bar\baz">foo</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 的引用块。
惰性匹配。 如果一行字符串 Ls 构成一个内容为 Bs 的引用块,那么如果从一行或多行中删除初始引用块标记,且该行中标记后的下一个非空白字符是段落延续文本,其结果仍是一个内容为 Bs 的引用块。段落延续文本是指将被解析为段落内容一部分,但不在段落开头的文本。
除此之外的其他内容均不视为引用块。
这是一个简单的例子
可以省略 > 字符后的空格
> 字符可以缩进 1-3 个空格
四个空格会产生一个代码块
懒惰性条款允许我们省略段落续行文本前的 >
引用块可以包含一些惰性匹配行和一些非惰性匹配行
惰性匹配仅适用于在添加了引用块标记后本应成为段落延续的行。例如,在第二行中不能省略 >:
> foo
> ---
否则会改变含义
同样,如果我们省略了第二行中的 >:
> - foo
> - bar
那么引用块在第一行之后结束
出于同样的原因,我们不能在缩进代码块或围栏代码块的后续行前省略 >
> 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>
注意在以下情况下,我们有一个懒惰续行
为了理解原因,请注意:在
> foo
> - bar
- bar 缩进太多无法开始列表,且不能是缩进代码块,因为缩进代码块不能中断段落,所以它是段落续行文本。
引用块可以为空
引用块可以有初始或最终空行
空行总是会分隔引用块
(大多数当前的 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>
无序列表标记是 -, + 或 * 字符。
有序列表标记是由 1–9 位阿拉伯数字 (0-9) 组成的序列,后跟 . 或 ) 字符。(长度限制的原因是 10 位数字在某些浏览器中会出现整数溢出。)
以下规则定义了列表项
基本情况。如果一系列行 Ls 构成一系列以非空白字符开头且彼此之间不超过一个空行的块 Bs,且 M 是宽度为 W 的列表标记,后面跟着 1 ≤ N ≤ 4 个空格,那么在 Ls 的第一行前加上 M 和随后的空格,并将 Ls 的后续行缩进 W + N 个空格的结果,就是一个以 Bs 为其内容的列表项。列表项的类型(无序或有序)由其列表标记的类型决定。如果列表项是有序的,则还会根据有序列表标记分配一个起始编号。
例外情况:当列表中的第一个列表项中断了段落时——也就是说,当它起始于原本会被算作段落延续文本的行时——那么 (a) 行 Ls 不能以空行开头,并且 (b) 如果该列表项是有序列表,起始数字必须为 1。
例如,设 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>
注意,列表标记和随后的任何内容之间至少需要一个空格,因此以下不是列表项
列表项可以包含由超过一个空行分隔的块。
列表项可以包含任何类型的块
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>
包含缩进代码块的列表项将逐字保留代码块内的空行。
注意,有序列表的起始编号必须为九位数字或更少
起始编号可以以 0 开头
起始编号不能为负数
缩进代码块需要缩进到列表项中包含文本区域边缘之外的四个空格。在下一种情况下,即为 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。所以,在上述情况下
以下是一些以空行开头但不为空的列表项
-
foo
-
```
bar
```
-
baz
<ul>
<li>foo</li>
<li>
<pre><code>bar
</code></pre>
</li>
<li>
<pre><code>baz
</code></pre>
</li>
</ul>
当列表项以空行开始时,列表标记后的空格数量不会改变所需的缩进量
列表项最多可以以一个空行开头。在以下示例中,foo 不是列表项的一部分
这是一个空的无序列表项
列表标记之后是否有空格无关紧要
这是一个空的有序列表项
列表可以以空的列表项开始或结束
然而,空列表项不能中断段落
缩进一个空格
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
- boo
<ul>
<li>foo
<ul>
<li>bar
<ul>
<li>baz
<ul>
<li>boo</li>
</ul>
</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 行为格式化的列表,前提是它们的布局方式对于人类阅读来说是自然的。
这里的策略是让列表标记的宽度和缩进决定块归入列表项所需的缩进,而不是使用固定和任意的数字。作者可以将列表项的主体视为一个单元,该单元向右缩进足够多以容纳列表标记(以及列表标记上的任何缩进)。(惰性规则 #5 然后允许在需要时取消对延续行的缩进。)
我们认为此规则优于任何要求从边距固定缩进级别的规则。四空格规则很清晰但不自然。以下内容:
- 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) 它们是具有相同分隔符 (. 或 )) 的有序列表编号,则两个列表标记属于同一类型。
如果列表由有序列表标记开头,则列表为有序列表;如果由无序列表标记开头,则为无序列表。
有序列表的起始编号由其初始列表项的列表编号决定。后续列表项的编号将被忽略。
如果列表的任何组成列表项由空行分隔,或者任何组成列表项直接包含两个中间有空行的块级元素,则列表为松散的。否则,列表为紧凑的。(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.
奇怪的是,Markdown.pl 确实允许块引用中断段落,尽管同样的考虑可能适用。
在 CommonMark 中,我们允许列表中断段落,原因有二。首先,人们在没有空行的情况下开始列表是很自然且不罕见的
I need to buy
- new shoes
- a coat
- a plane ticket
其次,我们被一种
统一原则所吸引:如果一段文本具有某种含义,那么当放入容器块(如列表项或引用块)中时,它将继续保持相同的含义。
(事实上,列表项和引用块的规范预设了这一原则。)这一原则意味着如果
* I need to buy
- new shoes
- a coat
- a plane ticket
是一个包含段落后跟嵌套子列表的列表项(正如所有 Markdown 实现所同意的那样,尽管由于列表是“紧凑的”,段落可能在渲染时没有 <p> 标签),那么
I need to buy
- new shoes
- a coat
- a plane ticket
它本身应该是一个段落后跟一个嵌套子列表。
由于允许列表项内的列表中断段落是公认的 Markdown 实践,统一原则要求我们也允许在列表项外这样做。(reStructuredText 采取了不同的方法,即使在其它列表项内也要求在列表前有空行。)
为了解决段落中出现不需要的硬换行数字列表的问题,我们只允许以 1 开头的列表中断段落。因此,
The number of windows in my house is
14. The number of doors is 6.
<p>The number of windows in my house is
14. The number of doors is 6.</p>
在诸如此类的情况下,我们可能仍然会得到非预期的结果
The number of windows in my house is
1. The number of doors is 6.
<p>The number of windows in my house is</p>
<ol>
<li>The number of doors is 6.</li>
</ol>
但该规则应该能防止大多数伪列表捕获。
项之间可以有任意数量的空行
- foo
- bar
- baz
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
</li>
<li>
<p>baz</p>
</li>
</ul>
- foo
- bar
- baz
bim
<ul>
<li>foo
<ul>
<li>bar
<ul>
<li>
<p>baz</p>
<p>bim</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
要分隔相同类型的连续列表,或将列表与本应被解析为最后一个列表项的子段落的缩进代码块分隔开,您可以插入一个空的 HTML 注释
- 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
- h
- i
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>e</li>
<li>f</li>
<li>g</li>
<li>h</li>
<li>i</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 heading
\[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 heading
[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 实体引用和数字字符引用,除了在代码块和代码行中出现的之外,都被识别并视为等同于对应的 Unicode 字符。符合 CommonMark 的解析器不需要存储关于特定字符是在源中使用 Unicode 字符还是实体引用表示的信息。
实体引用由 & + 任何有效的 HTML5 实体名称 + ; 组成。文档 https://html.whatwg.cn/multipage/entities.json 被用作有效实体引用及其对应码点的权威来源。
& © Æ Ď
¾ ℋ ⅆ
∲ ≧̸
<p> & © Æ Ď
¾ ℋ ⅆ
∲ ≧̸</p>
十进制数字字符由 &# + 1-8 个阿拉伯数字字符串 + ; 组成。数字字符引用被解析为相应的 Unicode 字符。无效的 Unicode 码位将被替换字符 (U+FFFD) 替换。出于安全原因,码位 U+0000 也将被 U+FFFD 替换。
十六进制数字字符由 &# + X 或 x + 1-8 个十六进制数字字符串 + ; 组成。它们也被解析为相应的 Unicode 字符(这次使用十六进制数字而非十进制指定)。
这里是一些非实体引用
  &x; &#; &#x;
&ThisIsNotDefined; &hi?;
<p>&nbsp &x; &#; &#x;
&ThisIsNotDefined; &hi?;</p>
尽管 HTML5 确实接受某些没有末尾分号的实体引用(例如 ©),但这里不予识别,因为这会使语法过于歧义
不在 HTML5 命名实体列表中的字符串也不会被识别为实体引用
实体和数字字符引用在除代码片段或代码块之外的任何上下文中都是可识别的,包括 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>
在代码片段和代码块中,实体和数字字符引用被视为字面文本。
反引号字符串是指一串包含一个或多个反引号字符(`)的字符串,且其前后均无反引号。
代码跨度以反引号字符串开始,并以等长的反引号字符串结束。代码跨度的内容是两个反引号字符串之间的字符,其中前导和尾随空格以及 行尾符 会被移除,且 空白字符 会被折叠为单个空格。
这是一个简单的代码跨度
这里使用了两个反引号,因为代码中包含一个反引号。此示例还演示了如何去除前导和尾随空格。
此示例展示了去除前导和尾随空格的原因。
行尾符被视为类似空格的处理。
内部空格和 行尾符 会被折叠为单个空格,就像浏览器处理它们一样。
然而,并非所有的 Unicode 空白字符(例如不换行空格)都会被折叠。
问:既然浏览器无论如何都会折叠空格,为什么不直接保留它们呢?答:因为我们可能需要面向非 HTML 格式,我们不应依赖特定于 HTML 的渲染假设。
(现有实现对内部空格和 行尾符 的处理方式各不相同。一些实现,包括 Markdown.pl 和 showdown,会将内部 行尾符 转换为 <br /> 标签。但这给喜欢硬换行段落的用户造成了困难,因为代码跨度中间的换行会导致输出中出现非预期的换行。其他实现则直接保留内部空格,如果目标仅是 HTML,这是没问题的。)
请注意,反斜杠转义在代码跨度中不起作用。所有反斜杠均被视为字面字符。
反斜杠转义永远是不需要的,因为人们总是可以选择包含 n 个反引号字符的字符串作为定界符,而代码本身不包含任何恰好为 n 个反引号的字符串。
代码跨度的反引号优先级高于除了 HTML 标签和自动链接之外的任何行内结构。因此,例如,这不会被解析为强调文本,因为第二个 * 是代码跨度的一部分。
且这也不会被解析为链接。
代码跨度、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
下面给出的规则涵盖了所有这些模式,同时允许不回溯的高效解析策略。
首先,进行一些定义。定界符序列(delimiter run)是指一个或多个 * 字符组成的序列(且前后均无 *),或一个或多个 _ 字符组成的序列(且前后均无 _)。
左侧侧翼定界符序列是指一个 定界符序列,满足 (a) 其后不是 Unicode 空白字符,且 (b) 其后不是 标点符号,或者其前是 Unicode 空白字符 或 标点符号。在本定义中,行首和行尾也被视为 Unicode 空白字符。
右侧侧翼定界符序列是指一个 定界符序列,满足 (a) 其前不是 Unicode 空白字符,且 (b) 其前不是 标点符号,或者其后是 Unicode 空白字符 或 标点符号。在本定义中,行首和行尾也被视为 Unicode 空白字符。
以下是一些定界符序列的示例。
左侧侧翼但非右侧侧翼
***abc
_abc
**"abc"
_"abc"
右侧侧翼但非左侧侧翼
abc***
abc_
"abc"**
"abc"_
既是左侧又是右侧侧翼
abc***def
"abc"_"def"
既非左侧也非右侧侧翼
abc *** def
a _ b
(根据前后的字符来区分左侧侧翼和右侧侧翼定界符序列的想法来源于 Roopesh Chander 的 vfmd。vfmd 使用“强调指示符字符串”而不是“定界符序列”这一术语,其区分左右侧翼序列的规则比这里给出的要复杂一些。)
以下规则定义了强调和强强调。
单个 _ 字符 可以开启强调,当且仅当它是 左侧侧翼定界符序列 的一部分,且满足 (a) 不是 右侧侧翼定界符序列 的一部分,或者 (b) 是前接标点的 右侧侧翼定界符序列 的一部分。
单个 _ 字符 可以关闭强调,当且仅当它是 右侧侧翼定界符序列 的一部分,且满足 (a) 不是 左侧侧翼定界符序列 的一部分,或者 (b) 是后接标点的 左侧侧翼定界符序列 的一部分。
双 __ 可以开启强强调,当且仅当它是 左侧侧翼定界符序列 的一部分,且满足 (a) 不是 右侧侧翼定界符序列 的一部分,或者 (b) 是前接标点的 右侧侧翼定界符序列 的一部分。
双 __ 可以关闭强强调,当且仅当它是 右侧侧翼定界符序列 的一部分,且满足 (a) 不是 左侧侧翼定界符序列 的一部分,或者 (b) 是后接标点的 左侧侧翼定界符序列 的一部分。
强调以可以开启强调的定界符开始,并以可以关闭强调的定界符结束,并且使用与开启定界符相同的字符(_ 或 *)。开启和关闭定界符必须属于不同的定界符序列。如果其中一个定界符既能开启又能关闭强调,那么包含开启和关闭定界符的定界符序列长度之和不能是 3 的倍数。
强强调以可以开启强强调的定界符开始,并以可以关闭强强调的定界符结束,并且使用与开启定界符相同的字符(_ 或 *)。开启和关闭定界符必须属于不同的定界符序列。如果其中一个定界符既能开启又能关闭强强调,那么包含开启和关闭定界符的定界符序列长度之和不能是 3 的倍数。
除非经过反斜杠转义,否则字面意义上的 * 字符不能出现在 * 定界的强调或 ** 定界的强强调的开头或结尾。
除非经过反斜杠转义,否则字面意义上的 _ 字符不能出现在 _ 定界的强调或 __ 定界的强强调的开头或结尾。
在上述 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 baz** 被解析为 **foo <strong>bar baz</strong> 而不是 <strong>foo **bar baz</strong>。
行内代码跨度、链接、图像和 HTML 标签的结合比强调更紧密。因此,当在包含这些元素与不包含这些元素的解释之间进行选择时,前者总是胜出。因此,例如,*[foo*](bar) 被解析为 *<a href="bar">foo*</a> 而不是 <em>[foo</em>](bar)。
这些规则可以通过一系列示例来说明。
规则 1
这不是强调,因为开启的 * 后跟有空格,因此不是 左侧侧翼定界符序列 的一部分。
这不是强调,因为开启的 * 前是字母数字,后是标点符号,因此不是 左侧侧翼定界符序列 的一部分。
Unicode 不换行空格也计为空白字符。
允许使用 * 进行词内强调。
规则 2
这不是强调,因为开启的 _ 后跟有空格。
这不是强调,因为开启的 _ 前是字母数字,后是标点符号。
不允许在单词内部使用 _ 进行强调。
此处 _ 不产生强调,因为第一个定界符序列是右侧侧翼,而第二个是左侧侧翼。
这是强调,即使开启定界符既是左侧侧翼也是右侧侧翼,因为它前接标点。
规则 3
这不是强调,因为关闭定界符与开启定界符不匹配。
这不是强调,因为关闭的 * 前面有空格。
换行符也被计为空白字符。
这不是强调,因为第二个 * 前面是标点符号,后面是字母数字(因此它不是 右侧侧翼定界符序列 的一部分)。
此限制的意义可以通过以下示例更清楚地理解。
允许使用 * 进行词内强调。
规则 4
这不是强调,因为关闭的 _ 前面有空格。
这不是强调,因为第二个 _ 前面是标点符号,后面是字母数字。
这是强调嵌套在强调中。
对于 _ 不允许进行词内强调。
这是强调,即使关闭定界符既是左侧侧翼也是右侧侧翼,因为它后接标点。
规则 5
这不是强强调,因为开启定界符后跟有空格。
这不是强强调,因为开启的 ** 前是字母数字,后是标点符号,因此不是 左侧侧翼定界符序列 的一部分。
允许使用 ** 进行词内强强调。
规则 6
这不是强强调,因为开启定界符后跟有空格。
换行符计为空白字符。
这不是强强调,因为开启的 __ 前是字母数字,后是标点符号。
禁止使用 __ 进行词内强强调。
这是强强调,即使开启定界符既是左侧侧翼也是右侧侧翼,因为它前接标点。
规则 7
这不是强强调,因为关闭定界符前面有空格。
(根据规则 11,它也不能被解释为强调的 *foo bar *。)
这不是强强调,因为第二个 ** 前是标点符号,后是字母数字。
此限制的意义可以通过这些示例更清楚地理解。
**Gomphocarpus (*Gomphocarpus physocarpus*, syn.
*Asclepias physocarpa*)**
<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
<em>Asclepias physocarpa</em>)</strong></p>
词内强调
规则 8
这不是强强调,因为关闭定界符前面有空格。
这不是强强调,因为第二个 __ 前是标点符号,后是字母数字。
此限制的意义可以通过以下示例更清楚地理解。
禁止使用 __ 进行词内强强调。
这是强强调,即使关闭定界符既是左侧侧翼也是右侧侧翼,因为它后接标点。
规则 9
任何非空的行内元素序列都可以作为强调跨度的内容。
特别是,强调和强强调可以嵌套在强调内。
注意,在上述情况中,这种解释被以下条件所排除:如果一个既能作为开头又能作为结尾的分隔符(例如 foo 后面的 *)所在的分隔符序列总长度是 3 的倍数,那么除非其两个部分的长度都是 3 的倍数,否则它不能形成强调。
<p><em>foo</em><em>bar<em></em>baz</em></p>
被以下条件排除:如果包含开始和结束分隔符的分隔符序列的长度之和是 3 的倍数,则既可以作为开始也可以作为结束的分隔符(例如 foo 后面的 *)不能构成强调。
相同的条件确保了即使在省略内部空格时,以下情况也都是嵌套在强调内的强强调:
支持无限级的嵌套。
不存在空强调或空强强调。
规则 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) 它们作为匹配的一对括号出现,包含一个开括号 [、零个或多个行内元素,以及一个闭括号 ]。
反引号 代码片段、自动链接 和原始 HTML 标签 的绑定比链接文本中的括号更紧密。因此,例如,[foo`]` 不能作为链接文本,因为第二个 ] 是代码片段的一部分。
链接文本中的方括号结合比 强调和强强调 的标记更紧密。因此,例如,*[foo*](url) 是一个链接。
链接目标由以下两者之一组成:
在开始的 < 和关闭的 > 之间,包含零个或多个不包含空格、换行符或未转义的 < 或 > 字符的字符序列,或者
非空的字符序列,不包括 ASCII 空格或控制字符,且仅在以下情况下包含圆括号:(a) 它们经过反斜杠转义,或 (b) 它们是平衡的一对未转义圆括号的一部分,该对括号本身不在另一对平衡的未转义圆括号内。
链接标题由以下两者之一组成:
直双引号字符(")之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 " 字符,或者
直单引号字符(')之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 ' 字符,或者
匹配的圆括号((...))之间零个或多个字符的序列,仅在经过反斜杠转义时才包含 ) 字符。
行内链接由 链接文本 紧随左圆括号 (、可选的 空白字符、可选的 链接目标、通过 空白字符 与链接目标分隔的可选 链接标题、可选的 空白字符 以及右圆括号 ) 组成。链接文本包含 链接文本 中包含的行内元素(不包括包围的方括号)。链接 URI 包含链接目标,若存在则不包括包围的 <...>,并按上述规定应用反斜杠转义。链接标题包含链接标题,不包括其包围的定界符,并按上述规定应用反斜杠转义。
这是一个简单的行内链接:
标题可以省略
标题和目标均可省略。
即使包含在尖括号中,目标也不能包含空格或换行符。
链接目标内的圆括号可以进行转义
在不进行转义的情况下,允许使用一层平衡的圆括号。
但是,如果圆括号内有圆括号,则需要进行转义或使用 <...> 形式。
圆括号和其他符号也可以按照 Markdown 中的惯例进行转义。
链接可以包含片段标识符和查询字符串。
[link](#fragment)
[link](http://example.com#fragment)
[link](http://example.com?foo=3#frag)
<p><a href="#fragment">link</a></p>
<p><a href="http://example.com#fragment">link</a></p>
<p><a href="http://example.com?foo=3#frag">link</a></p>
请注意,不可转义字符前的反斜杠仅被视为反斜杠。
URL 转义在目标内部应保持原样,因为所有 URL 转义字符也是有效的 URL 字符。目标中的实体和数值字符引用将按惯例解析为相应的 Unicode 码位。当写入 HTML 时,这些字符可以选配地进行 URL 转义,但本规范不对在 HTML 或其他格式中渲染 URL 的特定策略进行强制。渲染器可能对如何在输出中转义或规范化 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>
标题中可以使用反斜杠转义以及实体和数值字符引用。
标题必须使用空白与链接分隔。其它 Unicode 空白(如不换行空格)不起作用。
除非经过转义,否则不允许嵌套平衡的引号。
[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 大小写折叠,并将连续的内部 空白字符 折叠为单个空格。如果存在多个匹配的引用链接定义,则使用文档中排在前面的那个。(在这种情况下发出警告是可取的。)
第一个链接标签的内容被解析为行内元素,用作链接的文本。链接的 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>
在确定匹配时,连续的内部 空白字符 被视为一个空格。
这偏离了 John Gruber 的原版 Markdown 语法描述,该描述显式允许链接文本和链接标签之间存在空格。这使得引用链接与 行内链接 保持一致,根据原版 Markdown 和本规范,行内链接在链接文本后不允许出现空格。更重要的是,它防止了对连续 快捷引用链接 的意外捕获。如果允许链接文本和链接标签之间出现空格,那么在下面我们将得到一个单一的引用链接,而不是两个预期的快捷引用链接。
[foo]
[bar]
[foo]: /url1
[bar]: /url2
(请注意,快捷引用链接 是由 Gruber 本人在 Markdown.pl 的测试版中引入的,但从未包含在官方语法描述中。如果没有快捷引用链接,允许链接文本和链接标签之间出现空格是无害的;但一旦引入快捷引用,允许这样做就太危险了,因为它经常导致意外结果。)
当存在多个匹配的 链接引用定义 时,使用第一个。
请注意,匹配是在规范化字符串上执行的,而不是在解析的行内内容上。因此,以下内容不匹配,即使标签定义了等效的行内内容。
链接标签 不能包含括号,除非它们经过反斜杠转义。
请注意,在此示例中,] 没有经过反斜杠转义。
折叠引用链接由一个 链接标签 组成,该标签 匹配 文档其他位置的 链接引用定义,后跟字符串 []。第一个链接标签的内容被解析为行内元素,用作链接的文本。链接的 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)。
就本规范而言,方案是任何以 ASCII 字母开头,后跟 ASCII 字母、数字或加号 (”+”)、句点 (”.”)、连字符 (”-”) 的任意组合的 2–32 个字符的序列。
以下是一些有效的自动链接:
<http://foo.bar.baz/test?q=hello&id=22&boolean>
<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?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>
大写字母也可以。
请注意,许多符合本规范定义的 绝对 URI 的字符串并不是有效的 URI,这是因为它们的方案未注册,或者由于其语法存在其他问题
<made-up-scheme://foo,bar>
<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
自动链接中不允许出现空格。
反斜杠转义在自动链接内不起作用。
<http://example.com/\[\>
<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</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+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 注释由 <!-- + 文本 + --> 组成,其中 文本 不能以 > 或 -> 开头,不能以 - 结尾,且不能包含 --。(参见 HTML5 规范。)
一个 处理指令 (processing instruction) 由字符串 <?、一段不包含字符串 ?> 的字符序列,以及字符串 ?> 组成。
一个 声明 (declaration) 由字符串 <!、一个由一个或多个大写 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>
非注释
foo <!--> foo -->
foo <!-- foo--->
<p>foo <!--> foo --></p>
<p>foo <!-- foo---></p>
处理指令
声明
CDATA 部分
HTML 属性中的实体引用和数字字符引用会被保留
反斜杠转义在 HTML 属性中无效
一个在代码块或 HTML 标签之外的换行,如果其前面有两个或更多空格,且不位于块的末尾,则被解析为 硬换行(在 HTML 中渲染为 <br /> 标签)。
作为一种更直观的替代方案,可以在 行尾 之前使用反斜杠来代替两个空格
可以使用超过两个的空格
下一行开头的空格会被忽略
换行符可以出现在强调、链接以及其他允许内联内容的结构中
换行符不会出现在代码行内
或者 HTML 标签内
硬换行用于分隔块内的内联内容。两种硬换行语法在段落或其他块元素的末尾均无效
一个常规的换行符(不在代码行或 HTML 标签中),且未被两个或更多空格或反斜杠前置,被解析为软换行。(软换行在 HTML 中可以渲染为行尾或空格。结果在浏览器中是一样的。在此处的示例中,将使用行尾。)
行末和下一行开头的空格会被移除
符合标准的解析器可以将 HTML 中的软换行渲染为换行符或空格。
渲染器也可以提供一个选项,将软换行渲染为硬换行。
上述规则未定义的任何字符将被解析为普通文本内容。
内部空格会被逐字保留
在本附录中,我们描述了 CommonMark 参考实现中所使用的解析策略的一些特性。
解析分为两个阶段
第一阶段,消耗输入行并构建文档的块结构——即将其划分为段落、块引用、列表项等。文本被分配给这些块,但不进行解析。链接引用定义会被解析并构建一个链接映射表。
第二阶段,段落和标题的原始文本内容被解析为 Markdown 内联元素序列(字符串、代码行、链接、强调等),并使用第一阶段构建的链接引用映射表。
在处理的每个点,文档都被表示为由块组成的树。树的根是 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"
每一行被处理的文本都会对这棵树产生影响。通过分析该行,根据其内容,文档可以通过以下一种或多种方式进行变更
一旦某一行以这种方式被合并到树中,它就可以被丢弃,因此输入可以以流的形式读取。
对于每一行,我们遵循以下程序
首先,我们遍历开放的块,从根文档开始,沿最后一个子节点向下,直到最后一个开放块。每个块都施加了一个条件,如果要保持块开放,该行必须满足该条件。例如,块引用要求有 > 字符。段落要求非空行。在这个阶段,我们可能匹配所有或部分开放的块。但我们还不能关闭不匹配的块,因为我们可能遇到了惰性延续行。
接下来,在消耗现有块的延续标记后,我们寻找新的块开始(例如,用于块引用的 >)。如果我们遇到一个新的块开始,我们将在创建新块作为最后一个匹配块的子块之前,关闭步骤 1 中未匹配的任何块。
最后,我们查看该行的剩余部分(在消耗了块标记如 >、列表标记和缩进之后)。这些文本可以被合并到最后一个开放块中(段落、代码块、标题或原始 HTML)。
当看到段落中属于 Setext 标题下划线 的一行时,就会形成 Setext 标题。
引用链接定义是在段落关闭时被检测到的;累积的文本行被解析,以查看它们是否以一个或多个引用链接定义开头。任何剩余部分都成为普通段落。
通过考虑上述树是如何由四行 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 的。
到目前为止,内联解析中最棘手的部分是处理强调、加粗强调、链接和图像。这是使用以下算法完成的。
当我们解析内联内容并遇到以下内容时:
* 或 _ 字符,或者[ 或 ![我们插入一个包含这些符号作为其字面内容的文本节点,并添加一个指向此文本节点的指针到 分隔符栈。
分隔符栈是一个双向链表。每个元素都包含一个指向文本节点的指针,以及关于以下内容的信息
[, ![, *, _)当我们遇到 ] 字符时,我们调用 *查找链接或图像* 程序(见下文)。
当我们到达输入末尾时,我们调用 *处理强调* 程序(见下文),并将 stack_bottom 设为 NULL。
从分隔符栈的顶部开始,我们向后查找打开的 [ 或 ![ 分隔符。
如果没有找到,我们返回一个字面文本节点 ]。
如果找到了,但它不是 *激活的*,我们就从栈中移除该非激活分隔符,并返回一个字面文本节点 ]。
如果我们找到一个且它是激活的,那么我们向前解析,看看是否有一个内联链接/图像、引用链接/图像、紧凑引用链接/图像或快捷引用链接/图像。
如果没有,那么我们从分隔符栈中移除该开始分隔符,并返回一个字面文本节点 ]。
如果有,那么
我们返回一个链接或图像节点,其子节点是开始分隔符所指向的文本节点之后的内联内容。
我们对这些内联内容运行 *处理强调*,并将 [ 开始符作为 stack_bottom。
我们移除该开始分隔符。
如果我们有一个链接(而非图像),我们还会将开始分隔符之前的所有 [ 分隔符设置为 *非激活*。(这将防止在链接中嵌套链接。)
参数 stack_bottom 为我们在 分隔符栈 中下降的深度设置了一个下限。如果它是 NULL,我们可以一直下降到栈底。否则,我们在访问 stack_bottom 之前停止。
令 current_position 指向 分隔符栈 中 stack_bottom 之上的元素(如果 stack_bottom 为 NULL,则指向第一个元素)。
我们为每种分隔符类型 (*, _) 跟踪 openers_bottom。将其初始化为 stack_bottom。
然后我们重复以下操作,直到耗尽所有潜在的结束符:
在分隔符栈中向前移动 current_position(如果需要),直到找到第一个分隔符为 * 或 _ 的潜在结束符。(这将是距离输入起始处最近的潜在结束符——即解析顺序中的第一个。)
现在,在栈中向后查找(保持在 stack_bottom 和该分隔符类型的 openers_bottom 之上),找到第一个匹配的潜在开始符(“匹配”意味着相同的分隔符)。
如果找到了一个
判断我们拥有的是强调还是加粗强调:如果结束符和开始符的跨度长度都 >= 2,则为加粗,否则为普通。
相应地插入一个 emph(强调)或 strong emph(加粗强调)节点,位于与开始符对应的文本节点之后。
从分隔符栈中移除开始符和结束符之间的任何分隔符。
从开始和结束的文本节点中移除 1 个(对于普通强调)或 2 个(对于加粗强调)分隔符。如果结果为空,移除它们并从分隔符栈中移除相应的元素。如果结束节点被移除,重置 current_position 为栈中的下一个元素。
如果没找到
将 openers_bottom 设置为 current_position 之前的元素。(我们知道在该点及之前,不存在此类结束符的开始符,因此这为以后的搜索设置了下限。)
如果 current_position 处的结束符不是潜在的开始符,则将其从分隔符栈中移除(因为我们知道它也不可能是结束符)。
将 current_position 前进到栈中的下一个元素。
完成之后,我们从分隔符栈中移除 stack_bottom 之上的所有分隔符。