Skip to content

Commit

Permalink
Add optional function annotations (#1627)
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenvanvliet committed Nov 21, 2022
1 parent 69a74c0 commit b17fcd2
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 12 deletions.
5 changes: 4 additions & 1 deletion lib/ex_doc/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ defmodule ExDoc.Config do
def filter_modules(_module, _metadata), do: true
def before_closing_head_tag(_), do: ""
def before_closing_body_tag(_), do: ""
def annotations_for_docs(_), do: []

defstruct api_reference: true,
defstruct annotations_for_docs: &__MODULE__.annotations_for_docs/1,
api_reference: true,
apps: [],
assets: nil,
authors: nil,
Expand Down Expand Up @@ -44,6 +46,7 @@ defmodule ExDoc.Config do
version: nil

@type t :: %__MODULE__{
annotations_for_docs: (map() -> list()),
api_reference: boolean(),
apps: [atom()],
assets: nil | String.t(),
Expand Down
57 changes: 46 additions & 11 deletions lib/ex_doc/retriever.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,15 @@ defmodule ExDoc.Retriever do
config.groups_for_functions ++
[Callbacks: &(&1[:__doc__] == :callback), Functions: fn _ -> true end]

annotations_for_docs = config.annotations_for_docs

docs_groups = Enum.map(groups_for_functions, &elem(&1, 0))
function_docs = get_docs(module_data, source, groups_for_functions)
docs = function_docs ++ get_callbacks(module_data, source, groups_for_functions)
function_docs = get_docs(module_data, source, groups_for_functions, annotations_for_docs)

docs =
function_docs ++
get_callbacks(module_data, source, groups_for_functions, annotations_for_docs)

types = get_types(module_data, source)

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

## Function helpers

defp get_docs(module_data, source, groups_for_functions) do
defp get_docs(module_data, source, groups_for_functions, annotations_for_docs) do
{:docs_v1, _, _, _, _, _, doc_elements} = module_data.docs

nodes =
Expand All @@ -167,18 +173,38 @@ defmodule ExDoc.Retriever do
[]

function_data ->
[get_function(doc_element, function_data, source, module_data, groups_for_functions)]
[
get_function(
doc_element,
function_data,
source,
module_data,
groups_for_functions,
annotations_for_docs
)
]
end
end)

filter_defaults(nodes)
end

defp get_function(doc_element, function_data, source, module_data, groups_for_functions) do
defp get_function(
doc_element,
function_data,
source,
module_data,
groups_for_functions,
annotations_for_docs
) do
{:docs_v1, _, _, content_type, _, _, _} = module_data.docs
{{type, name, arity}, anno, signature, doc_content, metadata} = doc_element
doc_line = anno_line(anno)
annotations = annotations_from_metadata(metadata) ++ function_data.extra_annotations

annotations =
annotations_for_docs.(metadata) ++
annotations_from_metadata(metadata) ++ function_data.extra_annotations

line = function_data.line || doc_line
defaults = get_defaults(name, arity, Map.get(metadata, :defaults, 0))

Expand Down Expand Up @@ -226,17 +252,22 @@ defmodule ExDoc.Retriever do

## Callback helpers

defp get_callbacks(%{type: :behaviour} = module_data, source, groups_for_functions) do
defp get_callbacks(
%{type: :behaviour} = module_data,
source,
groups_for_functions,
annotations_for_docs
) do
{:docs_v1, _, _, _, _, _, docs} = module_data.docs

for {{kind, _, _}, _, _, _, _} = doc <- docs, kind in module_data.callback_types do
get_callback(doc, source, groups_for_functions, module_data)
get_callback(doc, source, groups_for_functions, module_data, annotations_for_docs)
end
end

defp get_callbacks(_, _, _), do: []
defp get_callbacks(_, _, _, _), do: []

defp get_callback(callback, source, groups_for_functions, module_data) do
defp get_callback(callback, source, groups_for_functions, module_data, annotations_for_docs) do
callback_data = module_data.language.callback_data(callback, module_data)

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

signature = signature(callback_data.signature)
specs = callback_data.specs
annotations = callback_data.extra_annotations ++ annotations_from_metadata(metadata)

annotations =
annotations_for_docs.(metadata) ++
callback_data.extra_annotations ++ annotations_from_metadata(metadata)

doc_ast = doc_ast(content_type, doc, file: source.path, line: doc_line + 1)

metadata = Map.put(metadata, :__doc__, :callback)
Expand Down
51 changes: 51 additions & 0 deletions test/ex_doc/retriever_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,57 @@ defmodule ExDoc.RetrieverTest do
assert %{id: "baz/0", group: :"Group 2"} = baz
end

test "custom function annotations", c do
elixirc(c, ~S"""
defmodule A do
@doc since: "1.0.0"
@doc deprecated: "deprecation message"
@doc foo: true
def foo(), do: :ok
end
""")

[mod] =
Retriever.docs_from_modules([A], %ExDoc.Config{
annotations_for_docs: fn metadata ->
if metadata[:foo] do
[:baz]
else
[]
end
end
})

[foo] = mod.docs
assert foo.id == "foo/0"
assert foo.annotations == [:baz, "since 1.0.0"]
assert foo.deprecated == "deprecation message"
end

test "custom callback annotations", c do
elixirc(c, ~S"""
defmodule A do
@doc foo: true
@callback callback_name() :: :ok
end
""")

[mod] =
Retriever.docs_from_modules([A], %ExDoc.Config{
annotations_for_docs: fn metadata ->
if metadata[:foo] do
[:baz]
else
[]
end
end
})

[foo] = mod.docs

assert foo.annotations == [:baz]
end

test "nesting", c do
elixirc(c, ~S"""
defmodule Nesting.Prefix.B.A do
Expand Down

0 comments on commit b17fcd2

Please sign in to comment.