檢視原始碼 歡迎使用 EDoc

EDoc 是 Erlang 程式文件產生器。它受到 Java(TM) 程式語言的 Javadoc(TM) 工具的啟發,EDoc 適用於 Erlang 世界的慣例,並具有 Javadoc 中沒有的幾個功能。

EDoc 可以產生任何網路瀏覽器都可存取的靜態 HTML 文件,或使用 erlang+html(3)EEP-48 文件區塊,為其他工具(如 shell_docs)提供文件。

目錄

簡介

EDoc 允許您使用 "@Name ..." 形式的標籤,將 Erlang 程式的文件寫在原始程式碼本身的註解中。一個原始程式檔不一定要包含標籤,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 版),不應使用。

也可以使用 bin/edoc EScript 直接從命令列執行 EDoc。該腳本充當 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 <rjh@acme.com>
   @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 標籤中。

    注意:為達到相同目的而使用其他「虛擬」@-標籤,可能在 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 文字(即其目的、用途等)。在 '.' 和文字之間必須至少有一個空白字元。有關語法和範例,請參閱 類型規格。所有資料類型描述都會放在文件的單獨區段中,無論標籤出現在哪裡。

    您可以重複使用實際 Erlang 程式碼中的類型定義進行文件編寫,而不是在 EDoc 文件註解中指定完整的類型別名。有關範例,請參閱 類型規格

概觀標籤

以下標籤可以在概觀檔案中使用。

模組標籤

以下標籤可以在模組宣告之前使用

  • @author - 指定作者的姓名以及聯絡資訊。電子郵件地址可以用 <...> 分隔符號提供,而 URI 可以用 [...] 分隔符號提供。電子郵件和 URI 都是可選的,並且所有字串都會去除周圍的空格。

    名稱是第一個非空的字串,且不包含在 <...>[...] 內,也不僅包含空白字元。(換句話說,名稱可以出現在電子郵件和 URI 的前面、中間或後面,但不能被拆分;第一個之後的任何部分都會被忽略。)如果有提供電子郵件地址,但沒有名稱,則電子郵件字串也會被用作名稱。如果沒有 <...> 部分,但名稱字串包含 '@' 字元,則會被假定為電子郵件地址。名稱和電子郵件都不能省略。

    範例

       %% @author Richard Carlsson
       %% @author Richard Carlsson <carlsson.richard@gmail.com>
       %%   [http://example.net/richardc/]
       %% @author <carlsson.richard@gmail.com>
       %% @author carlsson.richard@gmail.com [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_runerl.lang.list全域
函數/元數file/2在模組內
Module:Function/Arityedoc:application/2全域
Type()filename()在模組內
Module:Type()edoc:edoc_module()全域
//應用程式edoc全域
//應用程式/模組edoc_doclet全域
//應用程式/模組:函數/元數edoc_run:file/1全域
//應用程式/模組:類型()edoc:edoc_module()全域

表格:參考語法

EDoc 將使用它在 doc_path 選項指定的位置的 edoc-info 檔案中找到的資訊來解析參考。EDoc 將自動(並在某種程度上智能地)嘗試使用目前的程式碼路徑尋找任何本機 edoc-info 檔案,並將它們新增至 doc_path 清單的結尾。也會在目標文件目錄中搜尋現有的資訊檔案;這允許以遞增方式建立文件。(使用 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 `` 'foo@erlang.org' ''...
     %% @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}

其中 *name* 和 *argument* 以一個或多個空白字元分隔。引數可以是任何文字,其中可能包含其他巨集呼叫。未逸出的 "{@" 和 "}" 分隔符號的數量必須平衡。

引數文字首先在目前的環境中展開,並且結果會繫結到巨集參數,寫為 {@?}。(如果沒有給定引數,則 {@?} 會繫結到空字串。)然後巨集定義會替換為呼叫,並且擴展會繼續處理結果文字。不允許遞迴巨集擴展。

使用者定義的巨集

使用者可以使用 def EDoc 選項定義自己的巨集;有關更多資訊,請參閱 edoc:file/2edoc:get_doc/2。使用者定義的巨集會覆寫預定義的巨集。

預定義的巨集

  • {@date} - 擴展為目前日期,格式為 "月份 日 年",例如 "1月 29 2024"。

  • {@link reference. description} - 這會建立超連結;請參閱上方的 @see 函式標籤以瞭解詳細資訊。描述文字(包括句點分隔符號)是可選的;如果沒有給定文字,則使用參考本身。例如,{@link edoc:file/2} 會建立連結 edoc:file/2,而 {@link edoc:file/2. <em>這個連結</em>} 會建立 這個連結

  • {@module} - 擴展為目前模組的名稱。僅在處理模組時定義。

  • {@section heading} - 擴展為指向指定章節標題的超連結;有關更多資訊,請參閱標題

  • {@time} - 擴展為目前時間,格式為 "時:分:秒",例如 "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",則會產生文字 "請透過 support@vaporware.acme.com 聯絡我們"。還有:

       %% @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)才能正確產生 doc 區塊。冗餘的 -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 標籤後面的定義形式

類型定義::=`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 理解應用程式目錄佈局。

現在要追蹤所有提出進一步建議或提交錯誤報告的人變得太困難了,但我們始終感謝您的投入。謝謝。