本文档汇集了一系列在 AsciiDoc 中撰写文档的推荐实践。
AsciiDoc 提供了多种实现相同目标的方式,并且语法通常非常宽松。本指南有助于使您的文档保持一致,从而最大化效率、提高可读性并最小化维护成本。
文档扩展名
AsciiDoc 不关心您使用哪种扩展名。GitHub 支持 .asciidoc、.adoc 和 .asc 这几种扩展名。
.adoc 似乎是最常用的扩展名,也是我们偏好的。它不与其他知名应用程序冲突,易于区分,且不会过长(不像 .asciidoc)。它读起来也像“a doc”。因此,我们建议使用 .adoc。
我们强烈建议不要使用 .asc 扩展名。它与 AsciiDoc 的关联性不明显,并且在 Linux 上会被误识别为 PGP 文件。 |
每行一个句子
不要按固定列宽换行。相反,将每个句子放在单独的一行上,这种技术称为每行一个句子。这种技术类似于您编写和组织源代码的方式。结果可能非常出色。
使用每行一个句子风格的一些优点如下:
-
它可防止重排(意味着段落开头的一个改动不会导致段落其余行重新定位)。
-
您可以轻松地交换句子。
-
您可以轻松地分离或合并段落。
-
您可以注释掉句子或对其添加评论。
-
您可以发现过长的句子或长度差异很大的句子。
-
您可以发现写作中重复(因此平凡)的模式。
我们从 Neo4j 文档 的写作指南中获得了这个想法。然而,这个想法似乎可以追溯到 Buckminster Fuller 在 20 世纪 30 年代的发现,他称之为 通风散文。Brandon Rhodes 在 2009 年一篇关于 语义换行符 的博文中也推荐了这种技术。
需要注意的是,这种技术之所以有效,是因为 AsciiDoc 不会将文本中的换行符视为硬换行符。至少,读者看到的不是这样。连续的文本行之间的换行符在渲染文档(即读者看到的)中是不可见的。
| 虽然单个换行符在输出中不显示,但两个连续的换行符会开始一个新段落(或其他块)。 |
章节标题
有三种方式可以定义章节标题。这些变体可以分为两种风格:atx 和 setext(历史名称)。我们推荐使用不对称的 Atx 风格来定义您的章节标题。
在 Atx 风格中,章节标题定义在单行上。章节标题以一个或多个等号(即 =)开头,后跟一个空格和章节标题。前导字符的数量代表了深度。顶级章节保留给文档标题,因此文档中的第一个章节将有两个前导字符。
== First Section
此示例使用不对称的 Atx 风格。它需要的字符数最少,并且直观,因为前导字符的数量代表了标题级别。
当使用 HTML 后端时,此章节标题将转换为 h2 标题(可能包装在一个 section 块中用于样式化)。
<h2>First Section</h2>
对称的 Atx 风格用一对匹配的定界符将章节标题括起来。
== First Section ==
虽然这种语法对某些人来说可能看起来更好看,但它需要两倍的定界符而没有增加任何额外的语义。因此,我认为这是浪费按键次数,不建议使用。然而,任何一种 Atx 风格的章节标题都比 Setext 风格好。
在 Setext 风格中,章节标题定义在两行上。第二行是一系列字符,在章节标题下方形成下划线。下面是用 Setext 风格定义的章节标题示例。
First Section -------------
由于下划线行的长度取决于章节标题中字符的数量,因此必须引入另一个维度来确定级别。该维度是用于下划线的字符。这些字符(按文档标题到 5 级章节排序)是 =、-、~、^ 和 +。
我强烈建议不要使用 Setext 风格作为章节标题。保持下划线长度与标题长度同步是一项不必要的任务,浪费时间。更实质性的原因是,字符与标题级别的映射非常难以记住,并且对新的 AsciiDoc 采用者来说不明显。总而言之,使用 Setext 章节标题是一种糟糕的做法。不要使用它们!
| 建议使用不对称的 Atx 风格作为章节标题。它是撰写和在纯文本(源)文件中阅读最简单直观的方法。 |
文档标题
由于文档标题在一个文档中只出现一次,我(尽管强烈不建议)认为在这种情况下使用 Setext 风格是可以接受的,只要您同时禁用 compat-mode 属性(否则,它会将处理器置于兼容模式)。
Document Title ============== Author Name <author@example.org> :compat-mode!:
如果您不希望启用兼容模式,在此情况下您必须显式禁用它(即 compat-mode!)。当使用 Setext 风格的文档标题时,兼容模式会自动启用。有关详细信息,请参阅迁移指南。 |
请务必在文档标题后立即包含您的姓名。
| AsciiDoc 只支持作者行后的电子邮件。它不支持个人 URL。Asciidoctor 支持原始 URL 以及内联链接宏。 |
在作者行后也包含修订行是一个好习惯。
Document Title ============== Author Name <author@example.org> v1.0, 2012-02-10 :compat-mode!:
版本号是可选的。修订行可以只包含日期。
Document Title ============== Author Name <author@example.org> 2012-02-10 :compat-mode!:
分隔块
分隔块包含特殊文本,例如代码列表、引语、侧边栏文本、表格等。正如您可能猜到的,它们被一串定界符包围。定界符定义在单独的行上。内容放在定界符行之间。这是一个列表示例:
---- $ asciidoctor -b html5 recommended-practices.adoc ----
分隔块需要在单独的行上使用四个或更多重复字符来标记块的边界。唯一的例外是开放块,它只需要两个重复的 - 字符。
您可能会尝试将行进一步延长,要么到预定长度,要么匹配内容的长度。
------------------------------------------------- $ asciidoctor -b html5 recommended-practices.adoc -------------------------------------------------
不要这样做!
维护长定界符行是一项巨大的时间浪费,更不用说任意和容易出错了。我强烈敦促您使用形成分隔块所需的最少字符数,然后继续撰写内容。读者永远不会看到这些长字符串的定界符,因为它们不会被带到输出(HTML、DocBook 等)中。
| AsciiDoc 不强制要求打开分隔块的行的长度与关闭分隔块的行的长度相匹配,但我认为应该这样做。Asciidoctor 强制执行此要求,因此请确保它们匹配! |
文档属性(即变量)
通过利用文档属性,您可以节省大量打字时间。文档属性将频繁出现的引用和短语提升到文档(或章节)的顶部。您可以通过将信息分配给一个属性来声明一次,然后在整个文档中引用该属性。从这个意义上说,您可以将属性视为 AsciiDoc 的变量。
将信息存储在您在文档顶部声明的属性中,可以方便查找信息,并避免您在多个地方更新信息(即保持文档 DRY)。当您更新属性值时,该属性在文档中引用的所有地方的值都会改变。
DRY URL
属性的一个常见用途是存储 URL。URL 经常变化,可能相当长,并且通常包含需要转义的特殊字符。通过在属性中声明 URL 可以解决所有这些挑战。
您只需将 URL 分配给一个简短易记的属性:
:url-issues: https://github.com/asciidoctor/asciidoctor/issues
然后,您在文档中想要使用 URL 的任何地方都可以引用此属性,例如创建链接:
Submit bug fixes and feature requests to the {url-issues}[issue tracker].
由于您可能定义了许多不同的属性,因此最好采用一个系统来保持它们的组织性。让我们研究一个这样的系统。
属性组
如果您定义了许多文档属性,文档头部可能会变得混乱。为了保持整洁,最好
-
使用通用前缀命名相关属性(即给它们命名空间)
-
在横幅下方对相关属性进行分组
例如,我们建议在声明包含 URL 值的属性时,使用前缀 url- 或 uri-。这是出于几个原因:
-
它传达了属性值的类型(例如,
url-issues是用于问题跟踪器的 URL)。 -
它避免了与其他用于其他目的的属性发生冲突(例如,使用
org作为组织 URL 可能会与用于组织名称的org冲突)。 -
它会导致编辑器中的代码辅助工具(如文本自动完成)自然地将相关属性分组(例如,通过键入
url-来调出所有 URL 属性的列表)。
对相关属性进行分组可以轻松地看到新属性应该在哪里添加,从而帮助保持文档头部的整洁。它还为将来将组重新组织到单独的文件中打开了大门。在组内,您可能考虑对属性进行字母排序。此规则的一个例外是,如果一个属性引用了另一个属性,则必须先定义被引用的属性。
以下是我们建议用于对文档头部相关属性进行分组的一些横幅:
-
元数据 - 定义输出文档头部用于索引的信息的属性。
-
设置 - 控制内置转换和格式化行为的属性。
-
引用 - 在内容中引用的属性,例如 URL;用其类型的前缀命名每个属性,以便自动完成效果更好,例如
url-表示 URL。
您可以使用任何您觉得自然的附加组。
将这些建议结合起来的效果如下:
= Document Title
Author Name
// Metadata:
:description: The description of this page.
:keywords: writing, documentation, publishing
// Settings:
:icons: font
:idprefix:
:idseparator: -
// Refs:
:url-project: https://asciidoctor.org.cn
:url-docs: {url-project}/docs
:url-issues: https://github.com/asciidoctor/asciidoctor
:img-ci: https://github.com/asciidoctor/asciidoctor/workflows/CI/badge.svg
文档设置
大多数文档设置使用属性控制。文档设置在文档标题后面(中间没有任何空行)使用属性条目进行配置。有几个选项值得关注。
您可以使用 sectnums 属性启用章节编号(默认关闭)。
:sectnums:
您可以使用 description 属性设置文档的描述。该描述包含在文档的头部。
:description: This document catalogs a set of recommended practices for writing in AsciiDoc.
您可以通过以 \ 加上空格结束行来将任何 属性值拆分到多行。
:description: This document catalogs a set of recommended practices \
for composing documents in AsciiDoc.
您可以通过将其作为属性引用在文档中的任何位置使用此文本。
{description}
默认情况下,每个章节标题都会生成 ID。ID 由章节标题生成,默认情况下前面带有下划线(即 _)。您可以使用 idprefix 属性更改前缀。
:idprefix: id_
如果您想删除前缀,请将其赋值为空值:
:idprefix:
要禁用章节 ID 的自动生成,请取消设置 sectids 属性:
:sectids!:
设置 toc 属性可在文档顶部激活自动生成的目录。
:toc:
列表
AsciiDoc 支持 *(一个或多个)和 -(仅一个)作为顶级列表项的标记。
* first * second * third
或
- first - second - third
但是,在定义列表项时,连字符标记不能重复。这可能导致混淆,因为 AsciiDoc 在每次遇到不同标记时都会增加嵌套级别。例如,在以下情况下,带有星号标记的项嵌套在第一项中。
- first * nested item - second - third
当星号的数量似乎表明级别时,此嵌套规则仍然适用。
*** first * nested item *** second *** third
是的,没错,第二项列表项嵌套在第一项列表项中。
如果您遵循约定,星号的数量可以表示嵌套级别。
* first ** nested item * second * third
现在这样就很直观了。
如果您要使用嵌套列表,我强烈建议使用星号标记。
如果您只有顶级列表项,那么使用任一标记都是合理的。我甚至可能建议将连字符标记用于不打算包含嵌套项的列表,将星号标记用于确实包含嵌套项的列表。这样,它们很容易被识别为不同类型。
它们存在!
相邻的列表有时会融合。要强制开始一个新列表,请通过一个空行注释来偏移这两个列表:
* apples * oranges * bananas //- * carrots * tomatoes * celery