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
列表项可以为空吗?
* 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 码位。本规范未指定编码;它将行视为由字符而非字节组成。符合规范的解析器可能受限于特定编码。
一行(line)是由零个或多个 字符 组成,并以 行尾 或文件结尾终止的序列。
行结束符 是换行符 (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 个字符宽度的空格。
出于安全考虑,Unicode 字符 U+0000 必须替换为替换字符 (U+FFFD)。
我们可以将文档视为区块序列——结构化元素,如段落、块引用、列表、标题、分割线和代码块。有些区块(如块引用和列表项)包含其他区块;另一些(如标题和段落)包含内联内容——文本、链接、强调文本、图像、代码等。
块结构的指示符总是优先于行内结构的指示符。所以,例如,以下是一个包含两项的列表,而不是一个包含代码片段的项的列表:
这意味着解析可以分为两步进行:第一,辨别文档的块结构;第二,解析段落、标题和其他块结构中的文本行以确定行内结构。第二步需要仅在第一步结束时才能获得的关于链接引用定义的信息。请注意,第一步需要按顺序处理行,而第二步可以并行化,因为一个块元素的行内解析不会影响其他任何块元素的行内解析。
我们可以将块分为两类:可以包含其他块的容器块,以及不可包含其他块的叶子块。
本节描述了构成 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 标题下划线就会被解释为段落一部分的行:它不能被解释为代码围栏、ATX 标题、块引用、分割线、列表项或HTML 区块。
一个 Setext 标题下划线 是一个 = 字符序列或 - 字符序列,缩进不超过 3 个空格,且末尾可有任意数量的空格。如果一行包含单个 - 且可以被解释为 列表项,则应将其解释为该列表项,而非 Setext 标题下划线。
如果 Setext 标题下划线 使用 = 字符,则该标题为 1 级标题;如果使用 - 字符,则为 2 级标题。标题的内容是将第一行解析为 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 />
第一行可以缩进超过四个空格
缩进代码块之前或之后的空行不包含在其中
尾随空格包含在代码块的内容中
一个 代码围栏是一个至少由三个连续的反引号 (`) 或波浪号 (~) 组成的序列。(波浪号和反引号不能混用。)一个 围栏代码块以缩进不超过三个空格的代码围栏开头。
带有起始代码围栏的行可以可选地包含跟随在代码围栏之后的一些文本;这些文本会去除首尾空格,被称为 信息字符串。信息字符串不能包含任何反引号字符。(该限制的原因是,否则一些行内代码会被错误地解释为围栏代码块的开头。)
代码块的内容由所有后续行组成,直到出现与代码块开头相同类型(反引号或波浪号)且至少与起始代码围栏一样长的关闭 代码围栏。如果起始代码围栏缩进了 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, head, header, hr, html, legend, li, link, main, menu, menuitem, meta, nav, noframes, ol, optgroup, option, p, param, pre, section, source, title, summary, table, tbody, td, tfoot, th, thead, title, tr, track, ul,并紧跟空白字符、行尾、字符串 > 或字符串 />。
结束条件:行后跟一个空行。
除第 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>
<pre language="haskell"><code>
import Text.HTML.TagSoup
main :: IO ()
main = print $ parseTags tags
</code></pre>
script 标签(第 1 类)
<script type="text/javascript">
// JavaScript example
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
<script type="text/javascript">
// JavaScript example
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
style 标签(第 1 类)
<style
type="text/css">
h1 {color:red;}
p {color:blue;}
</style>
<style
type="text/css">
h1 {color:red;}
p {color:blue;}
</style>
如果没有匹配的结束标签,该区块将在文档末尾(或包含它的[块引用]或[列表项])结束
结束标签可以出现在与开始标签同一行上
注意,最后一行中结束标签之后的任何内容都将包含在 HTML 块中
注释(第 2 类)
处理指令(第 3 类)
声明(第 4 类)
CDATA(第 5 类)
<![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 个
第 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 url>
'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 的引用块。
懒惰规则 (Laziness):如果一串行 Ls 构成了一个包含内容 Bs 的块引用,那么对于其中一个或多个行,如果其块引用标记后的下一个非空白字符是段落延续文本,则删除这些行开头的块引用标记,得到的结果仍然是一个以 Bs 为内容的块引用。段落延续文本是指将被解析为段落内容的一部分,但并未出现在段落开头的文本。
除此之外的其他内容均不视为引用块。
这是一个简单的例子
可以省略 > 字符后的空格
> 字符可以缩进 1-3 个空格
四个空格会产生一个代码块
惰性匹配条款允许我们在段落延续行之前省略 >
引用块可以包含一些惰性匹配行和一些非惰性匹配行
懒惰规则仅适用于那些如果前面加上 > 就会成为段落延续的行。例如,在第二行中不能省略 >
> foo
> ---
否则会改变含义
同样,如果我们省略了第二行中的 >:
> - foo
> - bar
那么引用块在第一行之后结束
> - 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>
无序列表标记是 -, + 或 * 字符。
有序列表标记是由 1–9 位阿拉伯数字 (0-9) 组成的序列,后跟 . 或 ) 字符。(长度限制的原因是 10 位数字在某些浏览器中会出现整数溢出。)
以下规则定义了列表项
例如,设 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
```
- baz
+ ```
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>
<li>
<p>baz</p>
<ul>
<li>
<pre><code>foo
bar
</code></pre>
</li>
</ul>
</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>
注意,有序列表的起始编号必须为九位数字或更少
起始编号可以以 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>
这是一个空的无序列表项
列表标记之后是否有空格无关紧要
这是一个空的有序列表项
列表可以以空的列表项开始或结束
缩进一个空格
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 行为格式化的列表,前提是它们的布局方式对于人类阅读来说是自然的。
这里的策略是让列表标记的宽度和缩进决定块归入列表项所需的缩进,而不是使用固定和任意的数字。作者可以将列表项的主体视为一个单元,该单元向右缩进足够多以容纳列表标记(以及列表标记上的任何缩进)。(惰性规则 #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.
<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
其次,我们被一种
统一原则所吸引:如果一段文本具有某种含义,那么当放入容器块(如列表项或引用块)中时,它将继续保持相同的含义。
(事实上,列表项和引用块的规范预设了这一原则。)这一原则意味着如果
* 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
- 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 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 实体(代码块和代码 span 除外)在存储到 AST 之前都会被识别并转换为 Unicode 字符。这意味着渲染为非 HTML 格式时无需感知 HTML 实体。HTML 渲染器可以选择将 Unicode 字符转义为实体或保持原样。(然而,", &, <, 和 > 必须始终渲染为实体。)
命名实体 由 & 组成
;。以下文档 被用作有效实体名称及其对应码位的权威来源。 & © Æ Ď
¾ ℋ ⅆ
∲ ≧̸
<p> & © Æ Ď
¾ ℋ ⅆ
∲ ≧̸</p>
十进制实体 由 &# + 一串 1-8 位阿拉伯数字 + ; 组成。同样,这些实体需要被识别并转换为相应的 Unicode 码位。无效的 Unicode 码位将被“未知码位”字符 (U+FFFD) 替换。出于安全考虑,码位 U+0000 也将被 U+FFFD 替换。
十六进制实体 由 &# + X 或 x + 1-8 位十六进制数字串
;。它们也将被解析并转换为 AST 中相应的 Unicode 码位。这里是一些非实体引用
  &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
<p>&nbsp &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;</p>
尽管 HTML5 确实接受某些没有末尾分号的实体(如 ©),但此处它们不被识别为实体,因为这会使语法变得过于模棱两可
不在 HTML5 命名实体列表中的字符串也不会被识别为实体
实体在代码 span 或代码块之外的任何上下文中都被识别,包括原始 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 标签和自动链接具有相同的优先级。因此,这是代码。
但这却是一个 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) 是一个后面有标点的[左侧侧翼定界符序列]的一部分。
强调以 可以开启强调 的定界符开始,并以 可以关闭强调 的定界符结束,且使用与开启定界符相同的字符(_ 或 *)。开启定界符和关闭定界符之间必须存在非空的行内序列;这些形成了强调行内的内容。
强强调以 可以开启强强调 的定界符开始,并以 可以关闭强强调 的定界符结束,且使用与开启定界符相同的字符(_ 或 *)。开启定界符和关闭定界符之间必须存在非空的行内序列;这些形成了强强调行内的内容。
除非经过反斜杠转义,否则字面意义上的 * 字符不能出现在 * 定界的强调或 ** 定界的强强调的开头或结尾。
除非经过反斜杠转义,否则字面意义上的 _ 字符不能出现在 _ 定界的强调或 __ 定界的强强调的开头或结尾。
在上述 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
这不是强调,因为开启的 * 后跟有空格,因此不是 左侧侧翼定界符序列 的一部分。
这不是强调,因为开启的 * 前是字母数字,后是标点符号,因此不是 左侧侧翼定界符序列 的一部分。
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
任何非空的行内元素序列都可以作为强调跨度的内容。
特别是,强调和强强调可以嵌套在强调内。
但请注意:
区别在于,在上述情况中,内部定界符 可以关闭强调,而在有空格的情况下,它们不能。
注意,在以下情况中我们无法得到强强调,因为开启定界符在 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 中的惯例进行转义。
链接可以包含片段标识符和查询字符串。
[link](#fragment)
[link](http://example.com#fragment)
[link](http://example.com?foo=bar&baz#fragment)
<p><a href="#fragment">link</a></p>
<p><a href="http://example.com#fragment">link</a></p>
<p><a href="http://example.com?foo=bar&baz#fragment">link</a></p>
请注意,不可转义字符前的反斜杠仅被视为反斜杠。
URL 转义在目标内部应保持不变,因为所有 URL 转义字符也是合法的 URL 字符。目标中的 HTML 实体将像往常一样被解析为相应的 Unicode 码位,并在编写为 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 大小写折叠,并将连续的内部 空白 合并为单个空格。如果存在多个匹配的引用链接定义,则使用文档中首先出现的那个。(在这种情况下发出警告是值得推荐的。)
第一个链接标签的内容被解析为行内元素,用作链接的文本。链接的 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/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>
大写字母也可以。
自动链接中不允许出现空格。
反斜杠转义在自动链接内不起作用。
<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@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 则更宽松。)
一个 属性值规范 由可选的 空白、= 字符、可选的 空白 和 属性值 组成。
一个 属性值 由 无引号属性值、单引号属性值 或 双引号属性值 组成。
无引号属性值是一个非空字符串,不包括空格、"、'、=、<、> 或 `。
单引号属性值由 '、零个或多个不包含 ' 的字符,以及一个最后的 ' 组成。
双引号属性值由 "、零个或多个不包含 " 的字符,以及一个最后的 " 组成。
开放标签 由 < 字符、标签名称、零个或多个 [属性](@attribute]、可选的空白字符、可选的 / 字符和一个 > 字符组成。
一个 结束标签 由字符串 </、标签名称、可选的 空白 以及 > 字符组成。
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>
可以使用自定义标签名称
<responsive-image src="foo.jpg" />
<My-Tag>
foo
</My-Tag>
<responsive-image src="foo.jpg" />
<My-Tag>
foo
</My-Tag>
非法标签名称,不会被解析为 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 行内元素(字符串、代码 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"
每一行被处理的文本都会对这棵树产生影响。通过分析该行,根据其内容,文档可以通过以下一种或多种方式进行变更
一旦某一行以这种方式被合并到树中,它就可以被丢弃,因此输入可以以流的形式读取。
对于每一行,我们遵循以下程序
首先,我们遍历开放的块,从根文档开始,沿最后一个子节点向下,直到最后一个开放块。每个块都施加了一个条件,如果要保持块开放,该行必须满足该条件。例如,块引用要求有 > 字符。段落要求非空行。在这个阶段,我们可能匹配所有或部分开放的块。但我们还不能关闭不匹配的块,因为我们可能遇到了惰性延续行。
接下来,在消耗了现有块的延续标记后,我们寻找新的块开始(例如,块引用的 >)。如果我们遇到新的块开始,我们会在将新块创建为最后一个匹配块的子节点之前,关闭步骤 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 之上的所有分隔符。