Current section

Files

Jump to
ex_doc lib ex_doc formatter html search_data.ex
Raw

lib/ex_doc/formatter/html/search_data.ex

defmodule ExDoc.Formatter.HTML.SearchData do
@moduledoc false
alias ExDoc.Utils
def create(nodes, extras, proglang) do
content_type =
case proglang do
:elixir -> "text/markdown"
:erlang -> "text/plain"
end
items = Enum.flat_map(nodes, &module/1) ++ Enum.flat_map(extras, &extra/1)
data = %{
items: items,
content_type: content_type,
producer: %{
name: "ex_doc",
version: Application.spec(:ex_doc)[:vsn]
}
}
["searchData=" | ExDoc.Utils.to_json(data)]
end
defp extra(map) do
{intro, sections} = extract_sections_from_markdown(map.source)
intro_json_item =
encode(
"#{map.id}.html",
map.title,
:extras,
intro
)
section_json_items =
for {header, body} <- sections do
encode(
"#{map.id}.html##{Utils.text_to_id(header)}",
header <> " - #{map.title}",
:extras,
body
)
end
[intro_json_item | section_json_items]
end
defp module(%ExDoc.ModuleNode{} = node) do
{intro, sections} = extract_sections(node.doc_format, node)
module =
encode(
"#{node.id}.html",
node.title,
node.type,
intro
)
module_sections =
for {header, body} <- sections do
encode(
"#{node.id}.html#module-#{Utils.text_to_id(header)}",
header <> " - #{node.title}",
node.type,
body
)
end
functions = Enum.flat_map(node.docs, &node_child(&1, node))
types = Enum.flat_map(node.typespecs, &node_child(&1, node))
[module] ++ module_sections ++ functions ++ types
end
defp node_child(node, module_node) do
{intro, sections} = extract_sections(module_node.doc_format, node)
child =
encode(
"#{module_node.id}.html##{node.id}",
"#{module_node.id}.#{node.name}/#{node.arity}",
node.type,
intro
)
child_sections =
for {header, body} <- sections do
encode(
"#{module_node.id}.html##{node.id}-#{Utils.text_to_id(header)}",
header <> " - #{module_node.id}.#{node.name}/#{node.arity}",
node.type,
body
)
end
[child] ++ child_sections
end
defp encode(ref, title, type, doc) do
%{
ref: URI.encode(ref),
title: title,
type: type,
doc: doc
}
end
defp extract_sections("text/markdown", %{source_doc: %{"en" => doc}}) do
extract_sections_from_markdown(doc)
end
defp extract_sections("application/erlang+html", %{rendered_doc: nil}) do
{nil, []}
end
defp extract_sections("application/erlang+html", %{rendered_doc: doc}) do
{clean_html(doc), []}
end
defp extract_sections(_format, _doc) do
{"", []}
end
defp extract_sections_from_markdown(string) do
[intro | sections] =
Regex.split(~r/(?<!#)###? (?<header>\b.+)/, string, include_captures: true)
sections =
for [header, section] <- Enum.chunk_every(sections, 2) do
header = String.trim_leading(header, "#")
section =
section
|> ExDoc.Utils.strip_tags(" ")
|> drop_ignorable_codeblocks()
|> String.trim()
{clean_markdown(header), section}
end
{clean_markdown(intro), sections}
end
defp clean_markdown(doc) do
doc
|> ExDoc.Utils.strip_tags(" ")
|> String.trim()
end
defp clean_html(doc) do
doc
|> ExDoc.Utils.strip_tags(" ")
|> String.replace(~r/\s+/, " ")
|> String.trim()
end
@ignored_codeblocks ~w[vega-lite]
defp drop_ignorable_codeblocks(section) do
block_names = Enum.join(@ignored_codeblocks, "|")
String.replace(section, ~r/^```(?:#{block_names})\n(?:[\s\S]*?)```$/m, "")
end
end