Skip to content

Commit b17fcd2

Browse files
Add optional function annotations (#1627)
1 parent 69a74c0 commit b17fcd2

File tree

3 files changed

+101
-12
lines changed

3 files changed

+101
-12
lines changed

lib/ex_doc/config.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ defmodule ExDoc.Config do
66
def filter_modules(_module, _metadata), do: true
77
def before_closing_head_tag(_), do: ""
88
def before_closing_body_tag(_), do: ""
9+
def annotations_for_docs(_), do: []
910

10-
defstruct api_reference: true,
11+
defstruct annotations_for_docs: &__MODULE__.annotations_for_docs/1,
12+
api_reference: true,
1113
apps: [],
1214
assets: nil,
1315
authors: nil,
@@ -44,6 +46,7 @@ defmodule ExDoc.Config do
4446
version: nil
4547

4648
@type t :: %__MODULE__{
49+
annotations_for_docs: (map() -> list()),
4750
api_reference: boolean(),
4851
apps: [atom()],
4952
assets: nil | String.t(),

lib/ex_doc/retriever.ex

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,15 @@ defmodule ExDoc.Retriever do
108108
config.groups_for_functions ++
109109
[Callbacks: &(&1[:__doc__] == :callback), Functions: fn _ -> true end]
110110

111+
annotations_for_docs = config.annotations_for_docs
112+
111113
docs_groups = Enum.map(groups_for_functions, &elem(&1, 0))
112-
function_docs = get_docs(module_data, source, groups_for_functions)
113-
docs = function_docs ++ get_callbacks(module_data, source, groups_for_functions)
114+
function_docs = get_docs(module_data, source, groups_for_functions, annotations_for_docs)
115+
116+
docs =
117+
function_docs ++
118+
get_callbacks(module_data, source, groups_for_functions, annotations_for_docs)
119+
114120
types = get_types(module_data, source)
115121

116122
metadata = Map.put(metadata, :__doc__, module_data.type)
@@ -157,7 +163,7 @@ defmodule ExDoc.Retriever do
157163

158164
## Function helpers
159165

160-
defp get_docs(module_data, source, groups_for_functions) do
166+
defp get_docs(module_data, source, groups_for_functions, annotations_for_docs) do
161167
{:docs_v1, _, _, _, _, _, doc_elements} = module_data.docs
162168

163169
nodes =
@@ -167,18 +173,38 @@ defmodule ExDoc.Retriever do
167173
[]
168174

169175
function_data ->
170-
[get_function(doc_element, function_data, source, module_data, groups_for_functions)]
176+
[
177+
get_function(
178+
doc_element,
179+
function_data,
180+
source,
181+
module_data,
182+
groups_for_functions,
183+
annotations_for_docs
184+
)
185+
]
171186
end
172187
end)
173188

174189
filter_defaults(nodes)
175190
end
176191

177-
defp get_function(doc_element, function_data, source, module_data, groups_for_functions) do
192+
defp get_function(
193+
doc_element,
194+
function_data,
195+
source,
196+
module_data,
197+
groups_for_functions,
198+
annotations_for_docs
199+
) do
178200
{:docs_v1, _, _, content_type, _, _, _} = module_data.docs
179201
{{type, name, arity}, anno, signature, doc_content, metadata} = doc_element
180202
doc_line = anno_line(anno)
181-
annotations = annotations_from_metadata(metadata) ++ function_data.extra_annotations
203+
204+
annotations =
205+
annotations_for_docs.(metadata) ++
206+
annotations_from_metadata(metadata) ++ function_data.extra_annotations
207+
182208
line = function_data.line || doc_line
183209
defaults = get_defaults(name, arity, Map.get(metadata, :defaults, 0))
184210

@@ -226,17 +252,22 @@ defmodule ExDoc.Retriever do
226252

227253
## Callback helpers
228254

229-
defp get_callbacks(%{type: :behaviour} = module_data, source, groups_for_functions) do
255+
defp get_callbacks(
256+
%{type: :behaviour} = module_data,
257+
source,
258+
groups_for_functions,
259+
annotations_for_docs
260+
) do
230261
{:docs_v1, _, _, _, _, _, docs} = module_data.docs
231262

232263
for {{kind, _, _}, _, _, _, _} = doc <- docs, kind in module_data.callback_types do
233-
get_callback(doc, source, groups_for_functions, module_data)
264+
get_callback(doc, source, groups_for_functions, module_data, annotations_for_docs)
234265
end
235266
end
236267

237-
defp get_callbacks(_, _, _), do: []
268+
defp get_callbacks(_, _, _, _), do: []
238269

239-
defp get_callback(callback, source, groups_for_functions, module_data) do
270+
defp get_callback(callback, source, groups_for_functions, module_data, annotations_for_docs) do
240271
callback_data = module_data.language.callback_data(callback, module_data)
241272

242273
{:docs_v1, _, _, content_type, _, _, _} = module_data.docs
@@ -245,7 +276,11 @@ defmodule ExDoc.Retriever do
245276

246277
signature = signature(callback_data.signature)
247278
specs = callback_data.specs
248-
annotations = callback_data.extra_annotations ++ annotations_from_metadata(metadata)
279+
280+
annotations =
281+
annotations_for_docs.(metadata) ++
282+
callback_data.extra_annotations ++ annotations_from_metadata(metadata)
283+
249284
doc_ast = doc_ast(content_type, doc, file: source.path, line: doc_line + 1)
250285

251286
metadata = Map.put(metadata, :__doc__, :callback)

test/ex_doc/retriever_test.exs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,57 @@ defmodule ExDoc.RetrieverTest do
9191
assert %{id: "baz/0", group: :"Group 2"} = baz
9292
end
9393

94+
test "custom function annotations", c do
95+
elixirc(c, ~S"""
96+
defmodule A do
97+
@doc since: "1.0.0"
98+
@doc deprecated: "deprecation message"
99+
@doc foo: true
100+
def foo(), do: :ok
101+
end
102+
""")
103+
104+
[mod] =
105+
Retriever.docs_from_modules([A], %ExDoc.Config{
106+
annotations_for_docs: fn metadata ->
107+
if metadata[:foo] do
108+
[:baz]
109+
else
110+
[]
111+
end
112+
end
113+
})
114+
115+
[foo] = mod.docs
116+
assert foo.id == "foo/0"
117+
assert foo.annotations == [:baz, "since 1.0.0"]
118+
assert foo.deprecated == "deprecation message"
119+
end
120+
121+
test "custom callback annotations", c do
122+
elixirc(c, ~S"""
123+
defmodule A do
124+
@doc foo: true
125+
@callback callback_name() :: :ok
126+
end
127+
""")
128+
129+
[mod] =
130+
Retriever.docs_from_modules([A], %ExDoc.Config{
131+
annotations_for_docs: fn metadata ->
132+
if metadata[:foo] do
133+
[:baz]
134+
else
135+
[]
136+
end
137+
end
138+
})
139+
140+
[foo] = mod.docs
141+
142+
assert foo.annotations == [:baz]
143+
end
144+
94145
test "nesting", c do
95146
elixirc(c, ~S"""
96147
defmodule Nesting.Prefix.B.A do

0 commit comments

Comments
 (0)