Markdown 加粗、斜体与删除线的写法和坑

Markdown 写作约 6 分钟

加粗写 **加粗**,斜体写 *斜体*,删除线写 ~~删除线~~。星号放在词中间也能用,下划线不行,删除线是 GitHub Flavored Markdown 的扩展,不属于 CommonMark 的核心。强调失败、页面上留下原样的星号,几乎都是因为紧贴分隔符的那个字符,** 开头紧挨标点、或者结尾紧跟在标点后面,它和相邻文字之间就必须隔一个空格或者再隔一个标点,这在中文里最容易踩到,比如 **注意:**这样不行。妙言 V4.5.0 在「格式」菜单里有粗体、斜体和删除线,规则卡住的时候可以退回 <b> 和 <del> 标签。

星号、下划线与组合

*斜体* 或 _斜体_
**粗体** 或 __粗体__
***粗斜体***
**粗体里有 *斜体* 一段**
~~**又删除又加粗**~~

两种字符生成的 HTML 一样,一个是 <em>,两个是 <strong>,三个是粗体外面再包一层斜体,嵌套顺序随意,**_x_** 和 ***x*** 都是粗斜体。一段强调不能跨过空行,**粗体、一个空行、断开** 三者合起来还是原样的星号。强调在标题、列表项、表格单元格、链接文字里都能用,也可以包住行内代码。

区别在词中间的行为。un*frigging*believable 中间会变斜体,un_frigging_believable 和 snake_case_name 则保持原样,因为 CommonMark 不允许 _ 在词内部开启或结束强调。这样标识符是安全的,不过单独成词的 __init__ 仍然会变成粗体的 init,代码要放进反引号里。默认用 * 可以同时躲开这两个麻烦,因为它在词内和中文旁边的行为一致。

为什么有些强调会失效

CommonMark 看的是 * 或 _ 这一串字符两侧的字符。用大白话说,一串分隔符要能开启强调,后面不能是空白,而且后面如果是标点,前面必须是空白或标点,要能结束强调,前面不能是空白,而且前面如果是标点,后面必须是空白或标点,规范里把这两种叫作左侧夹持和右侧夹持,下划线在此之上再加一条词内规则。

下面这些例子在妙言 V4.5.0 自带的 cmark-gfm 0.29.0.gfm.3 里跑过,四个扩展也都打开了:

写法 结果
这是**加粗**文字 可以,中文字符算普通字符
**加粗**,逗号 和 **加粗**。 可以,结尾的 ** 前面是汉字
**注意:**这样不行 失败,留下原样的星号
中文**(括号)**继续 失败
句子**“引号”**后面 失败
**bold.**Next 失败,ASCII 里原因相同
这是_斜体_文字 失败,_ 在词内被禁止
这是*斜体*文字 可以

失败的几行形状相同,分隔符的里侧是标点,外侧是普通字符。全角的 :、“、( 都算标点,而中文又没有空格来满足规则。有三种改法在同一轮测试里都成立,把标点挪到外面,**注意**:这样可以,在分隔符旁边加空格或者再放一个标点,**注意:** 这样可以 或者 (**括号**)继续,再就是改用 <strong> 标签。

还有一点取决于引擎。CommonMark 0.31.2 规范把 ¥ 这类符号也算作标点,而妙言用的这版 cmark-gfm 仍然把 价格**¥100**元 渲染成粗体,所以在妙言里正常的笔记,换成按 0.31 规则实现的解析器可能就坏了,要发布到哪里就在哪里测一遍。妙言的编辑区也用自己的正则给粗体和斜体上色,并不走 cmark,那条正则只要求标记内侧不是空白,所以像 **注意:**这样 这样的片段在编辑区里会被染成粗体,预览里却显示星号,所以以预览为准。

删除线

~~文字~~ 是 GFM 的扩展,妙言 V4.5.0 已经启用。GFM 规范写的是一个或两个波浪线都行,GitHub 文档里列的是 ~~ ~~ 或 ~ ~。测试里 ~one~ 会划掉,但数量不一致的 ~~a~,以及行中间的三个波浪线,都保持原样。

单个波浪线对中文和技术文字是个坑。3~5 天,6~10 天 渲染出来是 3、被划掉的「5 天,6」、再加 10 天。~5 和 ~10 这种前面带空格的写法没事,~/Documents 也保持原样。表示范围就写 3-5,或者用全角的 ~,或者写成 \~,其余可转义的字符见 转义字符。想要删除线时写两个波浪线更稳。编辑区自己的删除线正则只认两个波浪线,所以单波浪线的删除线只在预览里生效,编辑区里看不到样式。

删除线不在 CommonMark 规范里,不带这个扩展的解析器会把波浪线直接打出来,CommonMark 与 GFM 里列了其他差异,文字要送到这类渲染器的话,用 <del> 或 <s> 更保险。

妙言里的菜单和快捷键

V4.5.0 里「格式」菜单有这几项:

菜单项 英文标签 快捷键 写入
粗体 Bold ⌘B **文字**
斜体 Italic ⌘I *文字*
删除线 Deleteline ⌘⇧X ~~文字~~

选中文字时命令会把它包起来,光标停在结尾标记后面,没选中时插入一对空标记,光标落在中间。它只包不切换,在已经加粗的文字上再按一次 ⌘B 会多包一层变成 ****文字****,要自己手动删掉。这些命令需要编辑区有焦点,预览模式下什么也不做。同一个菜单里还有下划线,写出来是 <u>文字</u>,因为 Markdown 没有下划线语法。其余语法的对应关系见 速查表。

什么时候用 HTML

预览会渲染原始 HTML,所以 <b>、<strong>、<del> 和 <s> 都能用,分隔符贴着标点或中文又加不了空格的时候就用它们。行内标签里的 Markdown 仍然会解析,<b>*x*</b> 得到的是粗体里包斜体。

标签要和文字放在同一行,单独占一行的标签会开启 HTML 块,块里面的行不再按 Markdown 解析。代价在另一头,会剥掉原始 HTML 的渲染器会丢掉这些标签,你想要强调的地方就只剩普通文字。

怎么检查

打开预览,找有没有残留的 *、_ 或 ~,然后依次排查,分隔符内侧是不是有空白,分隔符是不是一侧挨着标点、另一侧是字符,下划线是不是在词中间,波浪线数量是否一致,开头和结尾标记之间是不是有空行。字符还是被当成语法的话,就用反斜杠转义。

常见问题

粗体是两个星号还是两个下划线?

两种都合法,星号更稳,因为下划线在词内和中文旁边不起作用。

为什么加粗后面紧跟中文标点时会显示星号?

结尾的 ** 前面是标点、后面是字符,无法闭合,把标点挪到外面、加一个空格,或者改用 <strong>。

GitHub 支持单波浪线的删除线吗?

它的文档列出了 ~~text~~ 和 ~text~,GFM 规范也写的是一个或两个波浪线。