Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/ex_doc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ defmodule ExDoc do
deps: [],
extra_section: nil,
extras: [],
groups_for_extras: [],
filter_prefix: nil,
formatter: @default.formatter,
formatter_opts: [],
homepage_url: nil,
language: @default.language,
logo: nil,
main: nil,
groups_for_modules: [],
output: @default.output,
project: nil,
retriever: @default.retriever,
Expand All @@ -63,13 +65,15 @@ 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,
homepage_url: nil | String.t,
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,
Expand Down
18 changes: 10 additions & 8 deletions lib/ex_doc/formatter/html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ExDoc.Formatter.HTML do
"""

alias __MODULE__.{Assets, Autolink, Templates}
alias ExDoc.Markdown
alias ExDoc.{Markdown, GroupMatcher}

@main "api-reference"

Expand Down Expand Up @@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function takes too many parameters (arity is 7, max is 5).

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
Expand Down
4 changes: 2 additions & 2 deletions lib/ex_doc/formatter/html/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 48 additions & 0 deletions lib/ex_doc/group_matcher.ex
Original file line number Diff line number Diff line change
@@ -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
15 changes: 12 additions & 3 deletions lib/ex_doc/retriever.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
15 changes: 13 additions & 2 deletions test/ex_doc/formatter/html/templates_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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!,
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions test/ex_doc/formatter/html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,23 @@ defmodule ExDoc.Formatter.HTMLTest do
assert content =~ ~r{<title>Getting Started – Elixir v1.0.1</title>}
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"],
groups_for_extras: ["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"]))
Expand Down
52 changes: 52 additions & 0 deletions test/ex_doc/group_matcher_test.exs
Original file line number Diff line number Diff line change
@@ -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
35 changes: 29 additions & 6 deletions test/ex_doc/retriever_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ 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!}
Retriever.docs_from_files(files, config)
default = %ExDoc.Config{source_url_pattern: "http://example.com/%{path}#L%{line}", source_root: File.cwd!}

Retriever.docs_from_files(files, struct(default, config))
end

## MODULES
Expand Down Expand Up @@ -44,6 +45,29 @@ 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 ["CompiledWithDocs"], groups_for_modules: [
"Group": [CompiledWithDocs]
]
assert module_node.group == "Group"
end

test "string" do
[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"], groups_for_modules: [
"Group": ~r/^CompiledWith.?/
]
assert module_node.group == "Group"
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"
Expand Down Expand Up @@ -135,7 +159,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

Expand All @@ -147,9 +171,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

Expand Down