Skip to content

Commit bb2a1ad

Browse files
committed
Add IDs to nodes upfront
1 parent 8bbc037 commit bb2a1ad

File tree

4 files changed

+33
-30
lines changed

4 files changed

+33
-30
lines changed

lib/ex_doc/doc_ast.ex

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,22 @@ defmodule ExDoc.DocAST do
142142

143143
@doc """
144144
Adds an id attribute to the given headers.
145+
146+
A prefix for the id attribute can be given,
147+
which is automatically URL encoded to avoid
148+
issues.
145149
"""
146-
def add_ids_to_headers(doc_ast, headers) do
150+
def add_ids_to_headers(doc_ast, headers, prefix \\ "") do
151+
prefix = URI.encode(prefix)
152+
147153
doc_ast
148154
|> map_reduce_tags(%{}, fn {tag, attrs, inner, meta} = ast, seen ->
149155
if tag in headers and not Keyword.has_key?(attrs, :id) do
150156
possible_id = inner |> text() |> ExDoc.Utils.text_to_id()
151157
id_count = Map.get(seen, possible_id, 0)
152-
actual_id = if id_count >= 1, do: "#{possible_id}-#{id_count}", else: possible_id
158+
partial_id = if id_count >= 1, do: "#{possible_id}-#{id_count}", else: possible_id
153159
seen = Map.put(seen, possible_id, id_count + 1)
154-
{{tag, [id: actual_id] ++ attrs, inner, meta}, seen}
160+
{{tag, [id: prefix <> partial_id] ++ attrs, inner, meta}, seen}
155161
else
156162
{ast, seen}
157163
end

lib/ex_doc/formatter/html.ex

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ defmodule ExDoc.Formatter.HTML do
322322
title: "API Reference",
323323
title_content: title_content,
324324
headers:
325-
if(nodes_map.modules != [], do: ["Modules"], else: []) ++
326-
if(nodes_map.tasks != [], do: ["Mix Tasks"], else: [])
325+
if(nodes_map.modules != [], do: [{:h2, "Modules", "modules"}], else: []) ++
326+
if(nodes_map.tasks != [], do: [{:h2, "Mix Tasks", "mix-tasks"}], else: [])
327327
}
328328
end
329329

@@ -423,6 +423,7 @@ defmodule ExDoc.Formatter.HTML do
423423
ast =
424424
source
425425
|> Markdown.to_ast(opts)
426+
|> ExDoc.DocAST.add_ids_to_headers([:h2, :h3])
426427
|> sectionize(extension)
427428

428429
{source, ast}
@@ -447,20 +448,20 @@ defmodule ExDoc.Formatter.HTML do
447448

448449
source_path = source_file |> Path.relative_to(File.cwd!()) |> String.replace_leading("./", "")
449450
source_url = source_url_pattern.(source_path, 1)
450-
451451
search_data = normalize_search_data!(input_options[:search_data])
452452

453453
%{
454454
source: source,
455-
content: content_html,
456455
group: group,
457456
id: id,
458457
source_path: source_path,
459458
source_url: source_url,
460459
search_data: search_data,
461460
title: title,
462461
title_content: title_html || title,
463-
headers: ExDoc.DocAST.extract_headers(ast, [:h2])
462+
# TODO: Remove these fields but first we would need to make API reference return DocAST
463+
content: content_html,
464+
headers: ExDoc.DocAST.extract_headers_with_ids(ast, [:h2])
464465
}
465466
end
466467

lib/ex_doc/formatter/html/templates.ex

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,30 +135,18 @@ defmodule ExDoc.Formatter.HTML.Templates do
135135
%{key: text_to_id(group), name: group, nodes: nodes}
136136
end
137137

138-
defp module_sections(%ExDoc.ModuleNode{rendered_doc: nil}), do: [sections: []]
139-
140138
defp module_sections(module) do
141-
{sections, _} =
142-
module.doc
143-
|> ExDoc.DocAST.extract_headers([:h2])
139+
sections =
140+
(module.doc || [])
141+
|> ExDoc.DocAST.extract_headers_with_ids([:h2])
144142
|> headers_to_id_and_anchors()
145-
|> Enum.map_reduce(%{}, fn header, acc ->
146-
# TODO Duplicates some of the logic of link_headings/3
147-
case Map.fetch(acc, header.id) do
148-
{:ok, id} ->
149-
{%{header | anchor: "module-#{header.anchor}-#{id}"}, Map.put(acc, header.id, id + 1)}
150-
151-
:error ->
152-
{%{header | anchor: "module-#{header.anchor}"}, Map.put(acc, header.id, 1)}
153-
end
154-
end)
155143

156144
[sections: sections]
157145
end
158146

159147
defp headers_to_id_and_anchors(headers) do
160-
Enum.map(headers, fn text ->
161-
%{id: text, anchor: URI.encode(text_to_id(text))}
148+
Enum.map(headers, fn {:h2, text, anchor} ->
149+
%{id: text, anchor: anchor}
162150
end)
163151
end
164152

lib/ex_doc/retriever.ex

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ defmodule ExDoc.Retriever do
135135
relative_path: path_relative_to_cwd(module_data.source_file)
136136
}
137137

138-
{doc_line, doc_file, format, source_doc, doc, metadata} = get_module_docs(module_data, source)
138+
{doc_line, doc_file, format, source_doc, doc_ast, metadata} =
139+
get_module_docs(module_data, source)
139140

140141
group_for_doc = config.group_for_doc
141142
annotations_for_docs = config.annotations_for_docs
@@ -157,7 +158,7 @@ defmodule ExDoc.Retriever do
157158
docs_groups: config.docs_groups ++ module_data.default_groups,
158159
docs: ExDoc.Utils.natural_sort_by(docs, &"#{&1.name}/#{&1.arity}"),
159160
doc_format: format,
160-
doc: doc,
161+
doc: normalize_doc_ast(doc_ast, "module-"),
161162
source_doc: source_doc,
162163
moduledoc_line: doc_line,
163164
moduledoc_file: doc_file,
@@ -176,6 +177,12 @@ defmodule ExDoc.Retriever do
176177
nil
177178
end
178179

180+
# TODO: Consider perhaps moving auto-linking here.
181+
defp normalize_doc_ast(doc_ast, prefix) do
182+
doc_ast
183+
|> DocAST.add_ids_to_headers([:h2, :h3], prefix)
184+
end
185+
179186
# Helpers
180187

181188
defp get_module_docs(module_data, source) do
@@ -219,17 +226,18 @@ defmodule ExDoc.Retriever do
219226
defaults = get_defaults(name, arity, Map.get(metadata, :defaults, 0))
220227

221228
doc_ast =
222-
(source_doc && doc_ast(content_type, source_doc, file: doc_file, line: doc_line + 1)) ||
229+
doc_ast(content_type, source_doc, file: doc_file, line: doc_line + 1) ||
223230
doc_data.doc_fallback.()
224231

225232
group = group_for_doc.(metadata) || doc_data.default_group
233+
id = doc_data.id_key <> nil_or_name(name, arity)
226234

227235
%ExDoc.DocNode{
228-
id: doc_data.id_key <> nil_or_name(name, arity),
236+
id: id,
229237
name: name,
230238
arity: arity,
231239
deprecated: metadata[:deprecated],
232-
doc: doc_ast,
240+
doc: normalize_doc_ast(doc_ast, id <> "-"),
233241
source_doc: source_doc,
234242
doc_line: doc_line,
235243
doc_file: doc_file,

0 commit comments

Comments
 (0)