diff --git a/lib/ex_doc.ex b/lib/ex_doc.ex index 28b919b4d..9a5a382b7 100644 --- a/lib/ex_doc.ex +++ b/lib/ex_doc.ex @@ -35,6 +35,7 @@ defmodule ExDoc do deps: [], extra_section: nil, extras: [], + groups_for_extras: [], filter_prefix: nil, formatter: @default.formatter, formatter_opts: [], @@ -42,6 +43,7 @@ defmodule ExDoc do language: @default.language, logo: nil, main: nil, + groups_for_modules: [], output: @default.output, project: nil, retriever: @default.retriever, @@ -63,6 +65,7 @@ defmodule ExDoc do deps: [{ebin_path :: String.t, doc_url :: String.t}], extra_section: nil | String.t, extras: list(), + groups_for_extras: keyword(), filter_prefix: nil | String.t, formatter: nil | String.t, formatter_opts: Keyword.t, @@ -70,6 +73,7 @@ defmodule ExDoc do language: String.t, logo: nil | Path.t, main: nil | String.t, + groups_for_modules: keyword(), output: nil | Path.t, project: nil | String.t, retriever: :atom, diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex index ec88992ab..e33721c55 100644 --- a/lib/ex_doc/formatter/html.ex +++ b/lib/ex_doc/formatter/html.ex @@ -4,7 +4,7 @@ defmodule ExDoc.Formatter.HTML do """ alias __MODULE__.{Assets, Autolink, Templates} - alias ExDoc.Markdown + alias ExDoc.{Markdown, GroupMatcher} @main "api-reference" @@ -178,32 +178,34 @@ defmodule ExDoc.Formatter.HTML do def build_extras(project_nodes, config, extension) do config.extras |> Enum.map(&Task.async(fn -> - build_extra(&1, project_nodes, extension) + build_extra(&1, project_nodes, extension, config) end)) |> Enum.map(&Task.await(&1, :infinity)) end - defp build_extra({input, options}, project_nodes, extension) do + defp build_extra({input, options}, project_nodes, extension, config) do input = to_string(input) id = options[:filename] || input |> input_to_title() |> title_to_id() - build_extra(input, id, options[:title], options[:group], project_nodes, extension) + build_extra(input, id, options[:title], options[:group], project_nodes, extension, config) end - defp build_extra(input, project_nodes, extension) do + defp build_extra(input, project_nodes, extension, config) do id = input |> input_to_title() |> title_to_id() - build_extra(input, id, nil, "", project_nodes, extension) + build_extra(input, id, nil, "", project_nodes, extension, config) end - defp build_extra(input, id, title, group, project_nodes, extension) do + defp build_extra(input, id, title, group, project_nodes, extension, config) do if valid_extension_name?(input) do content = input |> File.read!() |> Autolink.project_doc(project_nodes, nil, extension) + group_pattern = GroupMatcher.match_extra config.groups_for_extras, input + html_content = Markdown.to_html(content, file: input, line: 1) title = title || extract_title(html_content) || input_to_title(input) - %{id: id, title: title, group: group, content: html_content} + %{id: id, title: title, group: group_pattern || group, content: html_content} else raise ArgumentError, "file format not recognized, allowed format is: .md" end diff --git a/lib/ex_doc/formatter/html/templates.ex b/lib/ex_doc/formatter/html/templates.ex index ef883df42..316dbf45f 100644 --- a/lib/ex_doc/formatter/html/templates.ex +++ b/lib/ex_doc/formatter/html/templates.ex @@ -176,9 +176,9 @@ defmodule ExDoc.Formatter.HTML.Templates do |> Enum.map_join(",", &sidebar_items_by_type/1) if items == "" do - ~s/{"id":"#{module_node.id}","title":"#{module_node.title}"}/ + ~s/{"id":"#{module_node.id}","title":"#{module_node.title}","group":"#{module_node.group}"}/ else - ~s/{"id":"#{module_node.id}","title":"#{module_node.title}",#{items}}/ + ~s/{"id":"#{module_node.id}","title":"#{module_node.title}","group":"#{module_node.group}",#{items}}/ end end diff --git a/lib/ex_doc/group_matcher.ex b/lib/ex_doc/group_matcher.ex new file mode 100644 index 000000000..652eac766 --- /dev/null +++ b/lib/ex_doc/group_matcher.ex @@ -0,0 +1,48 @@ +defmodule ExDoc.GroupMatcher do + @moduledoc """ + Match modules or extra pages to groups. + + Matching does happen by explicitly matching names or using regular expressions. + """ + @type pattern :: Regex.t | module() | String.t + @type patterns :: pattern | [pattern] + @type group_patterns :: keyword(patterns) + + @doc """ + Does try to find a matching group for the given module name or id + """ + @spec match_module(group_patterns, module(), String.t) :: String.t | nil + def match_module(group_patterns, module, id) do + match_group_patterns(group_patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, id) + string when is_binary(string) -> id == string + atom -> atom == module + end + end) + end + + @doc """ + Does try to find a matching group for the given extra filename + """ + @spec match_extra(group_patterns, String.t) :: String.t | nil + def match_extra(group_patterns, filename) do + match_group_patterns(group_patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, filename) + string when is_binary(string) -> filename == string + end + end) + end + + defp match_group_patterns(group_patterns, matcher) do + Enum.find_value(group_patterns, fn {group, patterns} -> + patterns = List.wrap patterns + match_patterns(patterns, matcher) && Atom.to_string(group) + end) + end + + defp match_patterns(patterns, matcher) do + Enum.any?(patterns, matcher) || nil + end +end diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex index 1383ddf78..7e6359e50 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -3,13 +3,15 @@ defmodule ExDoc.ModuleNode do Structure that represents a *module* """ - defstruct id: nil, title: nil, module: nil, doc: nil, doc_line: nil, - docs: [], typespecs: [], source_path: nil, source_url: nil, type: nil + defstruct id: nil, title: nil, module: nil, group: nil, doc: nil, + doc_line: nil, docs: [], typespecs: [], source_path: nil, + source_url: nil, type: nil @type t :: %__MODULE__{ id: nil | String.t, title: nil | String.t, module: nil | String.t, + group: nil | String.t, docs: list(), doc: nil | String.t, doc_line: non_neg_integer(), @@ -81,6 +83,7 @@ defmodule ExDoc.Retriever do Functions to extract documentation information from modules. """ + alias ExDoc.GroupMatcher alias ExDoc.Retriever.Error alias Kernel.Typespec @@ -115,7 +118,10 @@ defmodule ExDoc.Retriever do modules |> Enum.map(&get_module(&1, config)) |> Enum.filter(&(&1)) - |> Enum.sort(&(&1.id <= &2.id)) + |> Enum.sort_by(fn module -> + group_index = Enum.find_index(config.groups_for_modules, fn {k, _v} -> Atom.to_string(k) == module.group end) + {group_index || -1, module.id} + end) end defp filename_to_module(name) do @@ -171,10 +177,13 @@ defmodule ExDoc.Retriever do {title, id} = module_title_and_id(module, type) + module_group = GroupMatcher.match_module config.groups_for_modules, module, id + %ExDoc.ModuleNode{ id: id, title: title, module: module_info.name, + group: module_group, type: type, docs: docs, doc: moduledoc, diff --git a/test/ex_doc/formatter/html/templates_test.exs b/test/ex_doc/formatter/html/templates_test.exs index 313c10573..f7d89a401 100644 --- a/test/ex_doc/formatter/html/templates_test.exs +++ b/test/ex_doc/formatter/html/templates_test.exs @@ -14,8 +14,8 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do "http://elixir-lang.org" end - defp doc_config do - %ExDoc.Config{ + defp doc_config(config \\ []) do + default = %ExDoc.Config{ project: "Elixir", version: "1.0.1", source_root: File.cwd!, @@ -24,6 +24,8 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do source_url: source_url(), output: "test/tmp/html_templates" } + + struct(default, config) end defp get_module_page(names) do @@ -211,6 +213,15 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do assert content =~ ~r("id":"CompiledWithDocs.Nested")ms end + test "list_page outputs groups for the given nodes" do + names = [CompiledWithDocs, CompiledWithDocs.Nested] + group_mapping = [groups_for_modules: ["Group": [CompiledWithDocs]]] + nodes = ExDoc.Retriever.docs_from_modules(names, doc_config(group_mapping)) + content = Templates.create_sidebar_items(%{modules: nodes}, []) + + assert content =~ ~r("id":"CompiledWithDocs","title":"CompiledWithDocs","group":"Group")ms + end + ## MODULES test "module_page outputs the functions and docstrings" do diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index dfc0c1946..fa9b94644 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -249,13 +249,23 @@ defmodule ExDoc.Formatter.HTMLTest do assert content =~ ~r{