这是专题的一部分,详见:Markdown知识贴
目前很多Markdown语法说明(特别是国内的教程)只介绍Markdown具体语法的使用,而本章主要会介绍一般教程提及不多的知识。所以在阅读之前,你应该掌握最基础的Markdown语法,请看为知的帮助文档:http://www.wiz.cn/feature-markdown.html 。具体某个语法的书写,请参考其他教程,比如原生Markdown语法说明。
Markdown的组成与结构:
组成Markdown正文的元素可以分成区块元素和行内元素。
简单说,区块元素主要用来定义内容,提供框架,比如这是标题,这是列表,这是段落之类的。
而行内元素主要是提供实质内容,比如图片,文字之类的。好比说一个表格(区块),我们需要文字图片(行内元素)来填充它。
我们可以将一份Markdown文档看成一系列连续的区块元素(包括段落、区块引用、列表、标题、分隔线、区块代码等)的纵向排列,部分区块(如引用和列表)可以包含其他区块,而其他的区块(如标题和段落)则包含行内元素,如文本、链接、图片、行内代码等等。
——翻译自 commonmark.org
段落、文本行、换行
段落与行的概念在Markdown中很重要,而且很基础,可惜很多Markdown教程都将其忽略。
一个文本行指连续两个换行之间的内容,并非指最终显示所看到的行。简单说,手动换行才叫一行,自动换行形成的不叫一行。
在为知md插件下表现很明显,感受一下:
段落由一个或多个连续文本行组成,两个段落间由空行分开。
这是段落一的第一行。
这是段落一的第二行。
这是段落二的第一行,这是段落二的第一行,这是段落二的第一行。
这是段落二的第二行,这是段落二的第二行,这是段落二的第二行。
强制换行:当一个段落需要包含多个文本行时,需要先在行末敲入两个或以上空格再回车。不过目前很多编辑器(包括为知)都提供lazy方法,可以直接敲回车换行。
知道这些有什么用?可以用来调整文档的疏密。段落间的间距比较宽,如果希望文档密一点,多使用强制换行,如果希望文档优雅整齐一点,可以多用段落。
区块元素的相接
前面说过一份Markdown文档看成一系列连续的区块元素的纵向排列,所以一份Markdown文档的结构基本时由区块元素决定的。而区块元素之间的关系有两种——首尾相接与嵌套。
原生的Markdown只规定了段落间需要空行,没有规定其他的区块如何相接。想当然的其他区块直接换行相接就可以了,但是真的是这样吗?本人使用为知和edior.md插件中进行了测试,结果如下:
注:1. 用“\”表示可以直接换行相接。2. 空白HTML标签一般指<!-- -->
。其中空格处可以填入任意文字。
测试结果与想象相去甚远,不同区块间有不同的规则,不同的编辑器亦有不同的相接规则,这个不是仅在为知才出现的个例,而是几乎所有编辑器共有的现象。
尽管区块相接有严重的方言问题,但是有一些规则是通用的:
- 有明确结束标识的区块与有明确开始标识的区块相接可不用空行。当我们回头原生Markdown语法设计的时候,会发现虽然Markdown也是一种轻量级的标记语言,可是基本所有区块元素只有开始标识(比如引用使用
>
开始,无序列表使用*
、+
或-
开始),却没有结束标识,这种区块的结束取决于下一个区块或空行,这根据编辑器的不同就会产生不同的结果。但是后来加入的部分语法开始使用包裹式的语法,比如区块公式使用$$
包裹,这些区块有明确的结束标识。当有明确结束标识的区块与有明确开始标识的区块相接时,一般编辑器都不会出现差异,所以可以不用空行,直接换行相接。 - 分隔线与标题后接区块可以不空行。分隔线与标题占用的行数比较固定,只有一行或两行,其后可以直接换行接任何区块元素。
- 区块引用和列表后接其他区块必须空行。区块引用和列表与其他区块不同,它们可以嵌套区块, 而且有lazy输入,所以必须用空行区分。
- 使用减号画分隔线时,前面必须预留一空行。因为连续三个减号
---
这个语法与Setext标题语法重合了,不留空行容易造成识别错误。如果不想使用预留空行,可以在减号内加空格,如- - -
,以免与Setext标题语法重合。 - 空白HTML标签
<!-- -->
的使用。一般只在带lazy输入的区块连续出现的时候用于分割区块,目前允许lazy输入的区块有:区块引用、缩进式代码、列表。空白HTML标签不会显示在最后的输出中显示。
区块元素的嵌套
区块元素中引用和列表可以实现区块的嵌套,我们将其称作可嵌套区块。当我们说嵌套的时候,并非只指自身的嵌套,而是说可以嵌套基本所有类型的区块。
引用的嵌套
引用的嵌套相对简单,只要所有的嵌套内容每行行首加上>
,跟写简单的文字引用一样,只不过每一行都加>
而已,多级嵌套就多加几个>
。
>段落1行1
>段落1行2
>
>---
>段落2
>
>>二级引用
这是最后的显示结果:
段落1行1
段落1行2段落2
二级引用
说实在,引用的嵌套不实用,一般只会用到简单的文段引用,嵌套最多用到段落。未来Markdown被引入到电子邮箱领域后可能会有更多的应用吧。而且目前很多编辑器的引用嵌套都有个不起眼的小bug,嵌套中的标题会被纳入到目录大纲。
列表的嵌套
列表的嵌套相对复杂,目前一般的实现方式有两种,一是Tab方式,二是对齐方式。
Tab方式是原生Markdown提出嵌套方式,使用4个空格或1个Tab缩进实现多级嵌套。(这里的Tab指4个空格,有点编辑器Tab键值不一样,或者会根据上一行变化,需要注意)
几个Tab对应几级嵌套:
对齐方式是CommonMark提倡的方式,不固定每次嵌套所需的空格数量,使用空格缩进至嵌套内容与列表项目首行内容对齐为止。
两种方式各有优势,Tab方式更易写,每次缩进按一个Tab就可以;对齐方式更易读,写出来的文档与最后显示的结果更接近。
为知对Tab方式的支持更好,而Editor.md插件对对齐方式的支持更好。
使用插件的朋友需要注意一下几点:
- Editor.md插件中第一次编辑md会用缩进符显示Tab,这样录入和修改都很方便,但是第二次打开md文件tab符会自动转化成4个空格。
- Editor.md插件的Tab键值是F1。
其实还有一种混合的方法:在写多级嵌套表格的时候,用项目标识后多加1到2个空格让项目内容前面有4个字符的宽度,这样后面的次级列表用4个空格缩进既是Tab式又是对齐式的,如:
- 一级列表
1. 一个
1. 两个
- 一级列表
1. 一级列表
范围元素的嵌套和交叉
在原生Markdown中将所有的行内元素叫做范围元素。但是这里的范围元素指行内元素中采用标识包裹的语法元素,包括强调、重强调、删除线和行内代码。
如删除线内嵌套强调:1~~234*56*789~~0
。
如强调与重强调交叉:**1234*567**890*
。
注意:
- 行内代码可以被嵌套,但是不能嵌套其他范围元素。
- 强调、重强调与删除线不支持与自身嵌套或交叉,只能嵌套或交叉不同的范围元素。
- 为知对嵌套和交叉的支持有些小问题,Editor.md插件对交叉基本不支持。
Lazy输入
Markdown的目标是成为一种网络书写语言,所以为了让用户更方便书写,Markdown为部分语法提供了一些更简便更宽松的输入办法,这里将其称为“Lazy输入”(懒人输入办法)。
这里列举一些目前为知支持的Lazy输入:
缩进式区块代码和列表中的空行允许不缩进:
这二者会得到一样的结果。区块引用中空行行首允许不添加
>
:
二者会得到一样的结果。区块引用中的段落、区块代码、表格、区块公式允许只在区块首行添加
>
,其他行省略。
要求被包含的区块不能有空行,如果有空行,在空行的下一非空行需要添加>
。列表中被嵌套的段落、区块代码、表格、区块公式允许只在区块首行缩进,其他行不缩进。
一个段落区块中直接回车实现强制换行。在很多Markdown语法中,段落内部直接回车只能得到一个软换行。软换行就是Md源码中换行了,但是最后输出时不显示换行。段落内部强制换行需要行末先敲两个空格之后再回车。而为知中直接回车即可实现段落之内强制换行。
允许缩进一个空格快速实现2级列表和一级区块嵌套。一般建立二级列表或嵌套需要用对齐或Tab方式缩进,但是为知允许只缩进一个空格。但是这个方式不能用于两级以上的列表,会造成格式混乱。
允许直接插入自动链接。原生Markdown提供了快速插入链接的方法,称作自动链接,与一般的行内式和参考式方法不同,自动链接只需要把链接(或邮箱地址)用尖括号
< >
包裹起来即可。
另外,在区块相接部分,我们也可以理解为极大部分区块都使用空行区分,但是编辑器提供了Lazy输入办法,让部分区块可以不使用空行相接(只是可以这么理解,目前还没有一份Markdown说明是这么说的)。
软输入
软输入指只在源码中显示,却不影响最终显示的输入方法。软输入是为了让源码更加易读。软输入主要包括文本行首4个以下的空格,行末多余的空格,还有多余的空行,还有前面提到的原生Markdown中的行末回车软换行。这些在最终显示的时候都会移除。
在我看来显然Markdown受到了其他标记语言的影响,虽然文本行、段落和软换行这些概念在HTML之类的语言中很实用,但是Markdown拿来使用就显得有点画蛇添足。像多余的空行空格可以提高Markdown书写的容错率。但是软换行显然与Markdown易读易写的设计理念相悖,好在现在很多编辑器(包括为知)已经移除了软换行。
转义
Markdown之所以易写易读,很大程度的功劳在于它使用了大量的常见符号(甚至是空格空行回车)来构建语法,但是如果需要写入被占用的这些特殊符号,就会面临特殊符号被误识别为Markdown语法的可能性。这个时候就需要对特殊符号进行转义,转义方式为在符号前添加一个反斜线\
。
比如想输入:
2017. 金鸡报喜!
就需要在Markdown中这么写,否则会被识别为有序列表: 2017\. 金鸡报喜!
下面是John Gruber提供的符号列表,这些符号常常需要被转义(当然不限于这些符号):
\ 反斜线(backslash)
` 反引号(backtick)
* 星号(asterisk)
_ 下划线(underscore)
{} 大括号/花括号(curly braces)
[] 中括号/方括号(square brackets)
() 小括号/括弧(parentheses)
# 井号(hash mark)
+ 加号(plus sign)
- 减号/连字符(minus sign/hyphen)
. 英文句号/小数点(dot)
! 感叹号(exclamation mark)
输入Markdown代码
这里有个问题,如果我们想用区块代码记录Markdown围栏式区块代码怎么办?想用行内代码的内添加反引号`
怎么办?比如我现在使用Markdown写一篇Markdown教程,这种情况就会遇到。
我们知道无论围栏式区块代码还是行内代码,输入的内容都会原样输出,直到结束的反引号出现,如果代码内包含反引号,就可能被错误识别,提前结束代码。代码内部也不支持转义,上一节提到的转义技巧是无法使用的。
如果我们这么写,得到的结果会大跌眼镜:
目前有两个方法可以解决:
用更大的围栏包裹代码,如用4个反引号
`
。使用缩进式的方式写围栏式代码。
坏消息是为知两种方法都不支持,为知围栏式代码只支持3个反引号,不支持4个及以上的反引号,所以不能使用第一种方法。为知目前缩进式代码有bug,所以第二种方法也不能使用。Editor.md插件明明两种方法都支持的啊,为知差评。
那么行内代码内怎么添加反引号呢?行内代码也不能使用反斜线转义,无论```
或` ` `
都不能得到想要的结果。这时应该使用一种类似上面围栏式代码的方法。如果要行内代码内添加一个反引号,就用两个反引号包裹,如果要添加两个连续的反引号,就用三个反引号包裹代码,以此类推。
这是一个反引号的写法:`` ` ``
。
这是两个连续反引号的写法:``` `` ```
。
如果代码存在多个反引号,但不连续,用两个反引号包裹:`` 行内代码用`反引号`包裹 ``
。
这个为知是支持的,不用担心。
兼容HTML
非程序员用户可能不知道,Markdown兼容HTML标签语法。这就能解释为什么有的用户用尖括号< >
包裹内容的时候会发现内容消失了,因为它被识别当作为HTML标签了!这时候对尖括号转义即可。
虽然Markdown兼容HTML,但是在Markdown使用HTML标签显然不符合Markdown的设计的本意,而且懂HTML的人也不多,所以HTML标签是不推荐使用的。
本人觉得目前用得上的HTML标签只有两个:
一是<!-- -->
,用于区分特殊的区块,比如引用。
二是<u> </u>
,用于实现下划线样式。