CommonMark:Markdown 的正式规范

已发表: 2022-03-10
快速总结↬ Markdown 是一种强大的标记语言,它允许以纯文本格式进行编辑和格式化,然后可以将其解析并呈现为 HTML。 它具有声明性语法,对于技术人员和非技术人员来说,它既强大又易于学习。 然而,由于其原始规范中的相应歧义,已经有许多不同的风格(或自定义版本)旨在消除这些歧义并扩展原始语法支持。 这导致了可以解析的内容和渲染的内容之间的巨大差异。 CommonMark 旨在提供反映其实际使用情况的标准化 Markdown 规范。

CommonMark 是 Markdown 语法的合理化版本,其目标是消除原始 Markdown 规范周围的歧义和不一致。 它提供了一个标准化的规范,定义了语言的通用语法以及一套综合测试来验证 Markdown 实现是否符合该规范。

GitHub 使用 Markdown 作为其用户内容的标记语言。

“CommonMark 是一个雄心勃勃的项目,旨在正式指定 Internet 上许多网站使用的 Markdown 语法,以反映其实际使用情况 [...] 它允许人们在为开发人员提供的同时继续以他们一贯的方式使用 Markdown一个全面的规范和参考实现,以在平台之间以一致的方式互操作和显示 Markdown。”

——“GitHub Flavored Markdown 的正式规范”,GitHub 博客

2012 年,GitHub 开始创建自己的 Markdown 风格——GitHub Flavored Markdown (GFM)——以应对 Markdown 标准化的缺乏,并将语法扩展到其需求。 GFM 建立在 Sundown 之上,Sundown 是 GitHub 专门为解决当时现有 Markdown 解析器的一些缺点而构建的解析器。 五年后的 2017 年,它宣布弃用 Sundown,转而支持 CommonMark 解析和渲染库,即 GitHub Flavored Markdown 的正式规范中的 cmark。

在 Markdown 和 Visual Studio Code 的常见问题部分中,记录了 VSCode 中的 Markdown 使用 markdown-it 库以 CommonMark Markdown 规范为目标,该库本身遵循 CommonMark 规范。

CommonMark 已被广泛采用和实现(参见 CommonMark 实现列表),用于不同的语言,如 C(例如 cmark)、C#(例如 CommonMark.NET)、JavaScript(例如 markdown-it)等。这对开发人员来说是个好消息并且作者正在逐渐转向能够以一致的语法和标准化的规范使用 Markdown 的新领域。

关于 Markdown 解析器的简短说明

Markdown 解析器是直接或间接将 Markdown 文本转换为 HTML 的核心。

像 cmark 和 commonmark.js 这样的解析器不会直接将 Markdown 转换为 HTML,而是将其转换为抽象语法树 (AST),然后将 AST 呈现为 HTML,从而使处理过程更加精细且易于操作。 例如,在解析 - 到 AST - 和呈现 - 到 HTML 之间,可以扩展 Markdown 文本。

跳跃后更多! 继续往下看↓

CommonMark 的 Markdown 语法支持

已经将 CommonMark 规范作为其特定风格的基线的项目或平台通常是 CommonMark Markdown 规范严格子集集。 在大多数情况下,CommonMark 通过构建一个可构建的规范来减轻很多歧义。 GFM 就是一个典型的例子,虽然它支持所有 CommonMark 语法,但它还对其进行了扩展以适合其使用。

CommonMark 的语法支持一开始可能会受到限制,例如,它不支持此表语法,但重要的是要知道这是设计使然,因为此对话线程中的评论显示:支持的语法严格的,并且说成为语言本身的核心语法——与它的创建者 John Gruber 在 Markdown: Syntax 中指定的相同。

在撰写本文时,以下是一些受支持的语法:

  1. 段落和换行符,
  2. 标头,
  3. 强调和强烈的强调,
  4. 水平规则,
  5. 列表,
  6. 链接,
  7. 图片,
  8. 块引用,
  9. 代码,
  10. 代码块。

