查看源码 欢迎使用 EDoc

EDoc 是 Erlang 程序文档生成器。受 Java(TM) 编程语言的 Javadoc(TM) 工具启发,EDoc 适应了 Erlang 世界的惯例,并具有 Javadoc 中没有的几个特性。

EDoc 可以生成静态 HTML 文档,可以使用任何 Web 浏览器访问,也可以使用 EEP-48 文档块,通过 erlang+html(3) 为其他工具(如 shell_docs)提供文档。

目录

简介

EDoc 允许你将 Erlang 程序的文档以注释的形式写在源代码本身中,使用 "@Name ..." 形式的标签。源文件不一定需要包含标签才能让 EDoc 生成其文档,但如果没有标签,结果将仅包含可以从模块中提取的基本可用信息。

标签必须是注释行的第一个内容,除了前导的 '%' 字符和空格。注释必须在程序声明之间,而不是与任何程序文本在同一行。所有后续文本(包括连续的注释行)直到注释结束或下一个标记行,都被视为标签的内容

标签与最近的下一个“有意义”的程序构造(模块名称声明和函数定义)相关联。其他构造将被忽略;例如,在

   %% @doc Prints the value X.

   -record(foo, {x, y, z}).

   print(X) -> ...

中,@doc 标签与函数 print/1 相关联。

请注意,在以下注释中

   % % @doc ...

标签被忽略,因为只有第一个 '%' 字符被认为是“前导的”。这允许标签被“注释掉”。

一些标签(如 @type)不需要与任何程序构造关联。这些标签可以放置在文件的末尾,即“页脚”。

运行 EDoc

以下是运行 EDoc 的主要函数

  • edoc:application/2: 为典型的 Erlang 应用程序创建文档。
  • edoc:files/2: 为指定的源文件集创建文档。
  • edoc:run/2: 通用接口函数;上述函数的公共后端。选项在此处记录。

请注意,函数 edoc:file/2 属于旧的、已弃用的接口(来自 EDoc 版本 0.1),不应使用。

也可以直接从命令行运行 EDoc,使用 bin/edoc EScript。该脚本充当 edoc:application/2edoc:files/2 函数的命令行入口点。它还允许仅生成 EEP-48 文档块(使用 -chunks 标志),而不是完整的 HTML 文档。

概述页面

当为整个应用程序生成文档时,会生成概述页面或“首页”。(您现在阅读的页面是概述页面。)它应包含应用程序的高级描述或用户手册,并将更详细的细节留给各个模块的文档。默认情况下,概述页面从目标目录中的文件 overview.edoc 生成(通常,这是应用程序目录的 doc 子目录);有关详细信息,请参阅 edoc_doclet

概述文件的格式与 EDoc 文档注释的格式相同(请参阅 简介),除了行没有前导的 '%' 字符。此外,第一个标签行之前的所有行都将被忽略,并且可以用作注释。概述文件中的所有标签(如 @doc@version 等)都指整个应用程序;有关详细信息,请参阅 概述标签

以下是概述文件内容的示例

   ** this is the overview.doc file for the application 'frob' **

   @author R. J. Hacker <[email protected]>
   @copyright 2007 R. J. Hacker
   @version 1.0.0
   @title Welcome to the `frob' application!
   @doc `frob' is a highly advanced frobnicator with low latency,
   ...

通用标签

