From b17fcd278abe4f2f62de816b957da8fc83dcc18c Mon Sep 17 00:00:00 2001 From: Maarten van Vliet Date: Mon, 21 Nov 2022 11:45:25 +0100 Subject: [PATCH] Add optional function annotations (#1627) --- lib/ex_doc/config.ex | 5 ++- lib/ex_doc/retriever.ex | 57 +++++++++++++++++++++++++++------- test/ex_doc/retriever_test.exs | 51 ++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/lib/ex_doc/config.ex b/lib/ex_doc/config.ex index f04ac2b97..6ebee8755 100644 --- a/lib/ex_doc/config.ex +++ b/lib/ex_doc/config.ex @@ -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, @@ -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(), diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex index 7759b7737..0f48657aa 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -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) @@ -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 = @@ -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)) @@ -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 @@ -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) diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs index 435bf2b1a..0c46b0c2e 100644 --- a/test/ex_doc/retriever_test.exs +++ b/test/ex_doc/retriever_test.exs @@ -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