此 EEP 提议为 Erlang 提供一个结构化的文档 API,其中文档作为语言解析器的一部分进行处理,并直接包含在编译后的 .beam 文件中,以替代 EDoc 样式的注释。Python、Elixir 和 Clojure 是将文档视为数据而不是代码注释的语言示例。
目前 EDoc 的主要限制在于文档保留为代码注释。这需要一个显式工具来解析这些代码注释,从而使 IDE、shell 等访问文档变得复杂。最近,通过使 EDoc 编译为 EEP 48,在这方面取得了一些进展,但这仍然需要一个显式的步骤。
此外,“代码注释”方法在实现方面更加复杂,因为它需要解析源代码以及代码注释,解析代码注释等等。在文档和代码注释之间进行明确区分也是有益的:它们有不同的要求和不同的受众。
此 EEP 提议向 Erlang 添加两个模块属性:-doc
和 -moduledoc
。
与 EEP 48 一样,此提案仅涉及 API 参考及其文档。它不涵盖指南、教程和其他文档格式。
此 EEP 提议两个新属性:-doc
和 -moduledoc
。它们可以按如下方式使用
-module(base64).
-moduledoc "
Convenience functions for encoding and decoding from base64.
".
-doc "
Encodes the given binary to base64.
".
-spec encode(binary()) -> binary().
encode(Binary) ->
% ....
-doc "
Decodes the given binary from base64.
".
-spec decode(binary()) -> {ok, binary()} | error.
encode(Binary) ->
% ....
新的 -moduledoc
属性可以列在任何位置,它将包含给定模块的文档。-doc
属性必须列在函数、类型属性或回调属性之前的任何位置,它将包含以下函数、类型或回调的文档。例如,下面的示例
-doc "Example".
-spec example() -> ok.
example() -> ok.
等效于
-spec example() -> ok.
-doc "Example".
example() -> ok.
为同一函数列出多个带有字符串值的 -doc
属性应该会发出警告或错误,除非文档被设置为隐藏。例如,这是有效的
-doc "Example".
-doc hidden.
example() -> ok.
但这应该发出警告/错误
-doc "Example".
-doc "Updated example".
example() -> ok.
这也是如此
-doc "Example".
example(one) -> 1;
-doc "Updated example".
example(two) -> 2;
模块属性必须是字符串或原子 false
。将模块标记为隐藏意味着它不会成为文档的一部分。例如,假设上面的 base64
模块将其部分逻辑委托给私有的 base64_impl
模块
-module(base64_impl).
-moduledoc false.
请注意,模块可能被隐藏,但仍然可以记录单个函数
-module(base64_impl).
-moduledoc false.
-doc "
Some comments as if it was public.
".
decode64(Binary) ->
% ...
根据 EEP 48,这是故意的。例如,base64_impl
对于 base64
功能的用户应该是私有的,但是直接在 base64
上工作的开发人员可能仍然希望直接从其 IDE 访问 base64_impl
函数的文档。每个文档工具都应相应地遵守 hidden
。如果没有提供 -doc
,则根据 EEP 48,它默认为 none
。
-doc
属性也接受 false
原子。
一些开发人员倾向于不将文档与源代码放在一起。对于这种情况,-doc
和 -moduledoc
还可以提供 {file, Path}
,其中 Path
是包含 doc 属性的文件的相对路径
-moduledoc({file, "../doc/src/manual/my_module.asciidoc"}).
-doc({file, "../doc/src/manual/my_module.my_function.asciidoc"}).
编译器将在编译时读取该文件并将其嵌入到块中。
-doc
属性也可用于记录类型和回调。
-doc
属性也可以用于私有函数,以便工具和 IDE 可以在用户需要时提供文档。但是,私有函数不应出现在 EEP 48 文档块中。
通常,私有类型的处理方式与私有函数相同,但是有时类型用于模块内的文档和代码共享目的,但用户不希望将其导出以供通用使用。一个例子是 ssl 模块中的所有各种选项类型。
因此,公共函数规范或类型引用的任何私有类型也将包含在文档块中。此类类型的 exported
元数据键将设置为 false
。
新的模块属性还必须通过传递一个 map 作为参数来支持文档元数据
-module(beam64).
-moduledoc "
Convenience functions for encoding and decoding from base64.
".
-moduledoc #{
author => [<<"The Erlang/OTP team">>],
license => <<"Apache 2 License">>,
cross_references => [binary]
}.
如果多次使用 map 调用 -moduledoc
,则将合并这些 map。这带来了一个额外的好处,即可以将共享元数据移动到头文件中
%% prelude.hrl
-moduledoc #{
authors => [<<"The Erlang/OTP team">>],
license => <<"Apache 2 License">>
}.
然后我们可以包含并增强它
-module(beam64).
-include("prelude.hrl").
-moduledoc "
Convenience functions for encoding and decoding from base64.
".
-moduledoc #{cross_references => [binary]}.
可在 EEP 48 上找到内置属性的列表。
使用 -moduledoc
或 -doc
属性编译模块将在其 .beam 文件中生成一个 Docs 块,从而可以在 shell 中直接访问该文档。
默认情况下,发布工具也应从 .beam 文件中修剪掉文档块。请注意,beam_lib:strip_release/1
和 beam_lib:strip_files/1
已经完成了此操作。
warn_missing_doc
#如果包含在 EEP 48 文档块中的函数、类型或回调没有设置 -doc
属性,则编译器将发出警告,前提是将 warn_missing_doc
标志传递给编译器。
该标志既可以在命令行上作为 +warn_missing_doc
传递,也可以作为源代码的 -compile(warn_missing_doc)
属性传递。
关于文档的一个重要讨论是文档应采用哪种文档格式。幸运的是,EEP 48 对格式不可知,但是仍然必须列出一种格式。
为了方便多种文档格式,Erlang/OTP 允许用户在 -moduledoc
元数据中放置一个 format
键,该键应指定所用格式的 mime 类型,如 EEP 48 所指定。默认格式将为 text/markdown
。
-moduledoc(#{ format => "text/edoc" }).
使文档更结构化和可访问的直接结果是 Erlang 可以包含 doctests,这是在文档中运行和验证示例的能力。例如,有人可以这样写
-doc """
Encodes the given binary to base64.
1> base64:encode("hello").
<<"aGVsbG8=">>
""".
-spec encode(binary()) -> binary().
encode(Binary) ->
% ....
然后在您的测试套件中
doctests(_Config) ->
ct_doctest:run(base64).
doctest 属性将访问 base64 Docs 块中的文档条目,提取所有示例并运行它们。当然,虽然没有什么可以阻止在今天的 EDoc 之上实现 doctest,但此 EEP 使 doctest 的实现变得相当简单。
Doctests 将受益于单独的 EEP,因为有一些额外的考虑因素,例如 doctesting 异常、不可解析的格式等,但考虑到它们对用户和文档作者的好处,值得一提。
erl_docgen
怎么样? #如果此提案被接受,Edoc 会发生什么?
EEP 提出的工作的一个重要方面是尝试统一 Erlang/OTP 生态系统中的文档工具。在此之前,存在多个工具,EDoc 主要由开源项目使用,而 erl_docgen
由 Erlang/OTP 项目和其他第三方解决方案使用。
在短期内,将更新 EDoc,以便能够从包含 text/edoc
文档的 EEP 48 文档块生成 html 报告。指定文档的 EDoc 注释样式将被弃用,但是为了向后兼容,对 EDoc 注释的解析支持将保留很长时间。
从长远来看,目标是将文档渲染引擎切换为使用 ExDoc,后者可以通过将其作为 escript 运行或通过 Rebar3 集成来支持 Erlang 项目。使用 ExDoc 时,用户可以选择使用 EDoc 语法或迁移到 Markdown。
同样,Erlang/OTP 代码将转换为使用 ExDoc 而不是 erl_docgen
来生成文档。所有当前的 XML 文档文件都将转换为使用 Markdown。
本文档置于公共领域或 CC0-1.0-Universal 许可之下,以两者中更宽松的为准。