Skip to content

Commit e81a31b

Browse files
committed
Allow groups to carry descriptions
1 parent 8552c49 commit e81a31b

File tree

8 files changed

+99
-15
lines changed

8 files changed

+99
-15
lines changed

lib/ex_doc/formatter/epub/templates/module_template.eex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
<%= if summary != [] do %>
1919
<section id="summary" class="details-list">
2020
<h1 class="section-heading">Summary</h1>
21-
<%= for {name, nodes} <- summary, do: H.summary_template(name, nodes) %>
21+
<%= for {group, nodes} <- summary, do: H.summary_template(group.title, nodes) %>
2222
</section>
2323
<% end %>
2424

25-
<%= for {name, nodes} <- summary, key = text_to_id(name) do %>
25+
<%= for {group, nodes} <- summary, key = text_to_id(group.title) do %>
2626
<section id="<%= key %>" class="details-list">
27-
<h1 class="section-heading"><%=h to_string(name) %></h1>
27+
<h1 class="section-heading"><%=h to_string(group.title) %></h1>
2828
<div class="<%= key %>-list">
2929
<%= for node <- nodes, do: H.detail_template(node, module) %>
3030
</div>

lib/ex_doc/formatter/html/templates.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ defmodule ExDoc.Formatter.HTML.Templates do
134134
%{id: id, title: node.signature, anchor: URI.encode(node.id), deprecated: deprecated?}
135135
end
136136

137-
%{key: text_to_id(group), name: group, nodes: nodes}
137+
%{key: text_to_id(group.title), name: group.title, nodes: nodes}
138138
end
139139

140140
defp module_sections(%ExDoc.ModuleNode{rendered_doc: nil}), do: [sections: []]
@@ -169,7 +169,14 @@ defmodule ExDoc.Formatter.HTML.Templates do
169169

170170
def module_summary(module_node) do
171171
# TODO: Maybe it should be moved to retriever and it already returned grouped metadata
172-
ExDoc.GroupMatcher.group_by(module_node.docs_groups, module_node.docs, & &1.group)
172+
173+
group_titles = Enum.map(module_node.docs_groups, & &1.title)
174+
groups_index = Map.new(module_node.docs_groups, &{&1.title, &1})
175+
docs_groups = ExDoc.GroupMatcher.group_by(group_titles, module_node.docs, & &1.group)
176+
177+
Enum.map(docs_groups, fn {group_title, nodes} ->
178+
{Map.fetch!(groups_index, group_title), nodes}
179+
end)
173180
end
174181

175182
defp favicon_path(%{favicon: nil}), do: nil

lib/ex_doc/formatter/html/templates/module_template.eex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@
3939
</a>
4040
<span class="text">Summary</span>
4141
</h1>
42-
<%= for {name, nodes} <- summary, do: summary_template(name, nodes) %>
42+
<%= for {group, nodes} <- summary, do: summary_template(group.title, nodes) %>
4343
</section>
4444
<% end %>
4545

46-
<%= for {name, nodes} <- summary, key = text_to_id(name) do %>
46+
<%= for {group, nodes} <- summary, key = text_to_id(group.title) do %>
4747
<section id="<%= key %>" class="details-list">
4848
<h1 class="section-heading">
4949
<a class="hover-link" href="#<%= key %>">
5050
<i class="ri-link-m" aria-hidden="true"></i>
5151
</a>
52-
<span class="text"><%= name %></span>
52+
<span class="text"><%= group.title %></span>
5353
</h1>
5454
<div class="<%= key %>-list">
5555
<%= for node <- nodes, do: detail_template(node, module) %>

lib/ex_doc/group_matcher.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ defmodule ExDoc.GroupMatcher do
1515
end
1616

1717
@doc """
18-
Group the following entries and while preserving the order in `groups`.
18+
Group the following entries while preserving the order in `groups`.
1919
"""
2020
def group_by(groups, entries, by) do
2121
entries = Enum.group_by(entries, by)

lib/ex_doc/nodes.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ defmodule ExDoc.ModuleNode do
2626
metadata: nil
2727

2828
@typep annotation :: atom()
29+
@typep doc_group :: %{
30+
title: String.t() | atom(),
31+
description: String.t() | nil
32+
}
2933

