WackoWiki Markup

其他语言 Deutsch, Español and Русский.


这篇比较长的文章介绍了“Wacko 标记”是如何出现的,以及为什么它现在是这个样子的。如果你不知道什么是“Wacko 标记”,尤其是不清楚什么是“wiki”,你应该先到 WackoWiki 项目站点阅读几篇 documentation 的文章,特别是关于 wiki-concept 的那篇文章。



1. 关于 Wacko 标记

简而言之,Wacko 标记是一种特殊的文本格式化方式,它只需最少的特殊字符并尽量不破坏“源文本”的可读性,就能在程序转换后得到漂亮的 HTML。


Wacko 标记的功能足以对大大小小的页面进行完整的排版。除了传统的“斜体”“粗体”“下划线”和“删除线”等修饰外,还有列表(包括多级列表)、标题、表格、图片、引用高亮等多种标记方式。


Wacko 标记用于 WackoWiki 引擎以及其他开发者的项目中。若要将 Wacko 标记嵌入你的项目,只需下载并集成以下组件:

  • Wacko 格式化器,一个将 Wacko 标记转换为 HTML 的 PHP 类;
  • 可视化编辑器,用于 wiki 的编辑器。该项为可选,但与 Wacko 格式化器结合使用非常方便。

本文的源文本采用 Wacko 标记格式撰写,可供查看

2. 基本原则

