diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex
index c2d7a9dfe..a0faa8215 100644
--- a/lib/ex_doc/formatter/html.ex
+++ b/lib/ex_doc/formatter/html.ex
@@ -10,7 +10,7 @@ defmodule ExDoc.Formatter.HTML do
@doc """
Generate HTML documentation for the given modules.
"""
- @spec run(list, ExDoc.Config.t()) :: String.t()
+ @spec run([module], ExDoc.Config.t()) :: String.t()
def run(project_nodes, config) when is_map(config) do
config = normalize_config(config)
config = %{config | output: Path.expand(config.output)}
diff --git a/lib/ex_doc/formatter/html/autolink.ex b/lib/ex_doc/formatter/html/autolink.ex
index ce9e07437..5ff55822a 100644
--- a/lib/ex_doc/formatter/html/autolink.ex
+++ b/lib/ex_doc/formatter/html/autolink.ex
@@ -95,6 +95,7 @@ defmodule ExDoc.Formatter.HTML.Autolink do
%{
aliases: aliases,
+ config: config,
docs_refs: docs_refs ++ types_refs,
extension: extension,
lib_dirs: lib_dirs,
@@ -121,7 +122,7 @@ defmodule ExDoc.Formatter.HTML.Autolink do
def project_doc(nil, _id, _compiled), do: nil
def project_doc(string, id, compiled) when is_binary(string) and is_map(compiled) do
- config =
+ options =
compiled
|> Map.put(:id, id)
|> Map.put_new(:module_id, nil)
@@ -131,7 +132,7 @@ defmodule ExDoc.Formatter.HTML.Autolink do
string =
Enum.reduce(@regexes, string, fn {kind, language, link_type}, acc ->
- link(acc, language, kind, link_type, config)
+ link(acc, language, kind, link_type, options)
end)
postprocess(string)
@@ -340,12 +341,14 @@ defmodule ExDoc.Formatter.HTML.Autolink do
#
# It accepts a list of `options` used in the replacement functions.
# - `:aliases
- # - `:docs_refs`
+ # - `:config`
+ # - `:docs_refs` - Docs and types references
# - `:extension` - Default value is `".html"`
# - `:lib_dirs`
# - `:locals` - A list of local functions
# - `:module_id` - Module of the current doc. Default value is `nil`
# - `:modules_refs` - List of modules available
+ # - `:skip_undefined_reference_warnings_on`
#
# Internal options:
# - `:preprocess?` - `true` or `false`. Do preprocessing and postprocessing, such as replacing backticks
@@ -380,7 +383,16 @@ defmodule ExDoc.Formatter.HTML.Autolink do
pmfa = {_prefix, module, function, arity} = split_match(kind, match)
text = default_text(":", link_type, pmfa, text)
- if doc = module_docs(:erlang, module, lib_dirs) do
+ module_or_mfa =
+ case kind do
+ :module ->
+ module
+
+ :function ->
+ {module, function, arity}
+ end
+
+ if doc = module_docs(:erlang, module_or_mfa, lib_dirs) do
case kind do
:module ->
"[#{text}](#{doc}#{module}.html)"
@@ -467,7 +479,7 @@ defmodule ExDoc.Formatter.HTML.Autolink do
all
- doc = module_docs(:elixir, module, lib_dirs) ->
+ doc = module_docs(:elixir, {module, function, arity}, lib_dirs) ->
"[#{text}](#{doc}#{module}.html##{prefix}#{enc_h(function)}/#{arity})"
true ->
@@ -539,9 +551,15 @@ defmodule ExDoc.Formatter.HTML.Autolink do
defp default_lib_dirs(:erlang),
do: erlang_lib_dirs()
+ defp module_docs(:elixir, {module, function, arity}, lib_dirs),
+ do: lib_dirs_to_doc({"Elixir." <> module, function, arity}, lib_dirs)
+
defp module_docs(:elixir, module, lib_dirs),
do: lib_dirs_to_doc("Elixir." <> module, lib_dirs)
+ defp module_docs(:erlang, {module, function, arity}, lib_dirs),
+ do: lib_dirs_to_doc({module, function, arity}, lib_dirs)
+
defp module_docs(:erlang, module, lib_dirs),
do: lib_dirs_to_doc(module, lib_dirs)
@@ -588,7 +606,16 @@ defmodule ExDoc.Formatter.HTML.Autolink do
defp doc_prefix(%{type: c}) when c in [:callback, :macrocallback], do: "c:"
defp doc_prefix(%{type: _}), do: ""
- defp lib_dirs_to_doc(module, lib_dirs) do
+ defp lib_dirs_to_doc(module, lib_dirs) when is_atom(module) do
+ lib_dirs_to_doc({"#{module}", "", ""}, lib_dirs)
+ end
+
+ defp lib_dirs_to_doc(module, lib_dirs) when is_binary(module) do
+ lib_dirs_to_doc({module, "", ""}, lib_dirs)
+ end
+
+ defp lib_dirs_to_doc({module, function, arity}, lib_dirs)
+ when is_binary(module) and is_binary(function) and is_binary(arity) do
case :code.where_is_file('#{module}.beam') do
:non_existing ->
nil
@@ -600,7 +627,7 @@ defmodule ExDoc.Formatter.HTML.Autolink do
|> Enum.filter(fn {lib_dir, _} -> String.starts_with?(path, lib_dir) end)
|> Enum.sort_by(fn {lib_dir, _} -> -byte_size(lib_dir) end)
|> case do
- [{_, doc} | _] -> doc
+ [{_doc_path, doc} | _] -> doc
_ -> nil
end
end
diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex
index 795352379..a50dfdda7 100644
--- a/lib/ex_doc/retriever.ex
+++ b/lib/ex_doc/retriever.ex
@@ -11,17 +11,25 @@ defmodule ExDoc.Retriever do
alias ExDoc.Retriever.Error
@doc """
- Extract documentation from all modules in the specified directory or directories.
+ Extracts documentation from all modules in the specified directory or directories.
"""
@spec docs_from_dir(Path.t() | [Path.t()], ExDoc.Config.t()) :: [ExDoc.ModuleNode.t()]
def docs_from_dir(dir, config) when is_binary(dir) do
- pattern = if config.filter_prefix, do: "Elixir.#{config.filter_prefix}*.beam", else: "*.beam"
- files = Path.wildcard(Path.expand(pattern, dir))
- docs_from_files(files, config)
+ dir
+ |> __docs_from_dir__(config)
+ |> reject_docs_with_moduledoc_false()
end
def docs_from_dir(dirs, config) when is_list(dirs) do
- Enum.flat_map(dirs, &docs_from_dir(&1, config))
+ dirs
+ |> Enum.flat_map(&__docs_from_dir__(&1, config))
+ |> reject_docs_with_moduledoc_false()
+ end
+
+ defp __docs_from_dir__(dir, config) do
+ dir
+ |> files_from_pattern(config)
+ |> docs_from_files(config)
end
@doc """
@@ -35,20 +43,44 @@ defmodule ExDoc.Retriever do
end
@doc """
- Extract documentation from all modules in the list `modules`
+ Extracts documentation from all modules in the list `modules`
"""
- @spec docs_from_modules([atom], ExDoc.Config.t()) :: [ExDoc.ModuleNode.t()]
+ @spec docs_from_modules([module], ExDoc.Config.t()) :: [ExDoc.ModuleNode.t()]
def docs_from_modules(modules, config) when is_list(modules) do
modules
|> Enum.flat_map(&get_module(&1, config))
+ |> reject_docs_with_moduledoc_false()
|> Enum.sort_by(fn module ->
{GroupMatcher.group_index(config.groups_for_modules, module.group), module.id}
end)
end
+ ## Helpers
+
+ defp reject_docs_with_moduledoc_false(docs) do
+ Enum.reject(docs, fn
+ {:module_doc_hidden, _} -> true
+ _ -> false
+ end)
+ end
+
+ defp files_from_pattern(dir, config) do
+ pattern =
+ if config.filter_prefix do
+ "Elixir.#{config.filter_prefix}*.beam"
+ else
+ "*.beam"
+ end
+
+ pattern
+ |> Path.expand(dir)
+ |> Path.wildcard()
+ end
+
defp filename_to_module(name) do
- name = Path.basename(name, ".beam")
- String.to_atom(name)
+ name
+ |> Path.basename(".beam")
+ |> String.to_atom()
end
# Get all the information from the module and compile
@@ -60,10 +92,15 @@ defmodule ExDoc.Retriever do
raise Error, "module #{inspect(module)} is not defined/available"
end
- if docs_chunk = docs_chunk(module) do
- generate_node(module, docs_chunk, config)
- else
- []
+ case docs_chunk(module) do
+ false ->
+ []
+
+ :hidden ->
+ %{module_doc_hidden: module}
+
+ docs_chunk ->
+ generate_node(module, docs_chunk, config)
end
end
@@ -89,7 +126,7 @@ defmodule ExDoc.Retriever do
if function_exported?(module, :__info__, 1) do
case Code.fetch_docs(module) do
{:docs_v1, _, _, _, :hidden, _, _} ->
- false
+ :hidden
{:docs_v1, _, _, _, _, _, _} = docs ->
docs
diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs
index 8db6eebc0..28fc8f3e3 100644
--- a/test/ex_doc/retriever_test.exs
+++ b/test/ex_doc/retriever_test.exs
@@ -51,6 +51,13 @@ defmodule ExDoc.RetrieverTest do
"""
end
+ test "returns modules with @doc false" do
+ assert [CompiledWithDocs, CompiledWithDocs] ==
+ ["CompiledWithDocs", "ModuledocFalse", "CompiledWithDocs"]
+ |> docs_from_files()
+ |> Enum.map(& &1.module)
+ end
+
test "returns module group" do
[module_node] =
docs_from_files(["CompiledWithDocs"], groups_for_modules: [Group: [CompiledWithDocs]])
diff --git a/test/fixtures/moduledoc_false.ex b/test/fixtures/moduledoc_false.ex
new file mode 100644
index 000000000..fb79dca3a
--- /dev/null
+++ b/test/fixtures/moduledoc_false.ex
@@ -0,0 +1,10 @@
+defmodule ModuledocFalse do
+ @moduledoc false
+
+ @doc """
+ Foo is the new bar
+ """
+ def foo do
+ :foo
+ end
+end