以下标签可以在模块内的任何位置使用

  • @clear - 此标签会导致其上面的所有标签(直到上一个程序构造)被丢弃,包括 @clear 标签本身。标签后面的文本也将被忽略。这通常仅在包含条件编译的代码中才有用,当预处理打开时。(默认情况下,预处理是关闭的。)例如,在

       -ifdef(DEBUG).
       %% @doc ...
       foo(...) -> ...
       -endif.
       %% @clear
    
       %% @doc ...
       bar(...) -> ...

    中,@clear 标签确保 EDoc 在函数 bar 之前看不到两个 @doc 标签,即使函数 foo 的代码被预处理删除。(EDoc 无法看到第一个 @doc 标签“真正”属于什么,因为预处理会删除所有此类信息。)

  • @docfile - 读取纯文档文件(格式与概述文件相同 - 有关详细信息,请参阅 概述页面),并使用该文件中的标签,就好像它们写在 @docfile 标签的位置一样。内容是要读取的文件的名称;忽略前导和尾随空格。另请参阅 @headerfile

  • @end - 此标签后面的文本始终被忽略。必要时,使用此标签标记上一个标签的结尾,例如

       %% ----------------------------------
       %% ...
       %% @doc ...
       %% ...
       %% @end
       %% ----------------------------------

    以避免将最后一行“标尺”包含在 @doc 标签中。

    注意:为达到相同目的使用其他一些“虚拟”@-tag 可能在 EDoc 的特定实现中起作用,但不能保证。始终使用 @end 以确保未来的兼容性。

  • @headerfile - @docfile 标签 类似,但读取包含 Erlang 源代码的文件 - 通常这应该是一个头文件(扩展名为 .hrl)。如果该文件包含一个或多个函数定义或模块声明,则将忽略最后一个此类定义或模块声明之上的所有标签,并且 EDoc 将打印警告。此标签允许您在头文件中编写文档并将其插入到文档中的特定位置,即使该头文件被多个模块使用(即包含)。可以使用 includes 选项指定搜索路径(请参阅 edoc:read_source/2)。

  • @todo (或 @TODO) - 将待办事项附加到函数、模块或概述页面。内容可以是描述问题的任何 XHTML 文本,例如

       %% @TODO Finish writing the documentation.

       %% @todo Implement <a href="http://www.ietf.org/rfc/rfc2549.txt">RFC 2549</a>.

    这些标签也可以写成 "TODO:",例如

       %% TODO: call your mother

    有关更多信息,请参阅 Wiki 表示法。除非打开 todo 选项(请参阅 edoc:get_doc/2),否则通常不会显示待办事项。

  • @type - 文档化抽象数据类型或类型别名。内容包括类型声明或定义,可以选择后跟句点('.')分隔符和描述该类型的 XHTML 文本(即其目的、用途等)。在 '.' 和文本之间必须至少有一个空格字符。有关语法和示例,请参阅 类型规范。所有数据类型描述都放置在文档的单独部分中,无论标签出现在何处。

    无需在 EDoc 文档注释中指定完整的类型别名,可以重复使用实际 Erlang 代码中的类型定义进行文档化。有关示例,请参阅 类型规范

概述标签

以下标签可以在概述文件中使用。

模块标签