为了跟随示例,建议您使用 commonmark.js dingus 编辑器来尝试语法并获取呈现的预览、生成的 HTML 和 AST。

段落和换行符

在 Markdown 中,段落是由至少一个空行分隔的连续文本行。

以下规则定义了一个段落:

  1. Markdown 段落在 HTML 中呈现为段落元素 <p>。
  2. 不同的段落之间用一个或多个空行分隔。
  3. 对于换行符,段落应该用两个空格(或其等效的制表符)或反斜杠( \ )作为后缀。
句法呈现的 HTML
这是一行文字<p>这是一行文字</p>
这是一行文字
还有另一行文字
还有一个,但
同款
<p>这是一行文字
还有另一行文字
还有一个,但
同一段</p>
这是一段

还有一段

还有一个
<p>这是一段</p>
<p>还有一段</p>
<p>还有一个</p>
一行文本后的两个空格
或后置反斜杠\
两者都意味着换行
<p>一行文本后两个空格<br /><br>或后置反斜杠<br /><br>都表示换行</p>
  • 交互式教程来了解段落。
  • Dingus 永久链接使用预览和 AST 查看完整示例。
  • 了解有关段落的更多信息。

标题

Markdown 中的 Headings 代表 HTML Heading 元素之一。 定义标题的方法有两种:

  1. ATX 航向。
  2. 塞文标题。

