ExDoc
ExDoc is a tool to generate documentation for Erlang and Elixir projects. To see an example, you can access Elixir’s official docs.
Mix
ExDoc requires Elixir v1.12 or later. Then add ExDoc as a dependency:
def deps do
[
{:ex_doc, "~> 0.31", only: :dev, runtime: false},
]
end
Then run mix deps.get
.
Erlang development environment {: .warning}
Some Operating System distributions split Erlang into multiple packages, and at least one ExDoc dependency (
earmark_parser
) requires the Erlang development environment. If you see a message like “/usr/lib/erlang/lib/parsetools-2.3.1/include/yeccpre.hrl: no such file or directory”, it means you lack this environment. For instance, on the Debian operating system and its derivatives, you need toapt install erlang-dev
.
ExDoc will automatically pull in information from your projects, such as the application and version. However, you may want to set :name
, :source_url
and :homepage_url
in order to have nicer output from ExDoc:
def project do
[
app: :my_app,
version: "0.1.0-dev",
deps: deps(),
# Docs
name: "MyApp",
source_url: "https://github.com/USER/PROJECT",
homepage_url: "http://YOUR_PROJECT_HOMEPAGE",
docs: [
main: "MyApp", # The main page in the docs
logo: "path/to/logo.png",
extras: ["README.md"]
]
]
end
Now you are ready to generate your project documentation with mix docs
. To see all options available, run mix help docs
.
To learn about how to document your projects, see Elixir’s writing documentation page.
Rebar3
From Erlang/OTP 24+, you can use ExDoc to render your Erlang documentation written with EDoc. See rebar3_ex_doc
for more information.
Elixir (Mix)
{:makeup_html, ">= 0.0.0", only: :dev, runtime: false}
Erlang
{extras, [<<"README.md">>, <<"cheatsheet.cheatmd">>]}.
Elixir
In Elixir, you can add metadata to modules and functions.
For a module, use @moduledoc
, which is equivalent to adding the annotation to everything inside the module (functions, macros, callbacks, types):
@moduledoc since: "1.10.0"
For a function, use @doc
:
@doc since: "1.13.1"
Auto-linking
ExDoc for Elixir and Erlang will automatically generate links across modules and functions if you enclose them in backticks.
Tabsets
Where only one section of content of a series is likely to apply to the reader, you may wish to define a set of tabs.
This example contains code blocks, separating them into tabs based on language:
Elixir
IO.puts "Hello, world!"
Erlang
io:fwrite("Hello, world!\n").
Tabbed content must be defined between <!-- tabs-open -->
and <!-- tabs-close -->
HTML comments. Each h3
heading results in a new tab panel, with its text setting the tab button label.
Here is the above example’s source:
<!-- tabs-open -->
### Elixir
```elixir
IO.puts "Hello, world!"
```
### Erlang
```erlang
io:fwrite("Hello, world!\n").
```
<!-- tabs-close -->
Extensions
ExDoc renders Markdown content for you, but you can extend it to render complex objects on the page using JavaScript. To inject custom JavaScript into every page, add this to your configuration:
docs: [
# ...
before_closing_head_tag: &before_closing_head_tag/1,
before_closing_body_tag: &before_closing_body_tag/1
]
# ...
defp before_closing_head_tag(:html) do
"""
<!-- HTML injected at the end of the <head> element -->
"""
end
defp before_closing_head_tag(:epub), do: ""
defp before_closing_body_tag(:html) do
"""
<!-- HTML injected at the end of the <body> element -->
"""
end
defp before_closing_body_tag(:epub), do: ""
Besides an anonymous function, you can also pass a module-function-args
tuple. It will call the given module and function, with the format prefixed to the arguments:
docs: [
# ...
before_closing_head_tag: {MyModule, :before_closing_head_tag, []},
before_closing_body_tag: {MyModule, :before_closing_body_tag, []}
]
Or you can pass a map where the key is the format:
docs: [
# ...
before_closing_head_tag: %{html: "...", epub: "..."},
before_closing_body_tag: %{html: "...", epub: "..."}
]
Rendering Math
If you write TeX-style math in your Markdown, such as $\sum_{i}^{N} x_i$
, it ends up as raw text on the generated pages. To render expressions, we recommend using KaTeX, a JavaScript library that turns expressions into graphics. To load and trigger KaTeX on every documentation page, we can insert the following HTML:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/katex-copytex@1.0.2/dist/katex-copytex.min.css" rel="stylesheet" type="text/css">
<script src="https://cdn.jsdelivr.net/npm/katex-copytex@1.0.2/dist/katex-copytex.min.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
onload="renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
]
});"></script>
</script>
For more details and configuration options, see the KaTeX Auto-render Extension.
Rendering Vega-Lite plots
Snippets are also objects you may want to render in a special manner. For example, assuming your Markdown includes Vega-Lite specification in vega-lite
code snippets:
<script src="https://cdn.jsdelivr.net/npm/vega@5.20.2"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5.1.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6.18.2"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
for (const codeEl of document.querySelectorAll("pre code.vega-lite")) {
try {
const preEl = codeEl.parentElement;
const spec = JSON.parse(codeEl.textContent);
const plotEl = document.createElement("div");
preEl.insertAdjacentElement("afterend", plotEl);
vegaEmbed(plotEl, spec);
preEl.remove();
} catch (error) {
console.log("Failed to render Vega-Lite plot: " + error)
}
}
});
</script>
For more details and configuration options, see vega/vega-embed.