Packages
ex_doc
0.38.4
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/formatter/html/templates.ex
defmodule ExDoc.Formatter.HTML.Templates do
@moduledoc false
require EEx
alias ExDoc.Formatter.HTML.Assets
import ExDoc.Utils,
only: [
h: 1,
before_closing_body_tag: 2,
before_closing_footer_tag: 2,
before_closing_head_tag: 2,
text_to_id: 1
]
@doc """
Generate content from the module template for a given `node`
"""
def module_page(module_node, config) do
module_template(config, module_node)
end
@doc """
Format the attribute type used to define the spec of the given `node`.
"""
def format_spec_attribute(module, node) do
module.language.format_spec_attribute(node)
end
@doc """
Get the pretty name of a function node
"""
def pretty_type(%{type: t}) do
Atom.to_string(t)
end
@doc """
Returns the HTML formatted title for the module page.
"""
def module_type(%{type: :task}), do: ""
def module_type(%{type: :module}), do: ""
def module_type(%{type: type}), do: "<small>#{type}</small>"
defp enc(binary), do: ExDoc.Utils.h(URI.encode(binary))
@doc """
Create a JS object which holds all the items displayed in the sidebar area
"""
def create_sidebar_items(config, nodes_map, extras) do
nodes =
nodes_map
|> Enum.map(&sidebar_module/1)
|> Map.new()
|> Map.put(:extras, api_reference(config, nodes_map) ++ sidebar_extras(extras))
["sidebarNodes=" | ExDoc.Utils.to_json(nodes)]
end
defp api_reference(%{api_reference: false}, _nodes_map), do: []
defp api_reference(_config, nodes_map) do
headers =
if(nodes_map.modules != [], do: [%{id: "Modules", anchor: "modules"}], else: []) ++
if(nodes_map.tasks != [], do: [%{id: "Mix Tasks", anchor: "tasks"}], else: [])
[%{id: "api-reference", title: "API Reference", group: "", headers: headers}]
end
defp sidebar_extras(extras) do
for extra <- extras do
%{id: id, title: title, group: group} = extra
item = %{id: to_string(id), title: to_string(title), group: to_string(group)}
case extra do
%{search_data: search_data} when is_list(search_data) ->
search_data =
Enum.map(search_data, fn search_item ->
%{
anchor: search_item.anchor,
id: search_item.title,
labels: [search_item.type]
}
end)
item
|> Map.put(:headers, headers(extra.doc))
|> Map.put(:searchData, search_data)
%{url: url} when is_binary(url) ->
Map.put(item, :url, url)
_ ->
Map.put(item, :headers, headers(extra.doc))
end
end
end
defp sidebar_module({id, modules}) do
modules =
for module <- modules do
groups =
case module.docs_groups do
[] -> []
entries -> [nodeGroups: Enum.map(entries, &sidebar_entries/1)]
end
pairs =
for key <- [:id, :title, :nested_title, :nested_context],
value = Map.get(module, key),
do: {key, value}
others = [
deprecated: not is_nil(module.deprecated),
sections: headers(module.doc || []),
group: to_string(module.group)
]
Map.new(groups ++ pairs ++ others)
end
{id, modules}
end
defp sidebar_entries(group) do
nodes =
for node <- group.docs do
id =
if "struct" in node.annotations do
node.signature
else
if node.name == nil do
"nil/#{node.arity}"
else
"#{node.name}/#{node.arity}"
end
end
deprecated? = not is_nil(node.deprecated)
%{id: id, title: node.signature, anchor: URI.encode(node.id), deprecated: deprecated?}
end
%{key: text_to_id(group.title), name: group.title, nodes: nodes}
end
defp headers(doc) do
doc
|> ExDoc.DocAST.extract_headers_with_ids([:h2])
|> Enum.map(fn {:h2, text, anchor} ->
%{id: text, anchor: anchor}
end)
end
defp favicon_path(%{favicon: nil}), do: nil
defp favicon_path(%{favicon: favicon}), do: "assets/favicon#{Path.extname(favicon)}"
defp logo_path(%{logo: nil}), do: nil
defp logo_path(%{logo: logo}), do: "assets/logo#{Path.extname(logo)}"
defp sidebar_type(:exception), do: "modules"
defp sidebar_type(:module), do: "modules"
defp sidebar_type(:behaviour), do: "modules"
defp sidebar_type(:protocol), do: "modules"
defp sidebar_type(:task), do: "tasks"
defp sidebar_type(:search), do: "search"
defp sidebar_type(:cheatmd), do: "extras"
defp sidebar_type(:livemd), do: "extras"
defp sidebar_type(:extra), do: "extras"
@section_header_class_name "section-heading"
@doc """
Renders the document in the page.
For now it enriches the document by adding fancy anchors
around h2 and h3 tags with IDs.
"""
def render_doc(nil), do: ""
def render_doc(ast) do
ast
|> add_fancy_anchors()
|> ExDoc.DocAST.to_string()
end
defp add_fancy_anchors(ast) do
ExDoc.DocAST.map_tags(ast, fn
{tag, attrs, inner, meta} = ast
when tag in [:h2, :h3] and not is_map_key(meta, :verbatim) ->
if id = Keyword.get(attrs, :id) do
attrs =
Keyword.update(
attrs,
:class,
@section_header_class_name,
&(&1 <> " " <> @section_header_class_name)
)
{tag, attrs,
[
{:a, [href: "##{id}", class: "hover-link"],
[
{:i, [class: "ri-link-m", "aria-hidden": "true"], [], %{}}
], %{}},
{:span, [class: "text"], inner, %{}}
], meta}
else
ast
end
ast ->
ast
end)
end
templates = [
detail_template: [:node, :module],
footer_template: [:config, :source_path],
head_template: [:config, :title, :noindex],
module_template: [:config, :module],
not_found_template: [:config],
api_reference_entry_template: [:module_node],
api_reference_template: [:config, :nodes_map],
extra_template: [:config, :node, :refs],
search_template: [:config],
sidebar_template: [:config, :type],
summary_template: [:name, :nodes],
redirect_template: [:config, :redirect_to]
]
Enum.each(templates, fn {name, args} ->
filename = Path.expand("templates/#{name}.eex", __DIR__)
@doc false
EEx.function_from_file(:def, name, filename, args, trim: true)
end)
end