以下标签可以在模块声明之前使用

  • @author - 指定作者的姓名以及联系信息。可以在 <...> 分隔符内给出电子邮件地址,在 [...] 分隔符内给出 URI。电子邮件和 URI 都是可选的,并且所有字符串中的任何周围空格都被删除。

    该名称是第一个不在 <...>[...] 内且不只包含空格的非空字符串。(换句话说,名称可以出现在电子邮件和 URI 之前、之间或之后,但不能被拆分;第一个之后的所有部分都将被忽略。)如果给出了电子邮件地址,但没有给出名称,则电子邮件字符串也将用于名称。如果不存在 <...> 部分,但名称字符串包含 '@' 字符,则假定它是电子邮件地址。名称和电子邮件都不能省略。

    示例

       %% @author Richard Carlsson
       %% @author Richard Carlsson <[email protected]>
       %%   [http://example.net/richardc/]
       %% @author <[email protected]>
       %% @author [email protected] [http://example.net/richardc/]
  • @copyright - 指定模块的版权信息。内容可以是任意文本;例如

       %% @copyright 2001-2003 Richard Carlsson
  • @deprecated - 将模块标记为已弃用,表示不应再使用。内容必须是格式良好的 XHTML,最好包含指向替代方案的 {@link} 引用;例如

       %% @deprecated Please use the module {@link foo} instead.
  • @doc - 使用格式良好的 XHTML 文本描述模块。第一句话用作摘要(有关详细信息,请参阅 @doc 函数标签)。例如。

       %% @doc This is a <em>very</em> useful module. It is ...
  • @hidden - 标记模块,使其不会出现在文档中(即使生成“私有”文档也是如此)。 对于示例代码、测试模块等很有用。内容可以用作注释;EDoc 会忽略它。

  • @private - 将模块标记为私有(即,不属于公共接口),因此它不会出现在常规文档中。(如果生成“私有”文档,则会包含该模块。)内容可以用作注释;EDoc 会忽略它。

  • @reference - 指定对一些任意外部资源的引用,例如文章、书籍或网站。内容必须是格式良好的 XHTML 文本。示例

       %% @reference Pratchett, T., <em>Interesting Times</em>,
       %% Victor Gollancz Ltd, 1994.
       %% @reference See <a href="www.google.com">Google</a> for
       %% more information.
  • @see - 有关详细信息,请参阅 @see 函数标签

  • @since - 指定模块相对于其所属的应用程序、版本或发行版是在何时引入的。内容可以是任意文本。

  • @version - 指定模块版本。内容可以是任意文本。

函数标签

以下标签可以在函数定义之前使用

  • @deprecated - 有关详细信息,请参阅 @deprecated 模块标签

  • @doc - 描述函数的 XHTML 文本。文本的第一句话用作快速摘要;它在第一个句点字符(“.”)或感叹号(“!”)后结束,该字符后跟一个空格字符、换行符或标签文本的结尾,并且不在 XML 标记中。(作为例外,第一句话可能在初始段落元素中)

  • @equiv - 指定与另一个函数调用/表达式的等效性。内容必须是正确的 Erlang 表达式。如果表达式是函数调用,则会自动创建对被调用函数的交叉引用。通常,此标签用于代替 @doc

  • @hidden - 标记函数,使其不会出现在文档中(即使生成“私有”文档也是如此)。 对于调试/测试函数等很有用。内容可以用作注释;EDoc 会忽略它。

  • @param - 提供有关封闭函数的单个参数的更多信息。内容由参数名称组成,后跟一个或多个空格字符和 XHTML 文本。

  • @private - 将函数标记为私有(即,不属于公共接口),因此它不会出现在常规文档中。(如果生成“私有”文档,则会包含该函数。)仅对导出的函数有用,例如 spawn 的入口点。(非导出函数始终是“私有”的。)内容可以用作注释;EDoc 会忽略它。

  • @returns - 指定有关函数返回值的其他信息。内容由 XHTML 文本组成。

  • @see - 引用模块、函数、数据类型或应用程序。(请参阅引用。)内容由引用组成,可以选择后跟一个句点(“.”)、一个或多个空格字符和 XHTML 文本(用于标签);例如 "@see edoc" 或 "@see edoc. <b>EDoc</b>"。如果未指定标签文本,则将引用本身用作标签。

  • @since - 指定函数是在模块的哪个版本中引入的;参见 @version 模块标签。内容可以是任意文本。

  • @spec - 用于指定函数类型;有关语法详细信息,请参阅类型规范。如果函数名称包含在规范中,则它必须与实际代码中的名称匹配。当规范中未给出参数名称时,如果可能,将从源代码中获取合适的名称,否则将合成名称。

    除了在 EDoc 文档注释中指定完整的函数类型外,还可以将实际 Erlang 代码中的规范重新用于文档。有关示例,请参阅类型规范

  • @throws - 指定如果函数的执行因调用 erlang:throw(Term) 而突然终止,则该函数可能抛出的术语类型。内容是类型表达式(请参阅类型规范),并且可以是联合类型。

    请注意,类型为 exit 的异常(如调用 erlang:exit(Term) 引起的异常)和类型为 error 的异常(如 badargbadarith 等运行时错误)不被视为函数正常接口的一部分,并且无法使用 @throws 标签进行文档记录。

  • @type - 有关详细信息,请参阅 @type 通用标签。将 @type 标签放置在函数定义旁边可能很方便,但不会影响描述在生成的文档中的位置。

引用

在多个上下文中(@see 标签、@link 宏等),EDoc 允许你使用简单紧凑的语法引用模块、函数、数据类型和应用程序的生成文档。引用的可能格式为

引用语法示例范围
模块edoc_run, erl.lang.list全局
函数/元数file/2在模块内
模块:函数/元数edoc:application/2全局
Type()filename()在模块内
模块:Type()edoc:edoc_module()全局
//应用程序edoc全局
//应用程序/模块edoc_doclet全局
//应用程序/模块:函数/元数edoc_run:file/1全局
//应用程序/模块:类型()edoc:edoc_module()全局

表格:引用语法

EDoc 将使用在通过 doc_path 选项指定的位置找到的 edoc-info 文件中的信息解析引用。EDoc 将自动(并且有些智能地)尝试使用当前代码路径查找任何本地 edoc-info 文件,并将其添加到 doc_path 列表的末尾。还会在目标 doc 目录中搜索现有的信息文件;这允许增量构建文档。(使用 new 选项来忽略任何旧的信息文件。)

请注意,如果模块、函数或数据类型的名称明确使用应用程序进行限定(如 "//edoc/edoc_run" 中所示),这将覆盖有关该名称的任何其他信息,并且引用将相对于应用程序的位置进行(如果可以找到)。这使得可以引用例如模块 "fred" 作为 "//foo/fred",而不会意外获得对 "//bar/fred" 的引用。你不应为你当前创建的应用程序本地的名称使用这种形式的显式引用 - 它们将始终被正确解析。

请注意,诸如 file/2 之类的模块本地引用仅在模块内正常工作。在此类概述页面中(即,你当前正在阅读的页面),没有可用的模块上下文。

关于 XHTML 的说明

在多个位置,XHTML 标记可以用在文档文本中,尤其是在 @doc 标签中。与 HTML 的主要区别如下

  • 所有元素都必须具有显式的开始和结束标签,并且必须正确嵌套。这意味着你不能例如编写一个 <li> 标签而不必在正确的位置编写相应的 </li> 标签。这有时可能会令人讨厌,但它的巨大优势在于,EDoc 可以报告源代码中所有格式错误的 XHTML,而不是将错误传播到生成的文档中。
  • XHTML 标签和属性名称应始终为小写。
  • 属性必须用引号引起来,如 <a name="top"> 中所示。

要编写一个像 HTML <br> 这样的没有实际内容的元素,你可以编写完整的 <br></br>,或者更好的是,使用 XHTML 的简写形式 <br/>

由于 EDoc 的目的是记录程序,因此还提供了一种有限形式的“wiki”语法,使程序代码更容易内联编写(并使文档注释更容易阅读)。有关详细信息,请参阅Wiki 表示法

HTML 标题标签 h1h2 保留供 EDoc 使用。文档源代码中的标题应从 h3 开始。但是,有一种特殊的语法可以用来编写标题,而完全避免使用特定的级别编号;有关详细信息,请参阅标题

EDoc 使用 XMerL 解析和导出 XML 标记。

Wiki 表示法

当 EDoc 解析 XHTML 时,它会对文本进行额外的预处理和后处理,以便将 EDoc 特有的某些表示法扩展为适当的 XHTML 标记。此“wiki”(http://en.wikipedia.org/wiki/Wiki)表示法旨在使编写源代码文档更加容易。

空行分隔段落

在 XHTML 文本中保留空行(即,除任何前导注释起始符“%”字符外,仅包含空格的行)将使 EDoc 将空行之前和之后的文本拆分为单独的段落。例如

   %% @doc This will all be part of the first paragraph.
   %% It can stretch over several lines and contain <em>any
   %% XHTML markup</em>.
   %%
   %% This is the second paragraph. The above line is
   %% regarded as "empty" by EDoc, even though it ends with
   %% a space.

将生成以下文本

这一切都将是第一段的一部分。它可以跨越多行并包含任何 XHTML 标记

这是第二段。上面一行被 EDoc 视为“空”,即使它以空格结尾。

段落拆分发生在实际 XHTML 解析之后。它仅影响块级文本,而不影响例如 <pre> 标记中的文本或已在 <p> 标记中的文本。

标题

可以使用以下表示法编写节标题、副标题和子副标题

   == Heading ==
   === Sub-heading ===
   ==== Sub-sub-heading ====

这样的标题必须单独一行,除了空格外,并且不能拆分为多行。通过将文本中的任何空格替换为单个下划线字符,自动为标题创建链接目标。例如,

   == Concerning Hobbits ==

等效于

   <h3><a name="Concerning_Hobbits">Concerning Hobbits</a></h3>

因此,使用这种表示法的标题不应包含除空格之外的任何可能不属于 URL 标签的字符。如果需要创建此类标题,则必须使用显式的 XHTML 标记。

可以使用 @section 宏创建指向以此方式编写的标题的超链接,该宏会将参数文本转换为如上所述的标签。例如,

   {@section Concerning Hobbits}

等效于编写

   <a href="#Concerning_Hobbits">Concerning Hobbits</a>

以上扩展发生在 XML 解析之前。

在方括号内写入 URL,如“[http://www.w3c.org/]”,将生成一个超链接,如 http://www.w3c.org/,将 URL 同时用作目标和引用的标签,等效于编写“<a href="http://www.w3c.org/"><code>http://www.w3c.org/</code></a>”。这种简写方式使外部 URL 引用保持简洁且易于阅读。可识别的协议为 httpftpfile。此扩展发生在 XML 解析之前。

TODO 注释

以文本“TODO:”开头的行(冒号是必需的)会被识别为标签,就像它们被写成“@todo ...”一样(有关更多详细信息,请参见 @todo 标签)。

逐字引用

在 XHTML 文本中,“`”字符(Unicode 000060,称为“反引号”或“反引号”)可用于逐字引用。此扩展发生在 XML 解析之前。

  • 字符序列“`...`”或“``...''”将被扩展为“<code>...</code>”,其中所有出现的特殊 XML 字符“<”和“&”(为完整起见,还包括“>”)在引用的文本中都已转义为“&lt;”、“&amp;”和“&gt;”。所有空格都将从引用文本的开头和结尾处删除。

    双反引号“``...''”可用于引用包含单引号“'”字符的文本。自动删除周围的任何空格使得可以编写诸如“`` 'foo@bar' ''”之类的东西。

    要逐字引用包含“''”的文本,必须使用显式的 <code> 标记或类似标记。

  • 字符序列“```...'''”将扩展为“<pre><![CDATA[...]]></pre>”,这将禁用引用文本中的所有 XML 标记,并以固定字体显示结果,并保留缩进。空格将从引用文本的末尾删除,但不会从开头删除,除非是整行开头的空格。这对于多行代码示例或显示的单行代码很有用。

  • 要在 XML 中生成单个“`”字符而不开始新的引用,可以编写“`'”(“`”和“'”之间没有空格)。您当然也可以使用 XML 字符实体“&#x60;”。

示例

     %% @doc ...where the variable `Foo' refers to...
     %% @doc ...returns the atom `` '[email protected]' ''...
     %% @doc ...use the command ```erl -name foo''' to...
     %% @doc ...as in the following code:
     %% ```f(X) ->
     %%       case X of
     %%          ...
     %%       end'''
     %% @doc ...or in the following:
     %% ```
     %%     g(X) ->
     %%       fun () -> ... end
     %% '''

宏展开

在解析标签的内容之前,文本会进行宏展开。宏调用的语法为

    {@name}

    {@name argument}

其中nameargument用一个或多个空格字符分隔。参数可以是任何文本,其中可能包含其他宏调用。非转义的“{@”和“}”分隔符的数量必须平衡。

参数文本首先在当前环境中展开,结果绑定到宏参数,写为 {@?}。(如果未给出参数,则 {@?} 将绑定为空字符串。)然后,将宏定义替换为调用,并在生成的文本上继续展开。不允许递归宏展开。

用户定义的宏

用户可以使用 def EDoc 选项定义自己的宏;有关更多信息,请参见 edoc:file/2edoc:get_doc/2。用户定义的宏会覆盖预定义的宏。

预定义的宏

  • {@date} - 展开为当前日期,格式为“Month Day Year”,例如“Jan 29 2024”。

  • {@link reference. description} - 这会创建一个超链接;有关详细信息,请参见上面的 @see 函数标签。描述文本(包括句点分隔符)是可选的;如果未给出文本,则使用引用本身。例如,{@link edoc:file/2} 创建链接 edoc:file/2,而 {@link edoc:file/2. <em>this link</em>} 创建 this link

  • {@module} - 展开为当前模块的名称。仅当正在处理模块时才定义。

  • {@section heading} - 展开为指向指定节标题的超链接;有关更多信息,请参见 标题

  • {@time} - 展开为当前时间,格式为“Hr:Min:Sec”,例如“14:53:19”。

  • {@type type-expression} - <code>...</code> 标记中格式化类型表达式,并为数据类型添加超链接。例如,{@type {options, List::edoc:option_list()@}} 生成“{options, List::edoc:option_list()}”。(参见转义序列。)

  • {@version} - 旨在用于 @version 标签。默认为使用 {@date}{@time} 的时间戳。通常,当生成应用程序的官方版本时,用户会重新定义此宏。

转义序列

为防止某些字符被解释为分隔符,例如在输出中生成文本“{@”,或在宏调用的参数文本中使用“}”字符,可以使用以下转义序列

  • @{ - 展开为“{”。示例

       %% @doc A macro call starts with the sequence "@{@".
  • @} - 展开为“}”。示例

       %% @doc ...{@foo ...{Key, Value@}...}...
  • @@ - 展开为“@”。示例

       %% @doc Contact us at support@@{@hostname}

    如果宏 hostname 绑定到“vaporware.acme.com”,将生成文本“Contact us at [email protected]”。还有

       %% @doc You might want to write something like
       %% @@foo that will expand to @foo and does not start
       %% a new tag even if it appears first in a line.

类型规范

函数规范

请注意,尽管以下描述的语法仍然可以用于指定函数,但我们建议将 Erlang 规范(即 -spec-type 属性)添加到源代码中,如 类型和函数规范 中所述。这样,Dialyzer 的分析就可以用于保持文档的一致性和最新性。

Erlang 规范(-spec-type)是正确生成文档块所必需的。冗余的 -spec 属性和 @spec 标签会导致发出警告,并且规范将在块中跳过。旧式的 @spec@type 规范仍然可以用于生成静态 HTML 文档。

以下语法描述了 @spec 标签之后的规范形式。后缀“?”表示该元素是可选的。函数类型的优先级高于联合类型;例如,“(atom()) -> atom() | integer()”被解析为 ((atom()) -> atom()) | integer(),而不是 (atom()) -> (atom() | integer())

| Spec | ::= | FunType "where"? DefList? | FunctionName FunType "where"? DefList? | | --------------- | --- | ------------------------------------ | ---------------------------------------------- | ------------------ | -------------------------------- | -------------------- | ------- | ------------------ | ---------- | ------------------- | ------------------------ | ------- | ----------------- | --------------------------- | ----------------- | ------- | ---------------------------- | ------------------------------------------- | ------------------------------------------------------------- | | FunctionName | ::= | Atom | | FunType | ::= | "(" UnionTypes? ")" "->" UnionType | | UnionTypes | ::= | UnionType | UnionType "," UnionTypes | | UnionType | ::= | UnionList | Name "::" UnionList | | Name | ::= | Variable | | UnionList | ::= | Type | Type "+" UnionList | Type " | " UnionList | | Type | ::= | TypeVariable | Atom | Integer | Float | Integer ".." Integer | FunType | "fun(" FunType ")" | "fun(...)" | "{" UnionTypes? "}" | "#" Atom "{" Fields? "}" | "[" "]" | "[" UnionType "]" | "[" UnionType "," "..." "]" | "(" UnionType ")" | BinType | TypeName "(" UnionTypes? ")" | ModuleName ":" TypeName "(" UnionTypes? ")" | "//" AppName "/" ModuleName ":" TypeName "(" UnionTypes? ")" | | Fields | ::= | Field | Fields "," Fields | | Field | ::= | Atom "=" UnionList | | BinType | ::= | "<<>>" | "<<" BaseType ">>" | "<<" UnitType ">>" | "<<" BaseType "," UnitType ">>" | | BaseType | ::= | "_" ":" Integer | | UnitType | ::= | "_" ":" "_" "*" Integer | | TypeVariable | ::= | Variable | | TypeName | ::= | Atom | | ModuleName | ::= | Atom | ModuleName "." Atom | | AppName | ::= | Atom | | DefList | ::= | Def | DefList Def | DefList "," Def | | Def | ::= | TypeVariable "=" UnionList | TypeName "(" TypeVariables? ")" "=" UnionType | | TypeVariables | ::= | TypeVariable | TypeVariable "," TypeVariables |

表:规范语法

示例

    -spec my_function(X :: integer()) -> integer().
    %% @doc Creates ...
    %% @spec my_function(X::integer()) -> integer()
    %% @spec (X::integer()) -> integer()
    %% @spec sqrt(float()) -> float()
    %% @spec pair(S, T) -> {S, T}
    %% @spec append(List, List) -> List
    %%       List = [term()]
    %% @spec append(A::List, B::List) -> List
    %%       List = [Item]
    %%       Item = term()
    %% @spec open(File::filename()) -> FileDescriptor
    %% where
    %%       filename() = string() + atom(),
    %%       FileDescriptor = term()
    %% @spec close(graphics:window()) -> ok

第一个示例展示了推荐的指定函数的方法。

在以上示例中,XABFile 是参数名称,用于在文档文本中引用参数。类型变量 STList 用于简化类型规范,并且可以提供定义。也可以为命名类型提供定义,这意味着该名称只是一个别名。(使用 @type 标签来记录抽象数据类型。)如果命名类型在另一个模块中定义,则可以将其引用为 Module:TypeName(...)。请注意,在定义列表之前,关键字 'where' 是可选的,并且列表中的定义可以选择用 ',' 分隔。

字符 '|' 和 '+' 都可以用于分隔联合类型中的备选项;它们之间没有语义差异。请注意,符号 [Type] 表示“元素都属于 Type 的正确(以 nil 结尾的)列表”;例如,[atom()|integer()][atom()+integer()] 的含义相同,即原子和/或整数的正确列表。

如果仅为参数提供类型变量,如 "pair(S, T) -> ..." 中所示,则相同的变量名称可以隐式地用作参数名称;无需编写 "pair(S::S, T::T) -> ..."。

EDoc 会自动从源代码中提取可能的参数名称,以便在规范中未给出参数名称(或完全缺少规范)时使用。如果此操作失败,EDoc 将生成一个虚拟参数名称,例如 X1。这样,即使代码根本不包含任何注解,EDoc 通常也可以生成有用的文档。

类型定义

请注意,虽然以下描述的语法仍然可以用于指定类型,但我们建议将 类型和函数规范 中描述的 Erlang 类型添加到源代码中。

旧式 @type 标签将在文档块中仅生成类型名称,而不会生成定义 - 请使用 -type 属性来确保所有可用的类型信息都可用在块中。对于静态 HTML,除非存在具有相同名称的类型别名,否则将使用 Erlang 类型。

以下语法(辅助定义见上文)描述了可以跟随 @type 标签的定义的形式

Typedef::=`TypeName "(" TypeVariables? ")" DefList?``TypeName "(" TypeVariables? ")" "=" UnionList DefList?`

表:类型定义语法

(对于真正的抽象数据类型,没有指定等效项。)主定义之后可以跟随额外的本地定义。示例

    -type my_list(X) :: [X]. %% A special kind of lists ...
    -opaque another_list(X) :: [X].
    %% another_list() is a kind of list...
    %% @type myList(X). A special kind of lists ...
    %% @type filename() = string(). Atoms not allowed!
    %% @type thing(A) = {thong, A}
    %%           A = term().
    %%   A kind of wrapper type thingy.

前两个示例展示了指定类型的推荐方式。

预定义的数据类型

以下数据类型由 EDoc 预定义,并且不能重新定义

    any()
    arity()
    atom()
    binary()
    bitstring()
    bool()        (allowed, but use boolean() instead)
    boolean()
    byte()
    char()
    cons()
    deep_string()
    float()
    function()
    integer()
    iodata()
    iolist()
    list()
    maybe_improper_list()
    mfa()
    module()
    nil()
    neg_integer()
    node()
    non_neg_integer()
    nonempty_improper_list()
    nonempty_list()
    nonempty_maybe_improper_list()
    nonempty_string()
    none()
    number()
    pid()
    port()
    pos_integer()
    reference()
    string()
    term()
    timeout()
    tuple()

详细信息

  • any/0 表示“任何 Erlang 数据类型”。 term/0 只是 any/0 的别名。
  • atom/0binary/0float/0function/0integer/0pid/0port/0reference/0 是 Erlang 编程语言的基本数据类型。
  • boolean/0atom/0 的子集,由原子 truefalse 组成。
  • char/0integer/0 的子集,表示 Unicode 字符代码:十六进制 000000-10FFFF。
  • tuple/0 是所有元组 {...} 的集合。
  • list(T) 只是 [T] 的别名;list() 是 list(any()) 的别名,即 [any()]
  • nil/0 是空列表 [] 的别名。
  • cons(H,T) 是列表构造函数。通常不直接使用它。可以递归定义 list(T) := nil()+cons(T,list(T))
  • string/0[char()] 的别名。
  • deep_string() 递归定义为 [char()+deep_string()]
  • none/0 表示“没有数据类型”。例如,从不返回的函数的类型为 (...) -> none()

文档块

EDoc 实现了 EEP-48,并允许为使用 EDoc 语言进行源代码文档记录的 Erlang 项目输出文档块。

有两种方法可以生成文档块:使用 bin/edoc 或使用 Rebar3 edoc 命令。最终,它们都只是 EDoc 应用程序的入口点,因此选择使用哪个只是偏好问题。

使用 edoc EScript

为了使用 bin/edoc 生成文档块,请确保可以在 Erlang 代码路径中找到编译后的 Erlang 应用程序

$ pwd
/tmp/recon
$ rebar3 compile
$ export PATH="$(dirname `which erl`)/../lib/erlang/lib/edoc-0.12/bin:$PATH"
$ edoc -app recon -chunks -pa _build/default/lib/recon/ebin
Running with opts:
#{app => recon,
  code_paths => ["_build/default/lib/recon/ebin"],
  files => [],mode => chunks,run => app}
$ ls _build/default/lib/recon/doc/chunks/
recon.chunk        recon_lib.chunk    recon_rec.chunk
recon_alloc.chunk  recon_map.chunk    recon_trace.chunk

该项目不必使用 Rebar3 构建 - 以上只是一个示例。从现在开始,这些块将作为 Erlang shell 的文档的来源提供

$ erl -pa _build/default/lib/recon/ebin
...
1> h(recon).

   recon

  Recon, as a module, provides access to the high-level functionality contained in the
  Recon application.
  ...

2> h(recon_alloc, allocators, 0).

  -spec allocators() -> [allocdata(term())].

  returns a dump of all allocator settings and values

使用 Rebar3 edoc 命令

也可以使用 rebar3 edoc 构建文档块,前提是 edoc_opts 已正确设置

$ pwd
/tmp/recon
$ cat >> rebar.config <<EOF
> {edoc_opts, [{doclet, edoc_doclet_chunks},
>              {layout, edoc_layout_chunks},
>              {preprocess, true},
>              {dir, "_build/docs/lib/recon/doc"}]}.
> EOF
$ rebar3 edoc
$ ls _build/docs/lib/recon/doc/chunks/
recon.chunk        recon_lib.chunk    recon_rec.chunk
recon_alloc.chunk  recon_map.chunk    recon_trace.chunk

如果我们使用 docs 配置文件,rebar3 shell 将为我们设置路径

$ rebar3 as docs shell
...
1> h(recon).

   recon

  Recon, as a module, provides access to the high-level functionality contained in the
  Recon application.
  ...

使用 EDoc API

EDoc 带有两组 doclet/layout 对

为了使用 edoc:application/2edoc:files/2 生成文档块,我们必须指定要使用的 doclet 和 layout

Opts = [{doclet, edoc_doclet_chunks},
        {layout, edoc_layout_chunks}].

然后,只需决定是要为整个应用程序生成文档 (edoc:application/2) 还是仅为选定的源文件生成文档 (edoc:files/2)

App = my_app.
edoc:application(App, Opts).
%% or
Files = ["src/my_app_mod1.erl", "src/my_app_mod2.erl"].
edoc:files(Files, Opts).

有关使用此接口的示例,请参阅 src/edoc_cli.erl 源代码。

鸣谢

自 EDoc 的第一个版本以来,有几个人提出了建议(Luke Gorrie、Joe Armstrong、Erik Stenman、Sean Hinde、Ulf Wiger 等),甚至有人提交了代码来演示他们的想法(Vlad Dumitrescu、Johan Blom、Vijay Hirani 等)。在最初公开发布(EDoc 0.1 版)之后的伟大重写中,实际上没有包含任何这些代码,但大多数核心点在新系统中得到了解决,例如更好的模块化和插入不同布局引擎的可能性,以及使 EDoc 理解应用程序目录布局。

现在,很难跟踪所有提出进一步建议或提交错误报告的人,但我们始终感谢您的意见。谢谢。