XUtils

ex_doc

ExDoc is a tool to generate documentation for your Elixir projects.


ExDoc

Build Status

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 to apt 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.


Articles

  • coming soon...