English document is under construction.
- 语法优雅
- 标记简洁 & 易于书写
- 语法健全. 不能出现自相矛盾, 漏洞百出, 解释不清的地方
- 标记含义明确, 不要有歧义
- 标记不可以对正文文字造成视觉干扰 (不能让阅读者看着眼花缭乱, 分不清主次, 感到不适或困惑等)
- 标记规则的设计有完整的内在逻辑. 不能让各种标记百花齐放, 毫无规律可循
- 标记是宽容的. 错误的标记不会造成文本渲染的灾难
- 可扩展, 可编程. 最好支持脚本语言 (python/javascript) 的控制
- 可配置: 全局配置, 项目配置, 单文件配置 (front matter), 局部配置 (block scope)
- 支持项目级文档输出 ("导出成册")
- 持续更新, 标记规则将纳入版本控制, 与时俱进
如果您非常熟悉 markdown. 建议阅读这篇文章: 一对一的语法比较, 来快速学习新的文本标记语言.
"标记的边界" 指标记的开始处和结尾处, 它们需要明确的符号来指明.
例如, 使用括号包裹的文本, 左括号和右括号是它的边界; 使用引号包裹的文本, 前引号和后引号是它的边界.
在 markowl 中, 我们使用且唯一使用的边界符号是成对的方括号 ([]
).
即, 任何一段要被标记的文本, 都必须以 [
开始, 最后以 ]
结尾. 这段被包裹的部分才是有效标记文本.
嵌套规则如下:
-
默认情况下, 作用域是继承的; 即外层括号的效果会同时作用到内层括号:
这是一个 [**加粗并[==高亮]] 的效果.
注: 我们另有其他标记可以清除这种继承效果, 见后续的语法表.
-
内层括号的优先级高于外层, 当效果产生冲突时, 以内层括号为准:
这是 [#red;红色字体和[#green;绿色字体]].
综合示例:
这篇文章是 [关于 [**加粗], [*斜体], [==高亮], [~~[#red;删除线]] 效果的说明 <https://example.com/article.html>].
另附 markdown 同样效果的写法:
这篇文章是 [关于 **加粗**, *斜体*, <span style="background-color: yellow; color: black">高亮</span>, ~~<font color="red">删除线</font>~~ 效果的说明](https://example.com/article.html).
标记分为 "标准语法" 和 "简写语法".
标准语法非常强调逻辑性和严整性. 简写语法则充分利用符号的特点, 在保证无歧义的情况下提高书写和阅读的流畅性.
常用的标准语法都提供了对应的简写语法. 其次 markowl 的编程特性允许用户自定义 (扩展或重写) 简写语法. (本节只介绍前者)
[正文内容 :指令(参数1, 参数2, 参数3, ...)]
[正文内容 :指令缩写]
快速演示:
这是一个 [加粗:bold()] 效果
这是一个 [加粗:b] 效果
[这是一级标题:heading(level=1, numberic=false)]
[这也是一级标题:h1]
这是本项目的 [链接 :link(https://github.com/likianta/markowl-lang)].
下面是一张图片:
[:image(https://example.com/image.png, width=600, height=400, align=center)]
详细说明:
-
[正文内容 :指令(参数1, 参数2, 参数3, ...)]
格式细节:
- 指令位于后括号附近.
- 指令必须以冒号开头, 后跟一个指令名称 (名称由英文和下划线组成).
- 冒号前面的空格 (或者称之为正文与指令之间的空隙) 是可选的. 在渲染时, 该空格会被忽略.
- 指令的传参格式借鉴了 python 函数调用. 可以使用位置参数和关键字参数, 支持缺省参数.
- 但也有不同的地方:
- 对于字符串参数, 我们不会用引号包裹, 而是直接书写字面值.
- 对大小写不敏感. 例如, 布尔值我们推荐使用 true 和 false, 而不是 True 和 False. 未定义值推荐用 none 而不用 None.
- 从参数特征角度来讲, 我们在 markowl 的设计之初尽可能地使用简单的参数形式, 参数名称中不会包含空格, 结构体, 特殊符号等容易对标记结构造成破坏性影响的内容 (绝大多数情况, 只会用到单词, 连字符和下划线).
- 但也有不同的地方:
- 当参数为空时, 允许省略掉指令后面的小括号.
所有指令均会在后续的语法表中列出.
-
正文内容 :指令缩写
注意: 不是所有指令都有对应的指令缩写. 只有常用的会提供.
部分指令名称提供了常见的缩写形式, 例如 ":bold()" 和 ":b", ":image(url, ...)" 和 "img(url)".
请注意缩写并非是简单地使用了单词的省略写法. 指令缩写的目的是 把常用的带参指令更快地调用出来, 因此写法上会兼顾简洁性和表达能力:
示例:
[这是一条注释. 注释不会被渲染. :comment()] [这是一条注释. 注释不会被渲染. :cmt] [一级标题 :heading(level=1, numberic=false)] [一级标题 :h1] [二级标题 :heading(level=2)] [二级标题 :h2] [三级标题 (带有编号) :heading(level=3, numberic=true)] [三级标题 (带有编号) :h3x] [h3x 中的 'x' 取自单词 'index' :cmt] [这是一个有序列表项 :list(order=true)] [这是一个有序列表项 :ol] [这是一个无序列表项 :list(order=false)] [这是一个无序列表项 :ul]
再次提醒: 只有常用的标准指令提供了指令缩写. 但用户可以通过编程/配置的方式添加自定义的指令缩写 (或者修改已有的指令缩写).
注意: 不是所有指令都有对应的简写格式. 只有常用的会提供.
[前置标记符;正文内容]
[前置标记符 正文内容]
[正文内容 后置标记符]
[正文内容;后置标记符]
格式说明:
- 前置标记符与正文内容之间使用空格或分号分隔. (空格或分号的作用是相同的, 都是为了与正文内容分开. 您可以根据样式观感来选择用哪个.)
- 后置标记符与正文内容之间使用空格或分号分隔. 但是分号目前仍处于实验阶段, 未经过充分测试; 建议只使用空格作为分隔.
- 在同一标记块中, 前置标记符与后置标记符最多只有其中一方会出现.
- 无论是前置标记符还是后置标记符, (从设计角度) 都不允许使用空格作为标记符自身的一部分.
快速演示:
(前置标记符演示:)
这是 [加粗:b], [斜体:i], [高亮:highlight] 效果
这是 [**加粗], [*斜体], [==高亮] 效果
[这是一级标题:h1]
[# 这也是一级标题]
这是一个 [自定义颜色和背景色的高亮文字:color(fg=white, bg=red)]
这是一个 [#white/#red;自定义颜色和背景色的高亮文字]
[这是一段注释. 注释不会被渲染. :cmt]
[/ 这是一段注释. 注释不会被渲染.]
(后置标记符演示:)
这是本项目的 [链接 :link(https://github.com/likianta/markowl-lang)].
这是本项目的 [链接 <https://github.com/likianta/markowl-lang>]
下面是一张图片:
[图片占位文字 :image(https://example.com/image.png, width=600, height=400, align=center)]
[图片占位文字 !<https://example.com/image.png 600x400>]
[/ 请注意, 有些简写语法无法像标准语法那样详细定义每个参数, 部分参数只能使用缺省值.]
简写语法与标准语法的重要区别有两点:
-
使用符号而非指令名称 (英文单词) 来减少视觉干扰.
-
符号的位置可以前置 (左括号附近), 也可以后置 (右括号附近). 而指令必须后置.
问: 为什么有些符号选择了前置, 有些选择了后置?
答: 它取决于在语法设计时, 对阅读的焦点和内容优先级的考虑. 具体要求为:
- 如果符号短促, 简洁, 则放在前置位置, 方便书写者一眼看到它的标记效果.
- 如果符号较长 (例如 url), 则放在后置位置, 确保书写者先看到主要文字 (正文), 然后是次要文字 (链接). (这也符合我们的书写习惯.)
下面的示例会加深此理解:
[这是一段非常长的文本, 我会对它标记为特定的格式. 直到你看到末尾, 你才知道我用了什么标记. :color(#427ecc)] [#427ecc 这是一段非常长的文本, 我会对它标记为特定的格式. 现在, 在你读到本段时, 你已经知道标记效果是什么了.]
通常地, 标准标记语法不可避免地会比 markdown 显得更加繁琐. 在日常书写中, 我们提倡使用简写语法.
- 如果标记位于本行末尾, 则后方括号可省略
- 如果标记位于本行开头, 则前方括号可省略
- 如果标记应用于整行, 则前后方括号均可省略
示例:
[# 这是标题]
# 这是标题
[> 这是一段引用.]
> 这是一段引用.
这是正文内容. [/ 这是注释]
这是正文内容. [/ 这是注释
- 这是一个列表项目
[| 这是一个软换行]
[| 这是一个软换行]
[| 这是一个软换行]
- 这是一个列表项目
| 这是一个软换行
| 这是一个软换行
| 这是一个软换行
/ 上面这个还有另一种写法:
- 这是一个列表项目
[这是一个软换行 |]
[这是一个软换行 |]
[这是一个软换行 |]
- 这是一个列表项目
这是一个软换行 |
这是一个软换行 |
这是一个软换行 |
略.
有两种方式禁止转义:
- 使用反斜杠 (\), 对反斜杠后面的一个字符产生作用
- 使用反引号 (`), 对成对的反引号包裹的内容产生作用
示例:
显示原始样式的 \[链接 <https://example.com>\]
显示原始样式的 `[链接 <https://example.com>]`
查看这篇文档: c:/documents/[#blue; \[01\]\[2022-04-29\] introduction.pdf]
查看这篇文档: c:/documents/[#blue; `[01][2022-04-29]` introduction.pdf]
注:
- 按字母表顺序排列.
- 在示例中, 一般会展示标准语法和简写语法这两种写法.
-
指令:
:align(alignment=center)
- alignment: 对齐方式
- 类型:
literal[center, left, right]
- center: 居中对齐
- left: 左对齐
- right: 右对齐
- 类型:
- alignment: 对齐方式
-
符号:
-^-
: 居中对齐-<-
: 左对齐->-
: 右对齐
-
符号位置:
- 前置
-
示例:
[这是一段居中的文本 :align(center)] -^- 这是一段居中的文本 [段落右对齐 :align(rihgt)] ->- 段落右对齐
综合运用:
/ 给一张照片添加标题. [风景 !<http://example.com/image.png 1200x600>] -^- 由 [xxx <...>] 拍摄并上传.
/ 给代码添加来源. ``` :lang(python) import sys print(sys.executable) ``` ->- [*代码片段来自 [<https://example.com/code-snip.html>]].
-
指令:
:bold()
-
缩写:
:b
-
符号:
**
-
符号位置:
- 前置
-
示例:
这是 [**加粗] 效果.
-
指令:
:cite()
-
符号:
>
-
符号位置:
- 前置
-
示例:
[这是一段引用. :cite] [> 这是一段引用.] > 这是一段引用.
-
指令:
:clear()
-
说明:
当使用嵌套标记时, 默认的行为内层标记会继承于外层; 使用本标记可清除这种继承效果.
-
示例:
这是 [**加粗并 [==高亮]] 的效果. 这是 [**加粗 [或者:clear] [[==高亮]:clear]] 的效果. [/ ps: 它相当于... ] 这是 [**加粗] 或者 [==高亮] 的效果.
-
符号:
``` language_or_command ```
- language_or_command:
union[language, command]
- language:
str[cpp, csharp, javascript, js, py, python, ...]
- command:
:bold
,:italic
,:comment
, ...
- language:
- language_or_command:
-
示例:
``` py print('hello world') ``` ``` :lang(py) print('hello world') ``` ``` :comment 整个文本块都是注释, 不会被渲染. 整个文本块都是注释, 不会被渲染. 整个文本块都是注释, 不会被渲染. ``` ``` :bold :color(red) 整个文本块以红色加粗字体显示. 整个文本块以红色加粗字体显示. 整个文本块以红色加粗字体显示. ``` ...
-
指令:
:color(fg_color, bg_color=none)
- fg_color: 前景色
- 类型:
union[hex, name, none]
- hex: 不区分大小写. 例如 '#00ff00'
- name: 标准色名称 (TODO:提供完整的参考表). 例如 'red', 'cyan'
- none: 不定义. 使用系统默认值
- 类型:
- bg_color: 背景色
- 类型:
union[hex, name, none]
- 默认值:
none
- 类型:
- fg_color: 前景色
-
符号:
#fg_color/#bg_color
: 指定前景色和背景色#fg_color
: 只指定前景色 (字体颜色)#none/#bg_color
: 只指定背景色
-
符号位置:
- 前置
-
示例:
这是 [绿色字体 :color(green)]. 这是 [红底白字 :color(white, red)] 效果. 这是 [自定义颜色的字体 :color(#2839ff)]. 这是 [字体颜色默认, 只定义了背景色 :color(none, yellow)] 的情况. 这是 [#green 绿色字体]. 这是 [#white/#red 红底白字] 效果. 这是 [#2839ff 自定义颜色的字体]. 这是 [#none/#yellow 字体颜色默认, 只定义了背景色] 的情况.
-
指令:
:comment()
-
缩写:
:cmt
-
符号:
/
-
符号位置:
- 前置
-
说明: 一般用于行尾或整行. 行内可以用, 但非常少见.
-
示例:
这是正文内容. [/ 这是注释] / 这一整行都是注释.
当用于块注释时, 这样做:
``` :cmt 这是块注释 这是块注释 这是块注释 ``` ``` / 这是块注释 这是块注释 这是块注释 ```
-
指令:
:heading(level=1, numberic=false)
-
缩写:
:h
: 表示一级标题:h1
: 一级标题:h1x
: x 取自 'index' 这个词. 表示带有编号的章节标题
-
符号:
#
: 一级标题#.
: 带有编号的一级标题
-
符号位置:
- 前置
-
说明:
二级标题, 三级标题按照上面的格式类推. 即
:h2
,:h3
, ... 或者##
,###
, ... -
示例:
标题 (没有自带编号):
[heading 1 :heading(1)] [heading 2 :heading(2)] [heading 3 :heading(3)] [heading 1 :h1] [heading 2 :h2] [heading 3 :h3] # heading 1 ## heading 2 ### heading 3
标题 (有自带编号):
[heading 1 :heading(1, true)] [heading 2 :heading(2, true)] [heading 3 :heading(3, true)] [heading 1 :h1x] [heading 2 :h2x] [heading 3 :h3x] #. heading 1 ##. heading 2 ###. heading 3
-
指令:
:highlight(fg_color=black, bg_color=yellow)
-
符号:
==
-
符号位置:
- 前置
-
示例:
这是 [高亮 :highlight] 效果. 这是 [自定义颜色的高亮 :highlight(white, red)] 效果. 这是 [==高亮] 效果. [/ 注: 简写语法相较于标准语法, 在参数定义方面受限. 我们可以换一种方法达到自定义颜色的高亮效果. 如下: ] 这是 [#white/#red[==自定义颜色的高亮]] 效果.
-
指令:
:image(url, width=none, height=none, align=center, fill=fit_width, **kwargs)
- url: 网络链接或本地路径 (绝对或相对位置).
- width: 自定义图片的宽度. 单位像素.
- 类型:
optional[int]
- 类型:
- height: 自定义图片的高度. 单位像素.
- 类型:
optional[int]
- 类型:
- align: 对齐方式. 仅在图片在单独一行呈现时生效.
- 类型:
literal[center, left, right]
- center: 居中对齐
- left: 左对齐
- right: 右对齐
- 类型:
- fill: 填充模式
- 类型:
literal[fit_width, fit_height, stretch, pad]
- fit_width: 匹配视图宽度或自定义的 width 参数. 按比例计算高度. (此时自定义的 height 参数会失效.)
- fit_height: 匹配视图高度或自定义的 height 参数. 按比例计算宽度. (此时自定义的 width 参数会失效.)
- stretch: 拉伸. 当只定义了 width 或 height 其中一个时, 则按照原图宽高比例拉伸至目标值.
- pad: 保持原图尺寸. (此时自定义的 width 和 height 参数会失效.)
- 默认: fit_width
- 类型:
- kwargs:
- alt_text: 可选文字. 在鼠标移动到图片上时悬浮显示的文字信息.
- caption: 在图片下方显示的图像标签信息 (居中).
-
缩写:
:img(url size 'alt_text')
- 注意: 缩写的传参格式并非按照标准指令设计. 可以有自己的写法和形式.
- 参数:
- url: 网络链接或本地路径 (绝对或相对位置).
- size: 可选. 以宽乘高来表示. 例如
800x600
,1920x1080
. - 'alt_text': 可选, 在鼠标移动到图片上时悬浮显示的文字信息. 必须以引号包裹 (单双引号均可).
-
符号:
!<url>
- url: 网络链接或本地路径 (绝对或相对位置).
-
符号位置:
- 前置
- 后置
-
示例:
[风景 :image(https://example.com/image.png, width=1920, height=1080)] [风景 :img(https://example.com/image.png 1920x1080 '喜马拉雅山')] [风景 !<https://example.com/image.png>]
-
指令:
:interpolation(插值1, 插值2, 插值3, ...)
-
符号:
$
: 创建插值模板$number
: 向模板插值- number: 编号. 如果在插值模板中没有显式定义, 则按照每个值的渲染顺序自动创建编号.
-
符号位置:
- 前置
-
说明:
插值标记是用于解决下面的情景:
当单行的标记过多时, 会导致阅读充满了 "杂讯" 干扰:
这篇文章是 [关于 [**加粗], [*斜体], [==高亮], [~~删除线] 效果的说明 <https://example.com/article.html>].
我们可以先对标记点收集起来, 然后逐个细化定义:
这篇文章是 [$ [关于 [加粗], [斜体], [高亮], [删除线] 效果的说明]] [$1 :bold] [$2 :italic] [$3 :highlight] [$4 :strike] [$5 :link(https://example.com/article.html)]
其中, 第一行
[$ ...]
是模板, 在模板下一行开始, 每行添加一个缩进, 并通过[$1 ...]
,[$2 ...]
逐个处理模板插值.上述示例中, 模板没有显式定义编号, 因此
$1
,$2
, ... 是根据渲染顺序自动产生的.您也可以显式创建编号. 编号语法如下:
可以前置:
这篇文章是 [$ [1:关于 [2:加粗], [3:斜体], [4:高亮], [5:删除线] 效果的说明]] [$1 :link(https://example.com/article.html)] [$2 :bold] [$3 :italic] [$4 :highlight] [$5 :strike]
或者后置:
这篇文章是 [$ [关于 [加粗:1], [斜体:2], [高亮:3], [删除线:4] 效果的说明:5]] [$1 :bold] [$2 :italic] [$3 :highlight] [$4 :strike] [$5 :link(https://example.com/article.html)]
-
指令:
:italic()
-
缩写:
:i
-
符号:
*
-
符号位置:
- 前置
-
示例:
这是 [*斜体] 效果.
-
指令:
:lang(language, **kwargs)
-
language: str
可以使用语言的全称或缩写. 例如 "python" 和 "py", "javascript" 和 "js".
不区分大小写.
-
kwargs:
-
show_indent_line:
bool[default true]
是否显示缩进线. (仅对代码块结构有效)
-
show_line_number:
union[bool, literal['auto']]
是否显示代码的行号. 默认值是 'auto', 由语言和当前的环境配置决定. (仅对代码块结构有效)
-
-
-
符号:
$language
-
language: str
可以使用语言的全称或缩写. 例如 "python" 和 "py", "javascript" 和 "js".
不区分大小写.
-
-
符号位置:
前置- 后置
-
示例:
这是一段 python 代码 [`print([1, 2, 3])` :lang(py)], 被标记的地方会被渲染为等宽字体并施加代码高亮. (取决于 ide 是否支持) 这是一段 python 代码 [`print([1, 2, 3])` $py], 被标记的地方会被渲染为等宽字体并施加代码高亮. (取决于 ide 是否支持)
此外, 当用于代码块 (codeblock) 时, 可以按下面的形式简写:
``` :lang(py) print('hello world') ``` ``` py print('hello world') ```
-
指令:
:list(order=false, start=1)
-
order: bool
-
start: int
该参数仅在 order 为 true, 且位于同级的第一个列表项时生效.
将它设定为大于 1 的数, 则意味着强行从该序号开始编号.
-
-
缩写:
:ol
: 有序列表:ul
: 无序列表
-
符号:
-
<number>.
: 有序列表-
number:
列表编号, 数字.
手动地, 递增地填写此编号.
考虑到列表在编辑时的变动较为频繁, 允许重复数字, 错乱编号等. 由格式化工具或者渲染程序自动校正.
-
-
=
: 有序列表的另一种写法. 以等号 (=) 开头. -
-
: 无序列表. 以减号 (-) 开头.
-
-
符号位置:
- 前置
-
示例:
有序列表:
[monday :list(order=true)] [tuesday :list(order=true)] [wednesday :list(order=true)] [monday :ol] [tuesday :ol] [wednesday :ol] 1. monday 2. tuesday 3. wednesday = monday = tuesday = wednesday
无序列表:
[concise :list()] [intuitive :list()] [straightforward :list()] [concise :ul] [intuitive :ul] [straightforward :ul] - concise - intuitive - straightforward
-
指令:
:softwrap()
-
符号:
|
-
符号位置:
- 前置
- 后置
-
说明:
注: 该符号可单用也可以与内容一同包裹. 作为符号使用时, 可用于前置, 也可用于后置.
用于前置:
monday [| tuesday] [| wednesday] here another paragraph...
用于后置:
/ 既可以单用: monday [|] tuesday [|] wednesday / 也可以与内容一同包裹: [monday |] [tuesday |] wednesday here another paragraph...
如果某行使用了软换行, 但它的上/下一行是空行, 则该软换行会被忽略. 所以下面的写法与上面的示例效果相同 (看起来会更美观):
/ 符号前置: [| monday] [/ 注意观察开头的不同之处] [| tuesday] [| wednesday] / 符号后置 -- 既可以单用: monday [|] tuesday [|] wednesday [|] [/ 注意观察结尾的不同之处] / -- 也可以与内容一同包裹: [monday |] [tuesday |] [wednesday |] [/ 注意观察结尾的不同之处]
此外, 根据 "边界省略规则", 整行使用时, 左右方括号可以省略, 所以就成了下面的写法, 这也是我们最常用的写法:
- 这是一个列表项 | 对列表项的内容软换行显示 | 对列表项的内容软换行显示 | 对列表项的内容软换行显示 这是第一段 | 这是该段的软换行内容 | 这是该段的软换行内容 | 这是该段的软换行内容 |
-
指令:
:splitter(pattern=---, fg_color=none, bg_color=none, orientation=horizontal)
-
符号:
---
长度大于等于 3.
只能用于整行.
-
符号位置:
- 前置
-
示例:
这是一个段落... --- 这是另一个段落...