作者
José Valim <jose(dot)valim(at)gmail(dot)com> , Eric Bailey , Radek Szymczyszyn
状态
最终/24.0 已在 OTP 版本 24 中实现
类型
标准跟踪
创建
2018-01-04
发布历史
https://github.com/erlef/documentation-wg/issues/3 https://github.com/erlang/otp/pull/2545 https://github.com/erlang/otp/pull/2803 https://github.com/erlef/build-and-packaging-wg/issues/25

EEP 48:文档存储和格式 #

摘要 #

此 EEP 建议 BEAM 语言使用官方 API 文档存储。通过标准化 API 文档的存储方式,可以编写跨语言工作的工具。

基本原理 #

目前,在 BEAM 上运行的不同编程语言和库都设计了自己的模式来存储和访问文档。

例如,Elixir 和 LFE 在它们的 shell 中提供了一个 h 助手,可以打印任何模块的文档。

iex> h String
A String in Elixir is a UTF-8 encoded binary.

但是,Elixir 只能显示 Elixir 模块的文档。LFE 只能显示 LFE 函数的文档,等等。如果文档标准化,这样的功能可以很容易地添加到其他语言中,并在所有 BEAM 语言中保持一致。

此外,每种语言最终都会构建自己的工具来生成、处理和转换文档。我们希望统一的文档方法能够提高工具之间的兼容性。例如,Erlang IDE 将能够显示任何模块和函数的内联文档,无论该函数是 OTP 的一部分、一个库的一部分,还是用 Elixir、LFE 或 Alpaca 编写的。

注意:在本文档中,“文档”一词仅指模块和函数的 API 文档。指南、教程和其他材料对于项目也很重要,但不是本 EEP 的重点。

注意:此 EEP 不是关于文档格式的。它是关于一种存储文档的机制,以便更容易生成其他格式。例如,一个工具可以读取文档并从中生成 man 页面。

规范 #

此 EEP 分为三个部分。第一部分定义了文档可以存储的两个位置,第二部分定义了文档的形状,第三部分讨论了与 OTP 的集成。

第 1 部分:“Docs”存储 #

BEAM 语言存储文档主要有两种机制:在文件系统中(通常在 /doc 目录中)和在 .beam 文件中。

此 EEP 承认这两种选择,并旨在支持这两种选择。要查找名为 example 的模块的文档,工具应:

  1. 在代码路径中查找 example.beam,解析 BEAM 文件并检索 Docs

  2. 如果该块不可用,它应在代码路径中查找 “example.beam”,并在定义 example 模块的应用程序中找到 doc/chunks/example.chunk 文件

  3. 如果 .chunk 文件不可用,则文档不可用

选择使用块还是文件系统完全取决于语言或库。在这两种情况下,都可以通过剥离 Docs 块或删除 doc/chunks 目录随时添加或删除文档。

例如,像 Elixir 和 LFE 这样的语言在编译时会附加 Docs 块,这可以通过编译器标志来控制。另一方面,像 OTP 本身这样的项目可能会在与代码编译完全分离的单独命令中生成 doc/chunks 条目。

第 2 部分:“Docs”格式 #

在这两种存储方式中,文档都以完全相同的格式编写:通过 term_to_binary/1 序列化为二进制的 Erlang 项。该项在序列化时可以可选地压缩,并且必须遵循以下类型规范:

{docs_v1,
 Anno :: erl_anno:anno(),
 BeamLanguage :: atom(),
 Format :: mime_type(),
 ModuleDoc :: #{optional(DocLanguage) := DocValue} | none | hidden,
 Metadata :: map(),
 Docs ::
   [{{Kind, Name, Arity},
     Anno :: erl_anno:anno(),
     Signature :: [binary()],
     Doc :: #{optional(DocLanguage) := DocValue} | none | hidden,
     Metadata :: map()
    }]} when DocLanguage :: binary(),
             DocValue :: binary() | term()