3034
@type t :: %__MODULE__{
3135
id: String.t(),
@@ -43,7 +47,7 @@ defmodule ExDoc.ModuleNode do
4347
moduledoc_file: String.t(),
4448
source_path: String.t() | nil,
4549
source_url: String.t() | nil,
46-
docs_groups: [atom()],
50+
docs_groups: [doc_group],
4751
docs: [ExDoc.DocNode.t()],
4852
typespecs: [ExDoc.DocNode.t()],
4953
type: atom(),

lib/ex_doc/retriever.ex

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ defmodule ExDoc.Retriever do
141141
annotations_for_docs = config.annotations_for_docs
142142

143143
docs = get_docs(module_data, source, group_for_doc, annotations_for_docs)
144+
145+
moduledoc_groups = Map.get(metadata, :groups, [])
146+
147+
{docs_groups, docs} =
148+
get_docs_groups(moduledoc_groups ++ config.docs_groups ++ module_data.default_groups, docs)
149+
144150
metadata = Map.put(metadata, :kind, module_data.type)
145151
group = GroupMatcher.match_module(config.groups_for_modules, module, module_data.id, metadata)
146152
{nested_title, nested_context} = module_data.nesting_info || {nil, nil}
@@ -154,7 +160,7 @@ defmodule ExDoc.Retriever do
154160
module: module,
155161
type: module_data.type,
156162
deprecated: metadata[:deprecated],
157-
docs_groups: config.docs_groups ++ module_data.default_groups,
163+
docs_groups: docs_groups,
158164
docs: ExDoc.Utils.natural_sort_by(docs, &"#{&1.name}/#{&1.arity}"),
159165
doc_format: format,
160166
doc: doc,
@@ -198,6 +204,30 @@ defmodule ExDoc.Retriever do
198204
filter_defaults(nodes)
199205
end
200206

207+
defp get_docs_groups(module_groups, doc_nodes) do
208+
module_groups = Enum.map(module_groups, &normalize_group/1)
209+
210+
# Doc nodes already have normalized groups
211+
nodes_groups = Enum.map(doc_nodes, & &1.group)
212+
213+
normal_groups = module_groups ++ nodes_groups
214+
215+
{docs_groups, _} =
216+
Enum.flat_map_reduce(normal_groups, %{}, fn group, seen ->
217+
if is_map_key(seen, group.title) do
218+
{[], seen}
219+
else
220+
{[group], Map.put(seen, group.title, true)}
221+
end
222+
end)
223+
224+
# We do not need the full group data in each doc node anymore, only the
225+
# title.
226+
doc_nodes = Enum.map(doc_nodes, &Map.put(&1, :group, &1.group.title))
227+
228+
{docs_groups, doc_nodes}
229+
end
230+
201231
defp get_doc(doc, doc_data, module_data, source, group_for_doc, annotations_for_docs) do
202232
{:docs_v1, _, _, content_type, _, module_metadata, _} = module_data.docs
203233
{{type, name, arity}, anno, _signature, source_doc, metadata} = doc
@@ -222,7 +252,7 @@ defmodule ExDoc.Retriever do
222252
(source_doc && doc_ast(content_type, source_doc, file: doc_file, line: doc_line + 1)) ||
223253
doc_data.doc_fallback.()
224254

225-
group = group_for_doc.(metadata) || doc_data.default_group
255+
group = normalize_group(group_for_doc.(metadata) || doc_data.default_group)
226256

227257
%ExDoc.DocNode{
228258
id: doc_data.id_key <> nil_or_name(name, arity),
@@ -314,4 +344,15 @@ defmodule ExDoc.Retriever do
314344
defp source_link(%{url_pattern: url_pattern, relative_path: path}, line) do
315345
url_pattern.(path, line)
316346
end
347+
348+
defp normalize_group(group) do
349+
case group do
350+
%{title: title, description: description}
351+
when is_binary(title) and (is_binary(description) or is_nil(description)) ->
352+
group
353+
354+
title when is_binary(title) when is_atom(title) ->
355+
%{title: title, description: nil}
356+
end
357+
end
317358
end

test/ex_doc/retriever/erlang_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ defmodule ExDoc.Retriever.ErlangTest do
5959
moduledoc_line: 2,
6060
moduledoc_file: moduledoc_file,
6161
docs: [equiv_function2, function1, function2],
62-
docs_groups: ["Types", "Callbacks", "Functions"],
62+
docs_groups: [%{title: "Types"}, %{title: "Callbacks"}, %{title: "Functions"}],
6363
group: nil,
6464
id: "mod",
6565
language: ExDoc.Language.Erlang,
@@ -156,7 +156,7 @@ defmodule ExDoc.Retriever.ErlangTest do
156156
moduledoc_line: 6,
157157
moduledoc_file: moduledoc_file,
158158
docs: [type, callback, function],
159-
docs_groups: ["Types", "Callbacks", "Functions"],
159+
docs_groups: [%{title: "Types"}, %{title: "Callbacks"}, %{title: "Functions"}],
160160
group: nil,
161161
id: "mod",
162162
language: ExDoc.Language.Erlang,
@@ -397,7 +397,7 @@ defmodule ExDoc.Retriever.ErlangTest do
397397
deprecated: nil,
398398
moduledoc_line: _,
399399
docs: [function1, function2],
400-
docs_groups: ["Types", "Callbacks", "Functions"],
400+
docs_groups: [%{title: "Types"}, %{title: "Callbacks"}, %{title: "Functions"}],
401401
group: nil,
402402
id: "mod",
403403
language: ExDoc.Language.Erlang,

test/ex_doc/retriever_test.exs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,38 @@ defmodule ExDoc.RetrieverTest do
108108
assert %{id: "baz/0", group: "c"} = baz
109109
end
110110

111+
test "function groups description use moduledoc :groups metadata", c do
112+
elixirc(c, ~S"""
113+
defmodule A do
114+
@moduledoc groups: [
115+
"c",
116+
%{title: "b", description: "text for b"}
117+
]
118+
119+
@doc group: "a"
120+
@callback foo() :: :ok
121+
122+
@doc group: "b"
123+
def bar(), do: :ok
124+
125+
@doc group: "c"
126+
def baz(), do: :ok
127+
end
128+
""")
129+
130+
config = %ExDoc.Config{}
131+
{[mod], []} = Retriever.docs_from_modules([A], config)
132+
133+
assert [
134+
%{description: nil, title: "c"},
135+
%{description: "text for b", title: "b"},
136+
%{description: nil, title: "Types"},
137+
%{description: nil, title: "Callbacks"},
138+
%{description: nil, title: "Functions"},
139+
%{description: nil, title: "a"}
140+
] = mod.docs_groups
141+
end
142+
111143
test "function annotations", c do
112144
elixirc(c, ~S"""
113145
defmodule A do

0 commit comments

Comments
 (0)