AsciiDoc 推荐实践

本文档汇集了一系列在 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 不会将文本中的换行符视为硬换行符。至少,读者看到的不是这样。连续的文本行之间的换行符在渲染文档(即读者看到的)中是不可见的。

虽然单个换行符在输出中不显示,但两个连续的换行符会开始一个新段落(或其他块)。

章节标题

有三种方式可以定义章节标题。这些变体可以分为两种风格:atxsetext(历史名称)。我们推荐使用不对称的 Atx 风格来定义您的章节标题。

Atx 风格

在 Atx 风格中,章节标题定义在单行上。章节标题以一个或多个等号(即 =)开头,后跟一个空格和章节标题。前导字符的数量代表了深度。顶级章节保留给文档标题,因此文档中的第一个章节将有两个前导字符。

== First Section

此示例使用不对称的 Atx 风格。它需要的字符数最少,并且直观,因为前导字符的数量代表了标题级别。

当使用 HTML 后端时,此章节标题将转换为 h2 标题(可能包装在一个 section 块中用于样式化)。

<h2>First Section</h2>

对称的 Atx 风格用一对匹配的定界符将章节标题括起来。

== First Section ==

虽然这种语法对某些人来说可能看起来更好看,但它需要两倍的定界符而没有增加任何额外的语义。因此,我认为这是浪费按键次数,不建议使用。然而,任何一种 Atx 风格的章节标题都比 Setext 风格好。

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-。这是出于几个原因:

  1. 它传达了属性值的类型(例如,url-issues 是用于问题跟踪器的 URL)。

  2. 它避免了与其他用于其他目的的属性发生冲突(例如,使用 org 作为组织 URL 可能会与用于组织名称的 org 冲突)。

  3. 它会导致编辑器中的代码辅助工具(如文本自动完成)自然地将相关属性分组(例如,通过键入 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

计数器

计数器由文档属性支持。由于它们与其他所有文档属性共享相同的全局命名空间,因此为计数器名称加上前缀以避免与其他属性冲突是一个好主意。例如:

{counter:cnt-step}

文档设置

大多数文档设置使用属性控制。文档设置在文档标题后面(中间没有任何空行)使用属性条目进行配置。有几个选项值得关注。

章节编号

您可以使用 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 前缀

默认情况下,每个章节标题都会生成 ID。ID 由章节标题生成,默认情况下前面带有下划线(即 _)。您可以使用 idprefix 属性更改前缀。

:idprefix: id_

如果您想删除前缀,请将其赋值为空值:

:idprefix:

要禁用章节 ID 的自动生成,请取消设置 sectids 属性:

:sectids!:
目录

设置 toc 属性可在文档顶部激活自动生成的目录。

:toc:

图像和其他媒体

待办

路径

不要在每个图像引用中包含 images 目录。

块与内联

...

条件包含

待办

如何使用,使用原因

列表

无序列表标记

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

字面文本

待办

反引号与加号和直通内容

关于行内代码引用字符的建议

表格

堆叠单元格

利用它们,易于阅读。