其中在根元组中,我们有:

  • Anno - 定义本身的注释(行、列、文件)(请参阅 erl_anno

  • BeamLanguage - 表示语言的原子,例如:erlangelixirlfealpaca

  • Format - 文档的 MIME 类型,例如 “text/markdown” 或 “application/erlang+html”(请参阅 FAQ 中关于此字段的讨论)

  • ModuleDoc - 一个以文档语言为键的映射,例如 <<"en">><<"pt_BR">>,文档为二进制值。如果没有任何文档,则可能为原子 none,如果此条目的文档已被明确禁用,则可能为原子 hidden

  • Metadata - 以任何项作为值的原子键的映射。这可以用来添加注释,例如模块的“作者”、“已弃用”,或者语言或文档工具可能认为相关的任何其他内容

  • Docs - 模块中其他实体(例如函数和类型)的文档列表

对于 Docs 中的每个条目,我们有:

  • {Kind, Name, Arity} - 标识函数、回调、类型等的种类、名称和参数数量。官方实体为:functiontypecallback。其他语言将添加自己的实体。例如,Elixir 和 LFE 可以添加 macro

  • Anno - 模块文档或定义本身的注释(行、列、文件)(请参阅 erl_anno)

  • Signature - 实体的签名。它是一个二进制列表。每个条目表示签名中可以与空格或换行符连接的二进制值。例如,["binary_to_atom(Binary, Encoding)", "when is_binary(Binary)"] 可以呈现为单行或两行。它仅用于展示目的

  • Doc - 一个以文档语言为键的映射,例如 <<"en">><<"pt_BR">>,文档作为值。文档可以是二进制值,也可以是任何 Erlang 项,两者都由 Format 描述。如果它是 Erlang 项,则 Format 必须是 “application/erlang+SUFFIX”,例如当文档是 HTML 文档的 Erlang 表示时,为 “application/erlang+html”。如果没有任何文档,则 Doc 也可能为原子 none,如果此条目的文档已被明确禁用,则可能为原子 hidden

  • Metadata - 以任何项作为值的原子键的映射

注意:文档映射可以为空。在这种情况下,该函数的引用已添加到文档索引中,使其有效地公开,但未编写任何文档。

这种共享格式是 EEP 的核心,因为它有效地允许了跨语言协作。

Metadata 字段的存在允许语言、工具和库向每个条目添加自定义信息。此 EEP 记录了以下元数据键:

  • authors := [binary()] - 作者的二进制列表

  • cross_references := [module() | {module(), {Kind, Name, Arity}}] - 在生成文档时可以用作交叉引用的模块或模块条目的列表

  • deprecated := binary() - 如果存在,则表示当前条目已弃用,并带有表示弃用原因和替换已弃用代码的建议的二进制值

  • since := binary() - 表示添加此条目的版本的二进制值,例如 <<"1.3.0">><<"20.0">>

  • edit_url := binary() - 表示更改文档本身的 URL 的二进制值

可以随时将任何键添加到元数据。社区经常使用的键可以在以后的版本中标准化。

第 3 部分:与 OTP 的集成 #

最后一部分侧重于将前面几部分与 OTP 文档、工具和工作流程集成。以下项目是建议,对于 OTP 或任何其他语言或库采用此 EEP 来说都不是必需的。

此时,我们应该考虑对 OTP 进行更改,例如:

  • doc/chunks/*.chunk 文件作为 OTP 的一部分分发,并更改随 OTP 提供的工具以依赖它们。例如,可以更改 erl -man lists 来查找 lists.chunk 文件,解析出文档,然后将其动态转换为 man 页面。此任务可能需要多次更改,因为 OTP 将文档存储在 XML 文件中以及直接存储在源代码中。edoc 本身可能应该使用从源代码中吐出 .chunk 文件的功能进行增强

  • 向 Erlang 的 shell 添加 h(Module)h(Module, Function, Arity) 和类似的功能,以打印模块或给定函数和参数数量的文档。这应该能够打印任何其他实现此提案的库或语言的文档

常见问题解答 #

问:为什么我们的文档中有一个 Format 条目?

该提案中的主要权衡是文档格式。我们有两种选择:

  • 允许每种语言/库/工具选择自己的文档格式
  • 对所有语言施加统一的文档格式

统一的文档格式在选择如何编写文档方面没有给语言和库提供灵活性。随着生态系统变得更加多样化,不太可能找到适合所有人的格式。因此,我们引入了一个 Format 字段,允许每种语言和库选择其文档格式。缺点是,如果 Elixir 文档是用 Markdown 编写的,而一种语言不知道如何格式化 Markdown,那么该语言将不得不选择不显示 Elixir 文档或以原始方式(即 Markdown 格式)显示它们。

Erlang 处于特权地位。所有语言都将能够支持为 Erlang 选择的任何格式,因为所有语言都在 Erlang 上运行,并且可以直接访问 Erlang 的工具。

问:如果我有一个使用自定义文档工具包的 Erlang/Elixir/LFE/Alpaca 库,我也能利用它吗?

只要文档最终出现在 Docs 块中或 doc/chunks 目录中,我们绝对不在乎文档最初是如何编写的。如果您使用自定义格式,您可能需要教您选择的语言如何呈现它。请参阅上一个问题。

版权 #

本文档已置于公共领域。