以下规则定义 ATX 标题:

  1. 支持标题级别 1 ( h1 ) 到标题级别 6 ( h6 )。
  2. Atx 样式的标题以井号 ( # ) 符号为前缀。
  3. 至少需要有一个空格分隔文本和井号 ( # ) 符号。
  4. 哈希的计数等于标题的基数。 一个哈希是h1 ,两个哈希是h2 ,6 个哈希是h6
  5. 也可以在标题上附加任意数量的哈希符号,尽管这不会产生任何影响(即# Heading 1 #
句法呈现的 HTML
# 标题 1 <h1>标题 1</h1>
## 标题 2 <h2>标题 2</h2>
### 标题 3 <h3>标题 3</h3>
#### 标题 4 <h4>标题 4</h4>
##### 标题 5 <h5>标题 5</h5>
###### 标题 6 <h6>标题 6</h6>
## 标题 2 ## <h2>标题 2</h2>

以下规则定义了 Settext 标题:

  1. 仅支持标题级别 1 (h1) 和标题级别 2 (h2)。
  2. Settext 样式定义分别用等号 (=) 和破折号完成。
  3. 对于 Setext,至少需要一个等号或破折号。
句法呈现的 HTML
标题 1
=
<h1>标题 1</h1>
标题 2
-
<h2>标题 2</h2>
  • 交互式教程来了解标题。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关 ATX 航向的更多信息。
  • 了解有关 Settext 标题的更多信息。

强调和强烈的强调

Markdown 中的强调可以是斜体或粗体(强调强调)。

以下规则定义了强调:

  1. 普通强调和强烈强调在 HTML 中分别呈现为 Emphasis、<em> 和 Strong、<strong> 元素。
  2. 由单个星号 ( * ) 或下划线 ( _ ) 界定的文本将是重点。
  3. 以双星号或下划线为界的文本将是一个重点。
  4. 边界符号(星号或下划线)必须匹配。
  5. 符号和所附文本之间不得有空格。
句法呈现的 HTML
_Italic_ <em>斜体</em>
*Italic* <em>斜体</em>
__Bold__ <strong>粗体</strong>
**Bold** <strong>粗体</strong>
  • 交互式教程,了解重点。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解更多关于强调和强调的信息。

水平尺

水平规则 <hr/> 由三个或更多星号 ( * )、连字符 ( - ) 或下划线 ( _ ) 在新行上创建。 符号由任意数量的空格分隔,或者根本不分隔。

句法呈现的 HTML
*** <hr />
* * * <hr />
--- <hr />
- - - <hr />
___ <hr />
_ _ _ <hr />
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关主题休息的更多信息。

列表

Markdown 中的列表要么是项目符号(无序列)列表,要么是有序列表。

以下规则定义了一个列表:

  1. 项目符号列表在 HTML 中呈现为无序列表元素 <ul>。
  2. 有序列表在 HTML 中呈现为有序列表元素 <ol>。
  3. 项目符号列表使用星号、加号和连字符作为标记。
  4. 有序列表使用数字后跟句点或右括号。
  5. 标记必须是一致的(您必须只使用开头的标记用于列表项定义的其余部分)。
句法呈现的 HTML
* 一
* 二
* 三
<ul>
<li>一个</li>
<li>两个</li>
<li>三个</li>
</ul>
+ 一
+ 两个
+ 三个
<ul>
<li>一个</li>
<li>两个</li>
<li>三个</li>
</ul>
- 一
- 二
- 三
<ul>
<li>一个</li>
<li>两个</li>
<li>三个</li>
</ul>
- 一
- 二
+ 三个
<ul>
<li>一个</li>
<li>两个</li>
</ul>
<ul>
<li>三个</li>
</ul>
1. 一
2. 两个
3. 三
<ol>
<li>一个</li>
<li>两个</li>
<li>三个</li>
</ol>
3. 三
4.四个
5. 五
<ol 开始=“3”>
<li>三个</li>
<li>四个</li>
<li>五个</li>
</ol>
1. 一
2. 两个
3. 三
<ol>
<li>一个</li>
<li>两个</li>
<li>三个</li>
</ol>
  • 了解列表的交互式教程。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关列表项的更多信息。

链接

内联参考格式支持链接。

以下规则定义了一个链接:

  1. 链接呈现为 HTML Anchor 元素 <a>。
  2. 内联格式的语法为: [value](URL "optional-title") ,括号之间没有空格。
  3. 引用格式的语法: [value][id]表示引用, [id]: href "optional-title"表示超链接标签,至少用一行分隔。
  4. id定义标识符,可以由字母、数字、空格和标点符号组成。
  5. 定义标识符不区分大小写。
  6. 还支持自动链接,其中 URL 以小于 (<) 和大于 (>) 符号为界,并按字面显示。
 <!--Markdown--> [Google](https://google.com “Google”) <!--Rendered HTML--> <a href="https://google.com" title="Google">Google</a> <!--Markdown--> [Google](https://google.com) <!--Rendered HTML--> <a href="https://google.com">Google</a> <!--Markdown--> [Comparing Styling Methods in Next.js](/2020/09/comparing-styling-methods-next-js) <!--Rendered HTML--> <a href="/2020/09/comparing-styling-methods-next-js">Comparing Styling Methods In Next.js</a> <!--Markdown--> [Google][id] <!--At least a line must be in-between--> <!--Rendered HTML--> <a href="https://google.com" title="Google">Google</a> <!--Markdown--> <https://google.com> <!--Rendered HTML--> <a href="https://google.com">google.com</a> <!--Markdown--> <[email protected]> <!--Rendered HTML--> <a href="mailto:[email protected]">[email protected]</a>
  • 交互式教程以了解链接。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关链接的更多信息。

图片

Markdown 中的图像遵循链接的内联参考格式。

以下规则定义图像:

  1. 图像被呈现为 HTML 图像元素 <img>。
  2. 内联格式的语法为: ![alt text](image-url "optional-title")
  3. 参考格式的语法为: ![alt text][id]用于参考, [id]: image-url "optional-title"用于图像标签。 两者应至少用一个空行分隔。
  4. 图片标题是可选的,image-url 可以是相对的。
 <!--Markdown--> ![alt text](image-url "optional-title") <!--Rendered HTML--> <img src="image-url" alt="alt text" title="optional-title" /> <!--Markdown--> ![alt text][id] <!--At least a line must be in-between--> <!--Markdown--> <!--Rendered HTML--> <img src="image-url" alt="alt text" title="optional-title" />
  • 交互式教程来了解图像。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关图像的更多信息。

块引用

HTML 块引用元素 <blockquote> 可以通过在新行前加上大于号 ( > ) 来创建。

 <!--Markdown--> > This is a blockquote element > You can start every new line > with the greater than symbol. > That gives you greater control > over what will be rendered. <!--Rendered HTML--> <blockquote> <p>This is a blockquote element You can start every new line with the greater than symbol. That gives you greater control over what will be rendered.</p> </blockquote>

块引用可以嵌套:

 <!--Markdown--> > Blockquote with a paragraph >> And another paragraph >>> And another <!--Rendered HTML--> <blockquote> <p>Blockquote with a paragraph</p> <blockquote> <p>And another paragraph</p> <blockquote> <p>And another</p> </blockquote> </blockquote> </blockquote>

它们还可以包含其他 Markdown 元素,如标题、代码、列表项等。

 <!--Markdown--> > Blockquote with a paragraph > # Heading 1 > Heading 2 > - > 1. One > 2. Two <!--Rendered HTML--> <blockquote> <p>Blockquote with a paragraph</p> <h1>Heading 1</h1> <h2>Heading 2</h2> <ol> <li>One</li> <li>Two</li> </ol> </blockquote>
  • 交互式教程,了解块引用。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关 Blockquotes 的更多信息。

代码

还支持 HTML 内联代码元素 <code>。 要创建一个,请使用反引号 (`) 分隔文本,如果需要在封闭文本中使用文字反引号,则使用双反引号。

 <!--Markdown--> `inline code snippet` <!--Rendered HTML--> <code>inline code snippet</code> <!--Markdown--> `<button type='button'>Click Me</button>` <!--Rendered HTML--> <code><button type='button'>Click Me</button></code> <!--Markdown--> `` There's an inline back-tick (`). `` <!--Rendered HTML--> <code>There's an inline back-tick (`).</code>
  • 交互式教程来了解代码。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关代码跨度的更多信息。

代码块

还支持 HTML 预格式化文本元素 <pre>。 这可以通过至少三个和相等数量的边界反引号 ( ` ) 或波浪线 ( ~ ) 来完成 - 通常称为代码围栏,或至少 4 个空格的新行开始缩进。

 <!--Markdown--> ``` const dedupe = (array) => [...new Set(array)]; ``` <!--Rendered HTML--> <pre><code>const dedupe = (array) => [...new Set(array)];</code></pre> <!--Markdown--> const dedupe = (array) => [...new Set(array)]; <!--Rendered HTML--> <pre><code>const dedupe = (array) => [...new Set(array)];</code></pre>
  • 交互式教程来了解代码。
  • Dingus 永久链接可查看完整示例与预览和 AST。
  • 了解有关 Fenced 和 Indented 代码块的更多信息。

使用内联 HTML

根据 John Grubers 关于内联 HTML 的原始规范说明,任何 Markdown 语法未涵盖的标记,您只需使用 HTML 本身,唯一的限制是块级 HTML 元素 - 例如<div><table><pre><p>等 — 必须用空行与周围的内容分隔,并且块的开始和结束标记不应使用制表符或空格缩进。

但是,除非您可能是 CommonMark 本身或附近的人之一,否则您很可能会使用已经扩展以处理 CommonMark当前不支持的大量语法的风格编写 Markdown。

向前走

CommonMark 是一项持续不断的工作,其规范最后更新于 2019 年 4 月 6 日。Markdown 工具池中有许多流行的应用程序支持它。 有了 CommonMark 对标准化的努力的认识,我认为足以得出结论,在 Markdown 的简单性中,有很多工作在幕后进行,对于 CommonMark 的努力来说,GitHub Flavored Markdown 的正式规范是一件好事是基于规范。

向 CommonMark 标准化工作的转变并不会阻止创建风格以扩展其支持的语法,并且随着 CommonMark 为 1.0 版准备好必须解决的问题,有一些关于持续努力的有趣资源可以用于您的细读。

资源

  • John Gruber 介绍 Markdown
  • CommonMark 官方网站
  • GitHub 风格的 Markdown 规范
  • cmark 官方回购
  • GitHub 的 cmark 分支
  • 维基百科上的降价
  • 降价指南