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