尽管(我们是 WackoWiki 的作者、开发者)最初从 WakkaWiki 项目继承了 Wacko 标记(该项目此前位于 http://wakkawiki.de,现在已不可用),但我们是基于一组清晰的原则对其进行了发展,这些原则决定了它日后的成功。


因此,标记规则的形成虽然是“进化式”的,但始终由少量原则引导,使得标记保持便捷与自然。


这些原则旨在实现以下目标:

  • 降低“意外触发”的概率(即普通文本被误识别为标记);
  • 实现直观且易于记忆的标记;
  • 保护被格式化文本的环境,减少作者误输入造成的问题。

2.1. 语义应通过语法表达

任何 wiki 标记都基于“所想即所得”(WYTIWYG)的原则,因此每条 Wacko 标记规则都试图用自身的语法反映语义。由于整个排版是通过可在键盘上输入的简单文本符号完成的,我们尝试找到能够反映含义的组合:

  • **bold** —— 用星号表示粗体,显得与周围文本“分离”;
  • //italics// —— 斜体由 // 这种“编织线”表示;
  • --strikethrough--__underlining__ 分别表示删除线和下划线;
  • 列表的书写方式与我们在文本输入域中习惯写列表时很相似。

在这里,我们有意不同于 ((http://c2.com/cgi/wiki?TextFormattingExamples) 经典) 的那种“每侧仅用 5 个撇号”式的 wiki 标记。

2.2. 成对字符

如你所见,上述所有实例都包含成对出现的字符。得益于这一原则,从其他编辑器“直接粘贴”的文本通常不需要特殊预处理——这样的文本很少会恰好包含成对字符。


某些情形下会使用三连、四连乃至五六连字符来表示不同级别的标题。字符越多,标题层级越深。

2.3. 标记与内部文本接触

你可能是那种在键入破折号时总用“两个减号”写法(像这样:--)的人。无论如何,作者显然也属于这一类。而大量文本中既作为破折号又作为其他用途出现的双减号并不少见。


“标记应与内部文本接触”的原则可避免将“普通文本”误转换为标记。并非所有标记规则都采用了这一原则,但对那些单个成对字符在“源代码”中有可能出现的标记(例如删除线)尤其重要。


例如:--which is how it works-- 会产生删除线,而如果写成 -- we do so-- —— 文本就不会被识别为删除线。


需要指出的是,本规则与前后规则的目标均是尽量减少标记处理的“意外触发”,即避免文本中的某些字符被误认为是标记的一部分。

2.4. 换行表示文本片段结束

与 OpenWiki 标记不同,Wacko 标记中“源代码”的换行被字面理解为换行(两个换行表示新段落)。因此,我们将换行视为某个完结文本片段(如一句话或一段)的结束。


一些 Wacko 标记规则(如改变字体样式的规则:粗体、斜体、下划线等,以及标题)大多数仅作用于被换行限制的文本片段。采用该原则可以同时解决三类问题:

  • 降低“意外触发”的概率;
  • 简化对标记的理解(因为选区的“开始”和“结束”相距不远);
  • 降低“字体样式”在长文本中的作用,使其主要用于小范围文本格式化——这有助于提升最终效果的可读性。

2.5. “损坏”的标记不应破坏周围的 HTML

上述原则使我们能解决一个很重要的问题——“保护”格式化文本周围的 HTML 不被可能的格式错误破坏。凭借“粘性”规则,我们几乎总能判定标记的“开始”和“结束”位置,从而一次性插入两个 HTML 标签(或根本不插入)。对于那些不易成对的 Wacko 标记规则,应尽力保证最终结果的“紧凑性”。事实证明,我们做到了这一点。

2.6. 必须能准确写出我想写的内容

任何标记都应能“转义”,即让标记规则被忽略、文本片段原样通过格式化。这篇文章就是需要这种“转义”的典型例子。

2.7. 尽量减少键盘布局切换

最后但同样重要的是,对于讲俄语的用户来说,这一原则在便利性上尤其关键(对英语用户也要兼顾)。


原则要点是选择那些大多数(尤其是高频)规则可以在俄语输入布局下直接输入的字符组合,同时也要考虑对英语读者的可访问性。


因此,原始的引用语法 [[WikiLink Textual description]] 找到了对应的替代写法 ((WikiLink Text on link)),可在英文和俄文输入布局下使用。许多 wiki 标记规则都具备这一有益特性。

3. 标记规则

在应用这些原则后,我们开发并改进了原有规则集,仔细研究了不同的 wiki 引擎。如果敢自夸,我会说最终的 wiki 标记融合了各类优秀规则,同时保持精简与简单。实际上,经过我们的努力,标记确实吸收了众多优秀规则,仍保持简洁。


本章简要说明我们如何确立若干关键标记规则。

3.1. 单行标记

单行标记指那些使用在“普通文本”中常见符号、并用于文本局部排版的规则:

  • 粗体斜体下划线
  • 小号等宽 文本
  • 标题(与换行分割相关)
  • 链接(其文本通常也应保持“简短”)
  • 度数上标/下标(仅在单词内有效)

3.2. 多行标记

其余标记规则作用于较长的文本片段,通常因为它们的语义和表现形式应用于大段文本。


It's, like, a strikethrough.
Or
a quote that captures the entire paragraph.

3.3. 标记取消

标记取消有两种实现方式:

  • 用“成对字符”进行块级取消 ""
  • 用转义符(在被取消序列前输入特殊字符)即波浪号 ~

第一种方法便于对大段文本禁用标记,第二种方法在你只想禁用一两个词或一个成对字符时更方便。此外,波浪号转义还能用于显示双引号,如上文示例所示。


如果你打开本篇文章的 !/source,会发现标记取消非常常见——否则我们就无法展示未经处理的标记示例了。

3.4. 引用与图片

Wacko 标记会自动将文本中的 URL 转为超链接。如果可以明显判断某 URL 是图片(例如以 .jpg 或 .gif 结尾)——则使用 <img> 标签将图片插入到格式化后的文本中。这很方便,允许你直接从剪贴板插入链接并获得不错的结果。


对于带说明的链接有两种常见写法:

  • ((URL 说明文本))
  • [[URL 说明文本]] —— 功能相同,但不够方便
  • ((Wiki Reference With Spaces == 说明文本)) —— 允许在 wiki 链接的“左侧”包含空格(方括号写法也支持此功能)

Wiki Link Concept 使得插入站内链接更为简便,Wacko 标记也考虑到了这一点。所有非“站内”链接会被立即识别为“外部”链接,电子邮件地址亦同。


图片说明的写法与链接说明类似:

  • ((picture.jpg 说明)) 用于设置图片标题
  • ((URL small_picture.jpg)) 用于将图片作为链接插入

3.5. 列表结构

人们最自然地如何书写无序、有序以及嵌套列表?
**可能像这样:**
  * 列表项-1
  * 列表项-2
  * 子列表
    * 子列表项-1
    * 子列表项-2
  * 列表继续
  
  1. 有序列表第一项
  2. 第二项
    1. 嵌套列表项
    6. 编号可能不连续


你期望得到:

  • 列表项-1
  • 列表项-2
  • 子列表
    • 子列表项-1
    • 子列表项-2
  • 列表继续

  1. 有序列表第一项
  2. 第二项
    1. 嵌套列表项
    2. 编号可能不连续

这就是 Wacko 标记中列表规则的组织方式。该示例很好地说明了为什么列表项的起始处要向右缩进两个字符——这样可以避免与 粗体 文本混淆。


缩进选择为两个字符,基于“成对字符”原则,这种字符在普通文本中几乎不会出现。其他一些 wiki 格式使用五到六个空格——你可以想象在 <textarea> 中第三级嵌套会是什么样子!


c2.com 的原始做法使用制表符,但我们认为这不合理:在大多数界面中 Tab 用于导航,有些浏览器根本无法在输入域中输入制表符。


有序列表的编号会自动生成,这使得删除项目、添加新项或改变层级变得容易。如果需要一个从数字三开始的有序列表,可以这样写:1.#3 Like this

  1. Like this.

3.6. 表格标记

表格是任何标记中最难处理的部分之一。最好的方案是一组模拟表格竖线网格的规则。这样比用连字符、加号和竖线绘制整个网格要简单,而且源代码仍然可读。关于表格标记的更多信息请参见 Wacko markup syntax,其中有示例。


遗憾的是,表格标记是 Wacko 标记中最“脆弱”的部分,在某些情况下可能破坏页面外部环境的结构。这是我们为语法清晰和解析速度所愿意付出的代价。

4. 标记的发展

首先,上文并未描述所有标记规则,仅列出最重要的。如有遗漏,请先查阅文档 ((/Doc/English/Formatting) 以获取更完整的说明)。如果现有规则不足以满足需求,则应扩展标记。大多数我们新增的规则正是如此产生的。

4.1. 何时应更改现有标记?

永远不要。


这里指的是在不引入新规则的前提下修改已有规则。更改已有标记的行为可能会严重打扰已经习惯该标记的人们。试想如果某些站点上 <b> 被当作 <a> 使用,反之亦然,那将多混乱。


当然,如果某个项目内的某条规则确实极大地妨碍了你的工作,你可以考虑替换它。但在替换/移除规则时,要考虑熟悉标准标记的所有用户将如何接受这些变动!

4.2. 何时值得引入新的标记实体?

尽量少引入。


Wacko 标记有“高亮器(highlighters)”的概念,它们是可插件式的实体,可以很容易地添加而不占用“主标记空间”——即成对字符。下文将描述高亮器的概念。


若未经充分考虑就新增成对字符,会让用户不得不重新记忆这些变化。正因为开发者对每条新规则的后果深思熟虑,用户才能无障碍地使用 Wacko 标记。


若你想扩展标记,最好联系该格式的作者——他们可能已有想法,或愿意采纳你的建议。协调一致的行动总是更有效。

5. 复杂转换与服务功能

Wacko 标记的设计思想(源自 WakkaWiki 并由作者扩展)提供了两种方式,可以显著增强标记能力并在页面中嵌入复杂功能。


这两种方式是我们主要推荐给希望扩展标记规则的开发者的方式。此外,使用这些方法不需要深入解析 Wacko 格式化器的内部实现。

5.1. 高亮器的概念

“高亮器”是一个允许开发者实现复杂文本块转换逻辑的概念,并让用户方便地将该文本块以特定格式呈现的机制。


在 Wacko 标记正文中,高亮器的使用形式如 %%(html)<div class="test">testdiv</div>...%% —— 即使用成对字符 %%,并在括号中指明将要对高亮块内容调用哪个高亮函数。


开发者编写这些函数,函数接收一个文本变量,对其进行某些转换,然后返回结果。


通常此语法用于各种文本“着色”——用于语法高亮、聊天记录或电子邮件的标记、插入某些特殊文本块等。


该概念既是扩展标记的简单方式,也是:

  • 无需使用额外“成对字符”的方式;
  • 无需了解格式化器细节的方式。

5.2. Actions(动作)的概念

“动作”是一个允许开发者实现复杂功能(可能需要与用户交互),并允许用户在页面中合适位置便捷调用该功能的概念。


在 Wacko 标记正文中,“动作”写法如 {{changes page="/Dev"}} —— 使用成对字符 {{,随后指定动作名及附加参数。


开发者以与编写高亮器相同的方式编写这些函数。


在 WackoWiki 中,借助“动作”实现了大量与页面交互的功能(构建目录、列出最近更改/评论、搜索等)。


该概念允许在页面中“嵌入”复杂功能,而无需深入了解 Wacko 格式化器的实现细节。

5.3. Interwiki — 链接标记的扩展

还可以通过添加类似 ((repository:address))((repository:address 链接文本)) 的规则来扩展链接语法。在此类写法中,地址由两部分组成:“存储”与“该存储中的名称”。例如,指向 LiveJournal 的地址可写为 ((lj:mendokusee)),若要指向英文单词的翻译,可写 ((lingvo:fascinating))


此概念(以及我们许多标记)继承自 wiki,最初用于指向其他 wiki 节点。我们发现它对其他用途也很有用。


当然,还有其它实现类似功能的方法。不同站点采用过下列标记格式:


带冒号的 interwiki 变体(repository:address)在许多系统中被使用,已成事实上的标准,因此我们认为它更为正确。此外,interwiki 不会与常见的 [[Link Description]] 产生冲突。

6. 技术细节

本文不是详细介绍 Wacko 格式化器 工作原理的场所,但有几点主要内容值得提及。


首先,格式化器本身基于正则表达式(即兼容 Perl 的正则表达式)并采用“递归解析”原则。也就是说,它尝试将文本分解为“最大”的标记片段,然后对这些片段内部再次进行解析。这也意味着你不能嵌套使用相同的标记规则:例如,像这样 **example **nested** markup** 的写法无法工作。但你也会同意,这种嵌套通常并非必要。


其次,格式化器为更好地防止“意外触发”而分多轮处理。第一轮是一组简化规则,用于“转义”文本片段,禁止对其进行标记。在同一轮中,所有“高亮器”会被识别并保护,从而防止高亮器与其他标记相互重叠。第二轮则处理其余规则。


第三,格式化器支持对格式化结果进行缓存。由于某些标记需与用户交互或链接到页面(不同用户可能有不同处理方式),因此缓存时会生成一个只包含链接与“动作”等相关规则的“半成品”结果。最后一轮格式化是最快的一次,不需要递归,在显示页面内容时基于该“半成品”完成链接标记和插入“动作”。


若你对格式化更深层次的细节感兴趣,欢迎查阅 代码格式

7. 注意事项

本文旨在说明 Wacko 标记规则为何以及如何被发明,为什么会采用“成对字符”,并希望使用 Wacko 标记编辑格式的新项目能遵循其背后的原则。


如有疑问,欢迎在评论中提出或直接联系本文作者。


此致敬意,WackoWiki 团队 及本文作者敬上。

8. 参考资料

1. WackoWiki 项目
2. Formatter 有关所述标记的说明
3. Visual Editor 有关所述标记的说明
2. WakkaWiki —— WackoWiki 的历史渊源
3. OpenWiki —— Wacko 标记语法的另一个历史渊源
4. Description of wiki concepts
5. Wacko-Markup Syntax
8. 经典标记示例 (载于 c2.com)
9. WikiName —— 维基链接的概念