Packages
ex_doc
0.17.1
0.40.3
0.40.2
0.40.1
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.3
0.37.2
0.37.1
0.37.0
0.37.0-rc.2
0.37.0-rc.1
0.37.0-rc.0
0.36.1
0.36.0
0.35.1
0.35.0
0.34.2
0.34.1
0.34.0
0.33.0
0.32.2
0.32.1
0.32.0
0.31.2
0.31.1
0.31.0
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.6
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.3
0.27.2
0.27.1
0.27.0
0.26.0
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.2
0.24.1
0.24.0
0.23.0
0.22.6
0.22.5
0.22.4
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.2
0.20.1
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.19.0-rc
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
retired
0.17.1
0.17.0
retired
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
ExDoc is a documentation generation tool for Elixir
Current section
Files
Jump to
Current section
Files
lib/ex_doc.ex
defmodule ExDoc do
@moduledoc """
Elixir Documentation System. ExDoc produces documentation for Elixir projects
"""
defmodule Config do
@moduledoc """
Configuration structure that holds all the available options for ExDoc
You can find more details about these options in the `ExDoc.CLI` module.
"""
@default %{
formatter: "html",
language: "en",
output: "./doc",
retriever: ExDoc.Retriever,
source_ref: "master",
}
@spec default(atom) :: term
def default(field) do
Map.fetch!(@default, field)
end
def before_closing_head_tag(_), do: ""
def before_closing_body_tag(_), do: ""
defstruct [
assets: nil,
before_closing_head_tag: &__MODULE__.before_closing_head_tag/1,
before_closing_body_tag: &__MODULE__.before_closing_body_tag/1,
canonical: nil,
debug: false,
deps: [],
extra_section: nil,
extras: [],
filter_prefix: nil,
formatter: @default.formatter,
groups_for_extras: [],
groups_for_modules: [],
homepage_url: nil,
language: @default.language,
logo: nil,
main: nil,
output: @default.output,
project: nil,
retriever: @default.retriever,
source_beam: nil,
source_ref: @default.source_ref,
source_root: nil,
source_url: nil,
source_url_pattern: nil,
title: nil,
version: nil
]
@type t :: %__MODULE__{
assets: nil | String.t,
before_closing_head_tag: (atom() -> String.t),
before_closing_body_tag: (atom() -> String.t),
canonical: nil | String.t,
debug: boolean(),
deps: [{ebin_path :: String.t, doc_url :: String.t}],
extra_section: nil | String.t,
extras: list(),
groups_for_extras: keyword(),
filter_prefix: nil | String.t,
formatter: nil | String.t,
homepage_url: nil | String.t,
language: String.t,
logo: nil | Path.t,
main: nil | String.t,
groups_for_modules: keyword(),
output: nil | Path.t,
project: nil | String.t,
retriever: :atom,
source_beam: nil | String.t,
source_ref: nil | String.t,
source_root: nil | String.t,
source_url: nil | String.t,
source_url_pattern: nil | String.t,
title: nil | String.t,
version: nil | String.t
}
end
@ex_doc_version Mix.Project.config[:version]
@doc """
Returns the ExDoc version (used in templates).
"""
@spec version :: String.t
def version, do: @ex_doc_version
@doc """
Generates documentation for the given `project`, `vsn` (version)
and `options`.
"""
@spec generate_docs(String.t, String.t, Keyword.t) :: atom
def generate_docs(project, vsn, options) when is_binary(project) and is_binary(vsn) and is_list(options) do
config = build_config(project, vsn, options)
docs = config.retriever.docs_from_dir(config.source_beam, config)
find_formatter(config.formatter).run(docs, config)
end
# Builds configuration by merging `options`, and normalizing the options.
@spec build_config(String.t, String.t, Keyword.t) :: ExDoc.Config.t
defp build_config(project, vsn, options) do
options = normalize_options(options)
preconfig = %Config{
project: project,
version: vsn,
main: options[:main],
homepage_url: options[:homepage_url],
source_root: options[:source_root] || File.cwd!,
}
struct(preconfig, options)
end
# Short path for programmatic interface
defp find_formatter(modname) when is_atom(modname), do: modname
defp find_formatter("ExDoc.Formatter." <> _ = name) do
[name]
|> Module.concat()
|> check_formatter_module(name)
end
defp find_formatter(name) do
[ExDoc.Formatter, String.upcase(name)]
|> Module.concat()
|> check_formatter_module(name)
end
defp check_formatter_module(modname, argname) do
unless Code.ensure_loaded?(modname) do
raise "Formatter module not found for: #{argname}"
end
modname
end
# Helpers
defp normalize_options(options) do
pattern = options[:source_url_pattern] || guess_url(options[:source_url], options[:source_ref] || ExDoc.Config.default(:source_ref))
options = Keyword.put(options, :source_url_pattern, pattern)
if is_bitstring(options[:output]) do
Keyword.put(options, :output, String.trim_trailing(options[:output], "/"))
else
options
end
end
defp guess_url(url, ref) do
with {:ok, host_with_path} <- http_or_https(url),
{:ok, pattern} <- known_pattern(host_with_path, ref) do
"https://" <> append_slash(host_with_path) <> pattern
else
_ -> url
end
end
defp http_or_https("http://" <> rest),
do: {:ok, rest}
defp http_or_https("https://" <> rest),
do: {:ok, rest}
defp http_or_https(_),
do: :error
defp known_pattern("github.com/" <> _, ref),
do: {:ok, "blob/#{ref}/%{path}#L%{line}"}
defp known_pattern("gitlab.com/" <> _, ref),
do: {:ok, "blob/#{ref}/%{path}#L%{line}"}
defp known_pattern("bitbucket.org/" <> _, ref),
do: {:ok, "src/#{ref}/%{path}#cl-%{line}"}
defp known_pattern(_host_with_path, _ref),
do: :error
defp append_slash(url) do
if :binary.last(url) == ?/, do: url, else: url <> "/"
end
end