Packages
ex_doc
0.14.0
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 """
Handle all template interfaces for the HTML formatter.
"""
require EEx
@doc """
Generate content from the module template for a given `node`
"""
def module_page(node, modules, exceptions, protocols, config) do
types = group_types(node)
module_template(config, node, types.types, types.functions, types.macros, types.callbacks,
modules, exceptions, protocols)
end
@doc """
Get the full specs from a function, already in HTML form.
"""
def get_specs(%ExDoc.TypeNode{spec: spec}) do
[spec]
end
def get_specs(%ExDoc.FunctionNode{specs: specs}) when is_list(specs) do
presence specs
end
def get_specs(_node) do
nil
end
@doc """
Convert markdown to HTML.
"""
def to_html(nil), do: nil
def to_html(bin) when is_binary(bin), do: ExDoc.Markdown.to_html(bin)
@doc """
Get the pretty name of a function node
"""
def pretty_type(%ExDoc.TypeNode{type: t}) do
Atom.to_string(t)
end
def pretty_type(%ExDoc.FunctionNode{type: t}) do
case t do
:def -> "function"
:defmacro -> "macro"
:callback -> "callback"
:macrocallback -> "macro callback"
end
end
@doc """
Generate a link id
"""
def link_id(node), do: link_id(node.id, node.type)
def link_id(id, type) do
case type do
:macrocallback -> "c:#{id}"
:callback -> "c:#{id}"
:type -> "t:#{id}"
_ -> "#{id}"
end
end
@doc """
Gets the first paragraph of the documentation of a node. It strips
surrounding spaces and strips traling `:` and `.`.
If `doc` is `nil`, it returns `nil`.
"""
@spec synopsis(String.t) :: String.t
@spec synopsis(nil) :: nil
def synopsis(nil), do: nil
def synopsis(""), do: ""
def synopsis(doc) when is_bitstring(doc) do
doc
|> String.split(~r/\n\s*\n/)
|> hd()
|> String.strip()
|> String.replace(~r{[.:\s]+$}, "")
|> String.rstrip()
end
defp presence([]), do: nil
defp presence(other), do: other
@doc false
def h(binary) do
escape_map = [{"&", "&"}, {"<", "<"}, {">", ">"}, {~S("), """}]
Enum.reduce escape_map, binary, fn({pattern, escape}, acc) ->
String.replace(acc, pattern, escape)
end
end
@doc false
def enc_h(binary) do
binary
|> URI.encode()
|> h()
end
@doc """
Create a JS object which holds all the items displayed in the sidebar area
"""
@spec create_sidebar_items(list) :: String.t
def create_sidebar_items(input) do
object =
input
|> Enum.into([], &sidebar_items_keys/1)
|> Enum.join(",")
"sidebarNodes={#{object}}"
end
defp sidebar_items_keys({:extras, value}) do
keys =
value
|> Enum.into([], &sidebar_items_extra/1)
|> Enum.join(",")
~s/"extras":[#{keys}]/
end
defp sidebar_items_keys({id, value}) do
keys =
value
|> Enum.into([], &sidebar_items_node/1)
|> Enum.join(",")
~s/"#{id}":[#{keys}]/
end
defp sidebar_items_extra({id, title, headers}) do
headers = Enum.map_join(headers, ",", fn {header, anchor} ->
sidebar_items_object(header, anchor)
end)
~s/{"id":"#{id}","title":"#{title}","headers":[#{headers}]}/
end
defp sidebar_items_node(node) do
if Enum.empty?(node.docs) do
~s/{"id":"#{node.id}","title":"#{node.id}"}/
else
types =
node
|> group_types()
|> Enum.reject(fn {_type, entries} -> entries == [] end)
|> Enum.map_join(",", &sidebar_items_by_type/1)
~s/{"id":"#{node.id}","title":"#{node.id}",#{types}}/
end
end
defp sidebar_items_by_type({type, docs}) do
objects = Enum.map_join(docs, ",", fn doc ->
sidebar_items_object(doc.id, link_id(doc))
end)
~s/"#{type}":[#{objects}]/
end
defp sidebar_items_object(id, anchor) do
~s/{"id":"#{id}","anchor":"#{URI.encode(anchor)}"}/
end
def group_types(node) do
%{types: node.typespecs,
functions: Enum.filter(node.docs, & &1.type in [:def]),
macros: Enum.filter(node.docs, & &1.type in [:defmacro]),
callbacks: Enum.filter(node.docs, & &1.type in [:callback, :macrocallback])}
end
defp logo_path(%{logo: nil}), do: nil
defp logo_path(%{logo: logo}), do: "assets/logo#{Path.extname(logo)}"
defp sidebar_type(:protocol), do: "protocols"
defp sidebar_type(:exception), do: "exceptions"
defp sidebar_type(:extra), do: "extras"
defp sidebar_type(:module), do: "modules"
defp sidebar_type(:behaviour), do: "modules"
def asset_rev(output, pattern) do
output = Path.expand(output)
output
|> Path.join(pattern)
|> Path.wildcard()
|> relative_asset(output)
end
defp relative_asset([], _), do: nil
defp relative_asset([h|_], output), do: Path.relative_to(h, output)
@doc """
Extract a linkable ID from a heading
"""
@spec header_to_id(String.t) :: String.t
def header_to_id(header) do
header
|> String.replace(~r/<.+>/, "")
|> String.replace(~r/&#\d+;/, "")
|> String.replace(~r/&[A-Za-z0-9]+;/, "")
|> String.replace(~r/\W+/u, "-")
|> String.strip(?-)
|> String.downcase()
end
@doc """
Link secondary headings found with `regex` with in the given `content`.
IDs are prefixed with `prefix`.
"""
@h2_regex ~r/<h2.*?>(.+)<\/h2>/m
@spec link_headings(String.t, Regex.t, String.t) :: String.t
def link_headings(content, regex \\ @h2_regex, prefix \\ "")
def link_headings(nil, _, _), do: nil
def link_headings(content, regex, prefix) do
Regex.replace(regex, content, fn match, title ->
link_heading(match, title, header_to_id(title), prefix)
end)
end
defp link_heading(match, _title, "", _prefix), do: match
defp link_heading(_match, title, id, prefix) do
"""
<h2 id="#{prefix}#{id}" class="section-heading">
<a href="##{prefix}#{id}" class="hover-link"><i class="icon-link"></i></a>
#{title}
</h2>
"""
end
defp link_moduledoc_headings(content) do
link_headings(content, @h2_regex, "module-")
end
defp link_detail_headings(content, prefix) do
link_headings(content, @h2_regex, prefix <> "-")
end
templates = [
detail_template: [:node, :_module],
footer_template: [:config],
head_template: [:config, :page],
module_template: [:config, :module, :types, :functions, :macros, :callbacks,
:modules, :exceptions, :protocols],
not_found_template: [:config, :modules, :exceptions, :protocols],
api_reference_entry_template: [:node],
api_reference_template: [:config, :modules, :exceptions, :protocols],
extra_template: [:config, :title, :modules, :exceptions, :protocols, :content],
sidebar_template: [:config, :modules, :exceptions, :protocols],
summary_template: [:name, :nodes],
summary_item_template: [:node],
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
end
end