作者
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 提議一個官方 API 文件儲存方式,供 BEAM 語言使用。透過標準化 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 的整合。

第一部分:「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 條目。

第二部分:「Docs」格式 #

在兩種儲存方式中,文件都以完全相同的格式編寫:一個 Erlang 項,透過 term_to_binary/1 序列化為二進制檔案。該項在序列化時可以選擇性地壓縮,並且必須遵循以下的類型規格

{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 - 以文件語言作為鍵值的 map,例如 <<"en">><<"pt_BR">>,並以二進制值作為文件。如果沒有文件,則可以是原子 none,如果已明確停用此條目的文件,則可以是原子 hidden

  • Metadata - 以原子鍵值為鍵的 map,且以任何項為值。這可以用來新增註解,例如模組的「作者」、「已棄用」或語言或文件工具可能認為相關的任何其他內容

  • Docs - 模組中其他實體(例如函式和類型)的文件清單

Docs 中的每個條目,我們都有

  • {Kind, Name, Arity} - 識別函式、回呼、類型等的種類、名稱和元數。官方的實體是:functiontypecallback。其他語言會新增它們自己的實體。例如,Elixir 和 LFE 可以新增 macro

  • Anno - 模組文件或定義本身的註解(行、欄、檔案)(請參閱 erl_anno)

  • Signature - 實體的簽名。它是二進制檔案的清單。每個條目表示簽名中可以與空格或換行符連接的二進制檔案。例如,["binary_to_atom(Binary, Encoding)", "when is_binary(Binary)"] 可以呈現為單行或兩行。它的存在僅供展示之用

  • Doc - 以文件語言作為鍵值的 map,例如 <<"en">><<"pt_BR">>,並以值作為文件。文件可以是二進制檔案或任何 Erlang 項,兩者都由 Format 描述。如果是 Erlang 項,則 Format 必須是 "application/erlang+SUFFIX",例如當文件是 HTML 文件的 Erlang 表示時,則為 "application/erlang+html"。如果沒有文件,Doc 也可以是原子 none,如果已明確停用此條目的文件,則可以是原子 hidden

  • Metadata - 以原子鍵值為鍵的 map,且以任何項為值

注意:文件 map 可以是空的。在這種情況下,已將對該函式的參考新增到文件索引中,使其有效地公開,但沒有編寫任何文件。

這種共用格式是 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 的二進制檔案

可以隨時將任何鍵值新增到 Metadata。社群經常使用的鍵值可以在未來版本中標準化。

第三部分:與 OTP 的整合 #

最後一部分重點在於將前幾個部分與 OTP 文件、工具和工作流程整合。以下項目是建議,對於 OTP 或任何其他語言或函式庫採用此 EEP 並非必要。

此時,我們應該考慮對 OTP 進行變更,例如

  • doc/chunks/*.chunk 檔案作為 OTP 的一部分分發,並變更與 OTP 一起提供的工具以依賴它們。例如,可以變更 erl -man lists 來定位 lists.chunk 檔案、解析文件,然後即時將其轉換為 man 頁面。這項工作可能需要多項變更,因為 OTP 將文件儲存在 XML 檔案以及原始碼中。edoc 本身應該使用可以從原始碼中輸出 .chunk 檔案的函式來擴充

  • h(Module)h(Module, Function, Arity) 和類似的函式新增到 Erlang 的 shell 中,以印出模組或給定函式和元數的文件。這應該能夠印出任何其他實作此提案的函式庫或語言的文件

常見問題 #

問:為什麼文件中會有 Format 條目?

此提案中的主要權衡是文件格式。我們有兩個選項

  • 允許每種語言/函式庫/工具選擇自己的文件格式
  • 對所有語言強制執行統一的文件格式

統一的文件格式讓語言和函式庫在選擇如何編寫文件方面沒有彈性。隨著生態系統變得更加多樣化,不太可能找到適合所有人的格式。因此,我們引入了 Format 欄位,允許每種語言和函式庫選擇其文件格式。缺點是,如果 Elixir 文件以 Markdown 編寫,而某種語言不知道如何格式化 Markdown,那麼該語言將必須選擇不顯示 Elixir 文件或以原始格式(即 Markdown)顯示它們。

Erlang 處於有利位置。所有語言都將能夠支援為 Erlang 選擇的任何格式,因為所有語言都在 Erlang 上執行,並且可以直接存取 Erlang 的工具。

問:如果我有一個使用自訂文件工具組的 Erlang/Elixir/LFE/Alpaca 函式庫,我也可以利用這個功能嗎?

只要文件最終出現在 Docs 區塊或 doc/chunks 目錄內,我們完全不關心文件最初是如何撰寫的。如果您使用自訂格式,您可能需要教導您選擇的語言如何呈現它。請參閱先前的問題。

版權 #

本文件已置於公共領域。