From a59e346cb7ac42985c05dc72f06e273d3584ce0a Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Wed, 27 Sep 2017 11:12:38 +0200 Subject: [PATCH 1/8] Add grouping of modules --- lib/ex_doc.ex | 2 ++ lib/ex_doc/formatter/html/templates.ex | 4 +-- lib/ex_doc/retriever.ex | 31 +++++++++++++++++++--- test/ex_doc/retriever_test.exs | 27 ++++++++++++++++++- test/fixtures/grouped_explicitly_atom.ex | 3 +++ test/fixtures/grouped_explicitly_string.ex | 3 +++ test/fixtures/grouped_implicitly_regex.ex | 3 +++ 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/grouped_explicitly_atom.ex create mode 100644 test/fixtures/grouped_explicitly_string.ex create mode 100644 test/fixtures/grouped_implicitly_regex.ex diff --git a/lib/ex_doc.ex b/lib/ex_doc.ex index 28b919b4d..bad0eb53b 100644 --- a/lib/ex_doc.ex +++ b/lib/ex_doc.ex @@ -42,6 +42,7 @@ defmodule ExDoc do language: @default.language, logo: nil, main: nil, + module_groups: [], output: @default.output, project: nil, retriever: @default.retriever, @@ -70,6 +71,7 @@ defmodule ExDoc do language: String.t, logo: nil | Path.t, main: nil | String.t, + module_groups: list(), output: nil | Path.t, project: nil | String.t, retriever: :atom, 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/retriever.ex b/lib/ex_doc/retriever.ex index 1383ddf78..222deda9b 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(), @@ -115,7 +117,7 @@ defmodule ExDoc.Retriever do modules |> Enum.map(&get_module(&1, config)) |> Enum.filter(&(&1)) - |> Enum.sort(&(&1.id <= &2.id)) + |> Enum.sort_by(&({&1.group, &1.id})) end defp filename_to_module(name) do @@ -157,6 +159,7 @@ defmodule ExDoc.Retriever do end defp generate_node(module, type, config) do + module_group_patterns = config.module_groups source_url = config.source_url_pattern source_path = source_path(module, config) source = %{url: source_url, path: source_path} @@ -171,10 +174,13 @@ defmodule ExDoc.Retriever do {title, id} = module_title_and_id(module, type) + module_group = module_group(module, module_group_patterns) + %ExDoc.ModuleNode{ id: id, title: title, module: module_info.name, + group: module_group, type: type, docs: docs, doc: moduledoc, @@ -458,6 +464,25 @@ defmodule ExDoc.Retriever do {id, id} end + defp module_group(module, group_patterns) do + Enum.find_value(group_patterns, fn {group, patterns} -> + patterns = List.wrap patterns + module_in_patterns(module, patterns) && Atom.to_string(group) + end) + end + + defp module_in_patterns(module, patterns) do + "Elixir." <> module_string = Atom.to_string module + + Enum.any?(patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, module_string) + string when is_binary(string) -> module_string === string + atom -> atom === module + end + end) + end + defp module_id(module) do case inspect(module) do ":" <> inspected -> inspected diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs index 1df8dc99c..50e890409 100644 --- a/test/ex_doc/retriever_test.exs +++ b/test/ex_doc/retriever_test.exs @@ -5,7 +5,15 @@ defmodule ExDoc.RetrieverTest do defp docs_from_files(names, url_pattern \\ "http://example.com/%{path}#L%{line}") do files = Enum.map names, fn(n) -> "test/tmp/Elixir.#{n}.beam" end - config = %ExDoc.Config{source_url_pattern: url_pattern, source_root: File.cwd!} + config = %ExDoc.Config{ + source_url_pattern: url_pattern, + source_root: File.cwd!, + module_groups: [ + "Group Atom": [GroupedExplicitlyAtom], + "Group String": ["GroupedExplicitlyString"], + "Group Implicit": ~r/GroupedImplicitly.?/ + ] + } Retriever.docs_from_files(files, config) end @@ -44,6 +52,23 @@ defmodule ExDoc.RetrieverTest do assert module_node.module == CompiledWithDocs end + describe "docs_from_files returns the group" do + test "atom" do + [module_node] = docs_from_files ["GroupedExplicitlyAtom"] + assert module_node.group == "Group Atom" + end + + test "string" do + [module_node] = docs_from_files ["GroupedExplicitlyString"] + assert module_node.group == "Group String" + end + + test "regex" do + [module_node] = docs_from_files ["GroupedImplicitlyRegex"] + assert module_node.group == "Group Implicit" + end + end + test "docs_from_files returns the moduledoc info" do [module_node] = docs_from_files ["CompiledWithDocs"] assert module_node.doc == "moduledoc\n\n\#\# Example ☃ Unicode > escaping\n CompiledWithDocs.example\n\n### Example H3 heading\n\nexample\n" diff --git a/test/fixtures/grouped_explicitly_atom.ex b/test/fixtures/grouped_explicitly_atom.ex new file mode 100644 index 000000000..a02b237d8 --- /dev/null +++ b/test/fixtures/grouped_explicitly_atom.ex @@ -0,0 +1,3 @@ +defmodule GroupedExplicitlyAtom do + @moduledoc false +end diff --git a/test/fixtures/grouped_explicitly_string.ex b/test/fixtures/grouped_explicitly_string.ex new file mode 100644 index 000000000..bdcc3ceee --- /dev/null +++ b/test/fixtures/grouped_explicitly_string.ex @@ -0,0 +1,3 @@ +defmodule GroupedExplicitlyString do + @moduledoc false +end diff --git a/test/fixtures/grouped_implicitly_regex.ex b/test/fixtures/grouped_implicitly_regex.ex new file mode 100644 index 000000000..75694fc8f --- /dev/null +++ b/test/fixtures/grouped_implicitly_regex.ex @@ -0,0 +1,3 @@ +defmodule GroupedImplicitlyRegex do + @moduledoc false +end From f4d04fc46f8049f4c323172048b176038aedaeed Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Wed, 27 Sep 2017 20:42:31 +0200 Subject: [PATCH 2/8] =?UTF-8?q?Update=20retriever=20/=20tests=20according?= =?UTF-8?q?=20to=20Jos=C3=A9's=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/ex_doc/retriever.ex | 8 ++--- test/ex_doc/retriever_test.exs | 38 +++++++++++----------- test/fixtures/grouped_explicitly_atom.ex | 3 -- test/fixtures/grouped_explicitly_string.ex | 3 -- test/fixtures/grouped_implicitly_regex.ex | 3 -- 5 files changed, 23 insertions(+), 32 deletions(-) delete mode 100644 test/fixtures/grouped_explicitly_atom.ex delete mode 100644 test/fixtures/grouped_explicitly_string.ex delete mode 100644 test/fixtures/grouped_implicitly_regex.ex diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex index 222deda9b..e0797f45b 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -472,13 +472,13 @@ defmodule ExDoc.Retriever do end defp module_in_patterns(module, patterns) do - "Elixir." <> module_string = Atom.to_string module + id = module_id module Enum.any?(patterns, fn pattern -> case pattern do - %Regex{} = regex -> Regex.match?(regex, module_string) - string when is_binary(string) -> module_string === string - atom -> atom === module + %Regex{} = regex -> Regex.match?(regex, id) + string when is_binary(string) -> id == string + atom -> atom == module end end) end diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs index 50e890409..8309260a2 100644 --- a/test/ex_doc/retriever_test.exs +++ b/test/ex_doc/retriever_test.exs @@ -3,17 +3,12 @@ defmodule ExDoc.RetrieverTest do alias ExDoc.Retriever - defp docs_from_files(names, url_pattern \\ "http://example.com/%{path}#L%{line}") do + defp docs_from_files(names, config \\ []) do files = Enum.map names, fn(n) -> "test/tmp/Elixir.#{n}.beam" end - config = %ExDoc.Config{ - source_url_pattern: url_pattern, - source_root: File.cwd!, - module_groups: [ - "Group Atom": [GroupedExplicitlyAtom], - "Group String": ["GroupedExplicitlyString"], - "Group Implicit": ~r/GroupedImplicitly.?/ - ] - } + config = + %ExDoc.Config{source_url_pattern: "http://example.com/%{path}#L%{line}", source_root: File.cwd!} + |> struct(config) + Retriever.docs_from_files(files, config) end @@ -54,18 +49,24 @@ defmodule ExDoc.RetrieverTest do describe "docs_from_files returns the group" do test "atom" do - [module_node] = docs_from_files ["GroupedExplicitlyAtom"] - assert module_node.group == "Group Atom" + [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + "Group": [CompiledWithDocs] + ] + assert module_node.group == "Group" end test "string" do - [module_node] = docs_from_files ["GroupedExplicitlyString"] - assert module_node.group == "Group String" + [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + "Group": ["CompiledWithDocs"] + ] + assert module_node.group == "Group" end test "regex" do - [module_node] = docs_from_files ["GroupedImplicitlyRegex"] - assert module_node.group == "Group Implicit" + [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + "Group": ~r/^CompiledWith.?/ + ] + assert module_node.group == "Group" end end @@ -160,7 +161,7 @@ defmodule ExDoc.RetrieverTest do end test "docs_from_files returns the source" do - [module_node] = docs_from_files ["CompiledWithDocs"], "http://foo.com/bar/%{path}#L%{line}" + [module_node] = docs_from_files ["CompiledWithDocs"], source_url_pattern: "http://foo.com/bar/%{path}#L%{line}" assert module_node.source_url == "http://foo.com/bar/test/fixtures/compiled_with_docs.ex\#L1" end @@ -172,9 +173,8 @@ defmodule ExDoc.RetrieverTest do end test "docs_from_modules fails when module is not available" do - config = %ExDoc.Config{source_url_pattern: "http://example.com/%{path}#L%{line}", source_root: File.cwd!} assert_raise ExDoc.Retriever.Error, "module NotAvailable is not defined/available", fn -> - docs_from_files(["NotAvailable"], config) + docs_from_files(["NotAvailable"]) end end diff --git a/test/fixtures/grouped_explicitly_atom.ex b/test/fixtures/grouped_explicitly_atom.ex deleted file mode 100644 index a02b237d8..000000000 --- a/test/fixtures/grouped_explicitly_atom.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule GroupedExplicitlyAtom do - @moduledoc false -end diff --git a/test/fixtures/grouped_explicitly_string.ex b/test/fixtures/grouped_explicitly_string.ex deleted file mode 100644 index bdcc3ceee..000000000 --- a/test/fixtures/grouped_explicitly_string.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule GroupedExplicitlyString do - @moduledoc false -end diff --git a/test/fixtures/grouped_implicitly_regex.ex b/test/fixtures/grouped_implicitly_regex.ex deleted file mode 100644 index 75694fc8f..000000000 --- a/test/fixtures/grouped_implicitly_regex.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule GroupedImplicitlyRegex do - @moduledoc false -end From 15269474e9586946884d6e095d4b2e4492c0230f Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Wed, 27 Sep 2017 20:43:11 +0200 Subject: [PATCH 3/8] Add templates unit tests for groups of modules --- test/ex_doc/formatter/html/templates_test.exs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/ex_doc/formatter/html/templates_test.exs b/test/ex_doc/formatter/html/templates_test.exs index 313c10573..25c784c8d 100644 --- a/test/ex_doc/formatter/html/templates_test.exs +++ b/test/ex_doc/formatter/html/templates_test.exs @@ -14,7 +14,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do "http://elixir-lang.org" end - defp doc_config do + defp doc_config(config \\ []) do %ExDoc.Config{ project: "Elixir", version: "1.0.1", @@ -24,6 +24,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do source_url: source_url(), output: "test/tmp/html_templates" } + |> struct(config) end defp get_module_page(names) do @@ -210,6 +211,15 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do assert content =~ ~r("id":"CompiledWithDocs".*"functions":.*"example_without_docs/0")ms assert content =~ ~r("id":"CompiledWithDocs.Nested")ms end + + test "list_page outputs groups for the given nodes" do + names = [CompiledWithDocs, CompiledWithDocs.Nested] + group_mapping = [module_groups: ["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 From 6f0c18269171cee1da008aa2022d5d36fd98d4b5 Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Wed, 27 Sep 2017 20:49:03 +0200 Subject: [PATCH 4/8] Fix code formatting issues --- test/ex_doc/formatter/html/templates_test.exs | 7 ++++--- test/ex_doc/retriever_test.exs | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/ex_doc/formatter/html/templates_test.exs b/test/ex_doc/formatter/html/templates_test.exs index 25c784c8d..a4c8d1504 100644 --- a/test/ex_doc/formatter/html/templates_test.exs +++ b/test/ex_doc/formatter/html/templates_test.exs @@ -15,7 +15,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do end defp doc_config(config \\ []) do - %ExDoc.Config{ + default = %ExDoc.Config{ project: "Elixir", version: "1.0.1", source_root: File.cwd!, @@ -24,7 +24,8 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do source_url: source_url(), output: "test/tmp/html_templates" } - |> struct(config) + + struct(default, config) end defp get_module_page(names) do @@ -211,7 +212,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do assert content =~ ~r("id":"CompiledWithDocs".*"functions":.*"example_without_docs/0")ms assert content =~ ~r("id":"CompiledWithDocs.Nested")ms end - + test "list_page outputs groups for the given nodes" do names = [CompiledWithDocs, CompiledWithDocs.Nested] group_mapping = [module_groups: ["Group": [CompiledWithDocs]]] diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs index 8309260a2..c7d35c387 100644 --- a/test/ex_doc/retriever_test.exs +++ b/test/ex_doc/retriever_test.exs @@ -5,11 +5,9 @@ defmodule ExDoc.RetrieverTest do defp docs_from_files(names, config \\ []) do files = Enum.map names, fn(n) -> "test/tmp/Elixir.#{n}.beam" end - config = - %ExDoc.Config{source_url_pattern: "http://example.com/%{path}#L%{line}", source_root: File.cwd!} - |> struct(config) + default = %ExDoc.Config{source_url_pattern: "http://example.com/%{path}#L%{line}", source_root: File.cwd!} - Retriever.docs_from_files(files, config) + Retriever.docs_from_files(files, struct(default, config)) end ## MODULES From 50a822a3c321accda71fae1b5dbfb359106e6ade Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Wed, 27 Sep 2017 21:42:18 +0200 Subject: [PATCH 5/8] Sort groups by the order of setup --- lib/ex_doc/retriever.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex index e0797f45b..635a9b78d 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -117,7 +117,10 @@ defmodule ExDoc.Retriever do modules |> Enum.map(&get_module(&1, config)) |> Enum.filter(&(&1)) - |> Enum.sort_by(&({&1.group, &1.id})) + |> Enum.sort_by(fn module -> + group_index = Enum.find_index(config.module_groups, fn {k, _v} -> Atom.to_string(k) == module.group end) + {group_index || -1, module.id} + end) end defp filename_to_module(name) do From 51ca9720fcdb30f2099e44ac08572bb7eaf12d5c Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Thu, 28 Sep 2017 20:29:50 +0200 Subject: [PATCH 6/8] Implement the new config setup for grouping extra pages --- lib/ex_doc.ex | 2 ++ lib/ex_doc/formatter/html.ex | 32 ++++++++++++++++++++++------- test/ex_doc/formatter/html_test.exs | 14 +++++++++++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/ex_doc.ex b/lib/ex_doc.ex index bad0eb53b..3eba9fa25 100644 --- a/lib/ex_doc.ex +++ b/lib/ex_doc.ex @@ -35,6 +35,7 @@ defmodule ExDoc do deps: [], extra_section: nil, extras: [], + extra_groups: [], filter_prefix: nil, formatter: @default.formatter, formatter_opts: [], @@ -64,6 +65,7 @@ defmodule ExDoc do deps: [{ebin_path :: String.t, doc_url :: String.t}], extra_section: nil | String.t, extras: list(), + extra_groups: list(), filter_prefix: nil | String.t, formatter: nil | String.t, formatter_opts: Keyword.t, diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex index ec88992ab..66ddffcf1 100644 --- a/lib/ex_doc/formatter/html.ex +++ b/lib/ex_doc/formatter/html.ex @@ -178,36 +178,54 @@ 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 = extra_group input, config.extra_groups + 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 end + + defp extra_group(input, group_patterns) do + Enum.find_value(group_patterns, fn {group, patterns} -> + patterns = List.wrap patterns + extra_in_patterns(input, patterns) && Atom.to_string(group) + end) + end + + defp extra_in_patterns(input, patterns) do + Enum.any?(patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, input) + string -> input == string + end + end) + end def valid_extension_name?(input) do file_ext = diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index dfc0c1946..898d19ca6 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{Getting Started – Elixir v1.0.1} content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") assert content =~ ~r{"id":"readme","title":"Getting Started","group":""} - end + end test "run generates pages with custom group" do + extra_config = [ + extras: ["test/fixtures/README.md"], + extra_groups: ["Intro": ~r/fixtures\/READ.?/] + ] + generate_docs(doc_config(extra_config)) + content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") + assert content =~ ~r{"id":"readme","title":"README","group":"Intro"} + end + + test "run generates pages with custom group via the deprecated method as keyword opts" do generate_docs(doc_config(extras: ["test/fixtures/README.md": [group: "Intro"]])) content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") assert content =~ ~r{"id":"readme","title":"README","group":"Intro"} - end + end test "run generates with auto-extracted title" do generate_docs(doc_config(extras: ["test/fixtures/ExtraPage.md"])) From 41d299ae4abbcbb669e91e5f7a70f972b3af947c Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Thu, 28 Sep 2017 21:08:04 +0200 Subject: [PATCH 7/8] Extract the matching logic into a new module --- lib/ex_doc/formatter/html.ex | 20 ++--------- lib/ex_doc/group_matcher.ex | 45 +++++++++++++++++++++++++ lib/ex_doc/retriever.ex | 22 ++---------- test/ex_doc/formatter/html_test.exs | 2 +- test/ex_doc/group_matcher_test.exs | 52 +++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 lib/ex_doc/group_matcher.ex create mode 100644 test/ex_doc/group_matcher_test.exs diff --git a/lib/ex_doc/formatter/html.ex b/lib/ex_doc/formatter/html.ex index 66ddffcf1..cf7dbe4a7 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" @@ -201,7 +201,7 @@ defmodule ExDoc.Formatter.HTML do |> File.read!() |> Autolink.project_doc(project_nodes, nil, extension) - group_pattern = extra_group input, config.extra_groups + group_pattern = GroupMatcher.match_extra config.extra_groups, input html_content = Markdown.to_html(content, file: input, line: 1) title = title || extract_title(html_content) || input_to_title(input) @@ -210,22 +210,6 @@ defmodule ExDoc.Formatter.HTML do raise ArgumentError, "file format not recognized, allowed format is: .md" end end - - defp extra_group(input, group_patterns) do - Enum.find_value(group_patterns, fn {group, patterns} -> - patterns = List.wrap patterns - extra_in_patterns(input, patterns) && Atom.to_string(group) - end) - end - - defp extra_in_patterns(input, patterns) do - Enum.any?(patterns, fn pattern -> - case pattern do - %Regex{} = regex -> Regex.match?(regex, input) - string -> input == string - end - end) - end def valid_extension_name?(input) do file_ext = diff --git a/lib/ex_doc/group_matcher.ex b/lib/ex_doc/group_matcher.ex new file mode 100644 index 000000000..22c16727b --- /dev/null +++ b/lib/ex_doc/group_matcher.ex @@ -0,0 +1,45 @@ +defmodule ExDoc.GroupMatcher do + @moduledoc """ + """ + @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 635a9b78d..5d0aa9ce2 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -83,6 +83,7 @@ defmodule ExDoc.Retriever do Functions to extract documentation information from modules. """ + alias ExDoc.GroupMatcher alias ExDoc.Retriever.Error alias Kernel.Typespec @@ -177,7 +178,7 @@ defmodule ExDoc.Retriever do {title, id} = module_title_and_id(module, type) - module_group = module_group(module, module_group_patterns) + module_group = GroupMatcher.match_module module_group_patterns, module, id %ExDoc.ModuleNode{ id: id, @@ -467,25 +468,6 @@ defmodule ExDoc.Retriever do {id, id} end - defp module_group(module, group_patterns) do - Enum.find_value(group_patterns, fn {group, patterns} -> - patterns = List.wrap patterns - module_in_patterns(module, patterns) && Atom.to_string(group) - end) - end - - defp module_in_patterns(module, patterns) do - id = module_id module - - Enum.any?(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 - defp module_id(module) do case inspect(module) do ":" <> inspected -> inspected diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index 898d19ca6..8597fe8d6 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -260,7 +260,7 @@ defmodule ExDoc.Formatter.HTMLTest do content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") assert content =~ ~r{"id":"readme","title":"README","group":"Intro"} end - + test "run generates pages with custom group via the deprecated method as keyword opts" do generate_docs(doc_config(extras: ["test/fixtures/README.md": [group: "Intro"]])) content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") diff --git a/test/ex_doc/group_matcher_test.exs b/test/ex_doc/group_matcher_test.exs new file mode 100644 index 000000000..ca9371a8b --- /dev/null +++ b/test/ex_doc/group_matcher_test.exs @@ -0,0 +1,52 @@ +defmodule ExDoc.GroupMatcherTest do + use ExUnit.Case, async: true + import ExDoc.GroupMatcher + + describe "module matching" do + test "it can match modules by their atom names" do + patterns = [ + "Group": [MyApp.SomeModule, :lists] + ] + assert "Group" == match_module(patterns, MyApp.SomeModule, "MyApp.SomeModule") + assert "Group" == match_module(patterns, :lists, ":lists") + assert nil == match_module(patterns, MyApp.SomeOtherModule, "MyApp.SomeOtherModule") + end + + test "it can match modules by their string names" do + patterns = [ + "Group": ["MyApp.SomeModule", ":lists"] + ] + assert "Group" == match_module(patterns, MyApp.SomeModule, "MyApp.SomeModule") + assert "Group" == match_module(patterns, :lists, ":lists") + assert nil == match_module(patterns, MyApp.SomeOtherModule, "MyApp.SomeOtherModule") + end + + test "it can match modules by regular expressions" do + patterns = [ + "Group": ~r/MyApp\..?/ + ] + assert "Group" == match_module(patterns, MyApp.SomeModule, "MyApp.SomeModule") + assert "Group" == match_module(patterns, MyApp.SomeOtherModule, "MyApp.SomeOtherModule") + assert nil == match_module(patterns, MyAppWeb.SomeOtherModule, "MyAppWeb.SomeOtherModule") + end + end + + describe "extras matching" do + test "it can match extra files by their string names" do + patterns = [ + "Group": ["docs/handling/testing.md"] + ] + assert "Group" == match_extra(patterns, "docs/handling/testing.md") + assert nil == match_extra(patterns, "docs/handling/setup.md") + end + + test "it can match extra files by regular expressions" do + patterns = [ + "Group": ~r/docs\/handling?/ + ] + assert "Group" == match_extra(patterns, "docs/handling/testing.md") + assert "Group" == match_extra(patterns, "docs/handling/setup.md") + assert nil == match_extra(patterns, "docs/introduction.md") + end + end +end From fb72336b3d74ebc4523fa4710b536a778e76412c Mon Sep 17 00:00:00 2001 From: Benjamin Milde Date: Thu, 28 Sep 2017 21:46:29 +0200 Subject: [PATCH 8/8] Rename config keys --- lib/ex_doc.ex | 8 ++++---- lib/ex_doc/formatter/html.ex | 2 +- lib/ex_doc/group_matcher.ex | 3 +++ lib/ex_doc/retriever.ex | 5 ++--- test/ex_doc/formatter/html/templates_test.exs | 2 +- test/ex_doc/formatter/html_test.exs | 2 +- test/ex_doc/retriever_test.exs | 6 +++--- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/ex_doc.ex b/lib/ex_doc.ex index 3eba9fa25..9a5a382b7 100644 --- a/lib/ex_doc.ex +++ b/lib/ex_doc.ex @@ -35,7 +35,7 @@ defmodule ExDoc do deps: [], extra_section: nil, extras: [], - extra_groups: [], + groups_for_extras: [], filter_prefix: nil, formatter: @default.formatter, formatter_opts: [], @@ -43,7 +43,7 @@ defmodule ExDoc do language: @default.language, logo: nil, main: nil, - module_groups: [], + groups_for_modules: [], output: @default.output, project: nil, retriever: @default.retriever, @@ -65,7 +65,7 @@ defmodule ExDoc do deps: [{ebin_path :: String.t, doc_url :: String.t}], extra_section: nil | String.t, extras: list(), - extra_groups: list(), + groups_for_extras: keyword(), filter_prefix: nil | String.t, formatter: nil | String.t, formatter_opts: Keyword.t, @@ -73,7 +73,7 @@ defmodule ExDoc do language: String.t, logo: nil | Path.t, main: nil | String.t, - module_groups: list(), + 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 cf7dbe4a7..e33721c55 100644 --- a/lib/ex_doc/formatter/html.ex +++ b/lib/ex_doc/formatter/html.ex @@ -201,7 +201,7 @@ defmodule ExDoc.Formatter.HTML do |> File.read!() |> Autolink.project_doc(project_nodes, nil, extension) - group_pattern = GroupMatcher.match_extra config.extra_groups, input + 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) diff --git a/lib/ex_doc/group_matcher.ex b/lib/ex_doc/group_matcher.ex index 22c16727b..652eac766 100644 --- a/lib/ex_doc/group_matcher.ex +++ b/lib/ex_doc/group_matcher.ex @@ -1,5 +1,8 @@ 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] diff --git a/lib/ex_doc/retriever.ex b/lib/ex_doc/retriever.ex index 5d0aa9ce2..7e6359e50 100644 --- a/lib/ex_doc/retriever.ex +++ b/lib/ex_doc/retriever.ex @@ -119,7 +119,7 @@ defmodule ExDoc.Retriever do |> Enum.map(&get_module(&1, config)) |> Enum.filter(&(&1)) |> Enum.sort_by(fn module -> - group_index = Enum.find_index(config.module_groups, fn {k, _v} -> Atom.to_string(k) == module.group end) + 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 @@ -163,7 +163,6 @@ defmodule ExDoc.Retriever do end defp generate_node(module, type, config) do - module_group_patterns = config.module_groups source_url = config.source_url_pattern source_path = source_path(module, config) source = %{url: source_url, path: source_path} @@ -178,7 +177,7 @@ defmodule ExDoc.Retriever do {title, id} = module_title_and_id(module, type) - module_group = GroupMatcher.match_module module_group_patterns, module, id + module_group = GroupMatcher.match_module config.groups_for_modules, module, id %ExDoc.ModuleNode{ id: id, diff --git a/test/ex_doc/formatter/html/templates_test.exs b/test/ex_doc/formatter/html/templates_test.exs index a4c8d1504..f7d89a401 100644 --- a/test/ex_doc/formatter/html/templates_test.exs +++ b/test/ex_doc/formatter/html/templates_test.exs @@ -215,7 +215,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do test "list_page outputs groups for the given nodes" do names = [CompiledWithDocs, CompiledWithDocs.Nested] - group_mapping = [module_groups: ["Group": [CompiledWithDocs]]] + 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}, []) diff --git a/test/ex_doc/formatter/html_test.exs b/test/ex_doc/formatter/html_test.exs index 8597fe8d6..fa9b94644 100644 --- a/test/ex_doc/formatter/html_test.exs +++ b/test/ex_doc/formatter/html_test.exs @@ -254,7 +254,7 @@ defmodule ExDoc.Formatter.HTMLTest do test "run generates pages with custom group" do extra_config = [ extras: ["test/fixtures/README.md"], - extra_groups: ["Intro": ~r/fixtures\/READ.?/] + groups_for_extras: ["Intro": ~r/fixtures\/READ.?/] ] generate_docs(doc_config(extra_config)) content = read_wildcard!("#{output_dir()}/dist/sidebar_items-*.js") diff --git a/test/ex_doc/retriever_test.exs b/test/ex_doc/retriever_test.exs index c7d35c387..6b2e9def0 100644 --- a/test/ex_doc/retriever_test.exs +++ b/test/ex_doc/retriever_test.exs @@ -47,21 +47,21 @@ defmodule ExDoc.RetrieverTest do describe "docs_from_files returns the group" do test "atom" do - [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + [module_node] = docs_from_files ["CompiledWithDocs"], groups_for_modules: [ "Group": [CompiledWithDocs] ] assert module_node.group == "Group" end test "string" do - [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + [module_node] = docs_from_files ["CompiledWithDocs"], groups_for_modules: [ "Group": ["CompiledWithDocs"] ] assert module_node.group == "Group" end test "regex" do - [module_node] = docs_from_files ["CompiledWithDocs"], module_groups: [ + [module_node] = docs_from_files ["CompiledWithDocs"], groups_for_modules: [ "Group": ~r/^CompiledWith.?/ ] assert module_node.group == "Group"