Skip to content

Commit 68d3c53

Browse files
committed
Allow listing outside URLs in extras
This makes it possible to delcare URLs as extras and have them listed as links in the sidebar. For example, to set a "Wikipedia" url: ```elixir extras: [ "Wikipedia": [url: "https://wikipedia.com"] ] ``` Closes #2084
1 parent 38485a1 commit 68d3c53

File tree

7 files changed

+92
-51
lines changed

7 files changed

+92
-51
lines changed

assets/js/sidebar/sidebar-list.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export function initialize () {
5959
const items = []
6060
const hasHeaders = Array.isArray(node.headers)
6161
const translate = hasHeaders ? undefined : 'no'
62+
const href = node?.url || `${node.id}.html`
6263

6364
// Group header.
6465
if (node.group !== group) {
@@ -78,7 +79,7 @@ export function initialize () {
7879
}
7980

8081
items.push(el('li', {}, [
81-
el('a', {href: `${node.id}.html`, translate}, [node.nested_title || node.title]),
82+
el('a', {href: href, translate}, [node.nested_title || node.title]),
8283
...childList(`node-${node.id}-headers`,
8384
hasHeaders
8485
? renderHeaders(node)

formatters/html/dist/html-5PS32TPG.js formatters/html/dist/html-AG646WP7.js

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ex_doc/formatter/html.ex

+32-5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ defmodule ExDoc.Formatter.HTML do
6565
%{config | main: main || @main}
6666
end
6767

68+
defp normalize_extras(base) when is_binary(base), do: {base, %{}}
69+
defp normalize_extras({base, opts}), do: {base, Map.new(opts)}
70+
6871
@doc """
6972
Autolinks and renders all docs.
7073
"""
@@ -209,6 +212,7 @@ defmodule ExDoc.Formatter.HTML do
209212
defp generate_extras(extras, config) do
210213
generated_extras =
211214
extras
215+
|> Enum.reject(&is_map_key(&1, :url))
212216
|> with_prev_next()
213217
|> Enum.map(fn {node, prev, next} ->
214218
filename = "#{node.id}.html"
@@ -349,6 +353,7 @@ defmodule ExDoc.Formatter.HTML do
349353

350354
extras =
351355
config.extras
356+
|> Enum.map(&normalize_extras/1)
352357
|> Task.async_stream(
353358
&build_extra(&1, groups, language, autolink_opts, source_url_pattern),
354359
timeout: :infinity
@@ -388,6 +393,24 @@ defmodule ExDoc.Formatter.HTML do
388393
Map.put(extra, :id, "#{extra.id}-#{discriminator}")
389394
end
390395

396+
defp build_extra({input, %{url: _} = input_options}, groups, _lang, _auto, _url_pattern) do
397+
input = to_string(input)
398+
title = input_options[:title] || filename_to_title(input)
399+
group = GroupMatcher.match_extra(groups, input)
400+
401+
# TODO: Can we make the content/source a link?
402+
403+
%{
404+
group: group,
405+
id: Utils.text_to_id(title),
406+
source_path: input_options[:url],
407+
source_url: input_options[:url],
408+
title: title,
409+
title_content: title,
410+
url: input_options[:url]
411+
}
412+
end
413+
391414
defp build_extra({input, input_options}, groups, language, autolink_opts, source_url_pattern) do
392415
input = to_string(input)
393416
id = input_options[:filename] || input |> filename_to_title() |> Utils.text_to_id()
@@ -447,10 +470,6 @@ defmodule ExDoc.Formatter.HTML do
447470
}
448471
end
449472

450-
defp build_extra(input, groups, language, autolink_opts, source_url_pattern) do
451-
build_extra({input, []}, groups, language, autolink_opts, source_url_pattern)
452-
end
453-
454473
defp normalize_search_data!(nil), do: nil
455474

456475
defp normalize_search_data!(search_data) when is_list(search_data) do
@@ -602,7 +621,15 @@ defmodule ExDoc.Formatter.HTML do
602621

603622
{path, opts} ->
604623
base = path |> to_string() |> Path.basename()
605-
{base, opts[:filename] || Utils.text_to_id(Path.rootname(base))}
624+
625+
txid =
626+
cond do
627+
filename = opts[:filename] -> filename
628+
url = opts[:url] -> url
629+
true -> Utils.text_to_id(Path.rootname(base))
630+
end
631+
632+
{base, txid}
606633
end)
607634
end
608635
end

lib/ex_doc/formatter/html/search_data.ex

+30-30
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,8 @@ defmodule ExDoc.Formatter.HTML.SearchData do
1818
["searchData=" | ExDoc.Utils.to_json(data)]
1919
end
2020

21-
defp extra(map) do
22-
if custom_search_data = map[:search_data] do
23-
extra_search_data(map, custom_search_data)
24-
else
25-
{intro, sections} = extract_sections_from_markdown(map.source)
26-
27-
intro_json_item =
28-
encode(
29-
"#{map.id}.html",
30-
map.title,
31-
:extras,
32-
intro
33-
)
34-
35-
section_json_items =
36-
for {header, body} <- sections do
37-
encode(
38-
"#{map.id}.html##{Utils.text_to_id(header)}",
39-
header <> " - #{map.title}",
40-
:extras,
41-
body
42-
)
43-
end
44-
45-
[intro_json_item | section_json_items]
46-
end
47-
end
48-
49-
defp extra_search_data(map, custom_search_data) do
50-
Enum.map(custom_search_data, fn item ->
21+
defp extra(%{search_data: search_data} = map) when is_list(search_data) do
22+
Enum.map(search_data, fn item ->
5123
link =
5224
if item.anchor === "" do
5325
"#{map.id}.html"
@@ -59,6 +31,34 @@ defmodule ExDoc.Formatter.HTML.SearchData do
5931
end)
6032
end
6133

34+
defp extra(%{url: url} = map) do
35+
[encode("#{map.id}", map.title, :extras, url)]
36+
end
37+
38+
defp extra(map) do
39+
{intro, sections} = extract_sections_from_markdown(map.source)
40+
41+
intro_json_item =
42+
encode(
43+
"#{map.id}.html",
44+
map.title,
45+
:extras,
46+
intro
47+
)
48+
49+
section_json_items =
50+
for {header, body} <- sections do
51+
encode(
52+
"#{map.id}.html##{Utils.text_to_id(header)}",
53+
header <> " - #{map.title}",
54+
:extras,
55+
body
56+
)
57+
end
58+
59+
[intro_json_item | section_json_items]
60+
end
61+
6262
defp module(%ExDoc.ModuleNode{} = node) do
6363
{intro, sections} = extract_sections(node.doc_format, node)
6464

lib/ex_doc/formatter/html/templates.ex

+9-10
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,9 @@ defmodule ExDoc.Formatter.HTML.Templates do
8282

8383
defp sidebar_extras(extras) do
8484
for extra <- extras do
85-
%{id: id, title: title, group: group, content: content} = extra
85+
%{id: id, title: title, group: group} = extra
8686

87-
item =
88-
%{
89-
id: to_string(id),
90-
title: to_string(title),
91-
group: to_string(group),
92-
headers: extract_headers(content)
93-
}
87+
item = %{id: to_string(id), title: to_string(title), group: to_string(group)}
9488

9589
case extra do
9690
%{search_data: search_data} when is_list(search_data) ->
@@ -103,10 +97,15 @@ defmodule ExDoc.Formatter.HTML.Templates do
10397
}
10498
end)
10599

106-
Map.put(item, :searchData, search_data)
100+
item
101+
|> Map.put(:headers, extract_headers(extra.content))
102+
|> Map.put(:searchData, search_data)
103+
104+
%{url: url} when is_binary(url) ->
105+
Map.put(item, :url, url)
107106

108107
_ ->
109-
item
108+
Map.put(item, :headers, extract_headers(extra.content))
110109
end
111110
end
112111
end

mix.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
%{
2-
"earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
2+
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
33
"easyhtml": {:hex, :easyhtml, "0.3.2", "050adfc8074f53b261f7dfe83303d864f1fbf5988245b369f8fdff1bf4c4b3e6", [:mix], [{:floki, "~> 0.35", [hex: :floki, repo: "hexpm", optional: false]}], "hexpm", "b6a936f91612a4870aa3e828cd8da5a08d9e3b6221b4d3012b6ec70b87845d06"},
44
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
55
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},

test/ex_doc/formatter/html_test.exs

+14
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ defmodule ExDoc.Formatter.HTMLTest do
112112
assert to_string(bar_content["h1"]) == "README bar"
113113
end
114114

115+
test "extras defined as external urls", %{tmp_dir: tmp_dir} = context do
116+
config =
117+
doc_config(context, extras: ["README.md", "Elixir": [url: "https://elixir-lang.org"]])
118+
119+
File.write!("#{tmp_dir}/README.md", "README")
120+
121+
generate_docs(config)
122+
123+
# Verify necessary files in .build
124+
content = File.read!(tmp_dir <> "/html/README.html")
125+
126+
assert content =~ "https://elixir-lang.org"
127+
end
128+
115129
test "warns when generating an index.html file with an invalid redirect",
116130
%{tmp_dir: tmp_dir} = context do
117131
output =

0 commit comments

Comments
 (0)