Skip to content

Commit

Permalink
allow for new extra sidebar format that groups h3s into h2s
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Jan 14, 2025
1 parent a3f75e8 commit f7af74e
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 20 deletions.
27 changes: 22 additions & 5 deletions assets/js/autocomplete/suggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export function getSuggestions (query, limit = 8) {
...findSuggestionsInTopLevelNodes(nodes.extras, query, SUGGESTION_CATEGORY.extra, 'page'),
...findSuggestionsInSectionsOfNodes(nodes.modules, query, SUGGESTION_CATEGORY.section, 'module'),
...findSuggestionsInSectionsOfNodes(nodes.tasks, query, SUGGESTION_CATEGORY.section, 'mix task'),
...findSuggestionsInSectionsOfNodes(nodes.extras, query, SUGGESTION_CATEGORY.section, 'page')
...findSuggestionsInSectionsOfNodes(nodes.extras, query, SUGGESTION_CATEGORY.section, 'page'),
...findSuggestionsInChildNodesOfExtras(nodes.extras, query, SUGGESTION_CATEGORY.section, 'page')
].filter(suggestion => suggestion !== null)

return sort(suggestions).slice(0, limit)
Expand Down Expand Up @@ -74,13 +75,29 @@ function findSuggestionsInChildNodes (nodes, query, category) {
const label = nodeGroupKeyToLabel(key)

return childNodes.map(childNode =>
childNodeSuggestion(childNode, node.id, query, category, label) ||
moduleChildNodeSuggestion(childNode, node.id, query, category, label)
childNodeSuggestion(childNode, node.id, node.id, query, category, label) ||
moduleChildNodeSuggestion(childNode, node.id, node.id, query, category, label)
)
})
})
}

/**
* Finds suggestions in node groups of the given parent nodes for extras.
*/
function findSuggestionsInChildNodesOfExtras (nodes, query, category) {
return nodes
.filter(node => node.nodeGroups && !node.searchData)
.flatMap(node => {
return node.nodeGroups.flatMap(group => {
const { key, nodes: childNodes } = group;

Check failure on line 93 in assets/js/autocomplete/suggestions.js

View workflow job for this annotation

GitHub Actions / assets (1.17, 27, 18.x)

'key' is assigned a value but never used

Check failure on line 93 in assets/js/autocomplete/suggestions.js

View workflow job for this annotation

GitHub Actions / assets (1.17, 27, 18.x)

Extra semicolon
return childNodes.map(childNode =>
childNodeSuggestion(childNode, node.title, node.id, query, category, 'section')
).concat([childNodeSuggestion(group, node.title, node.id, query, category, 'section')])
})
})
}

/**
* Finds suggestions in the sections of the given parent nodes.
*/
Expand Down Expand Up @@ -125,14 +142,14 @@ function nodeSuggestion (node, query, category, label) {
* Builds a suggestion for a child node.
* Returns null if the node doesn't match the query.
*/
function childNodeSuggestion (childNode, parentId, query, category, label) {
function childNodeSuggestion (childNode, parentTitle, parentId, query, category, label) {
if (!matchesAll(childNode.id, query)) { return null }

return {
link: `${parentId}.html#${childNode.anchor}`,
title: highlightMatches(childNode.id, query),
labels: [label],
description: parentId,
description: parentTitle,
matchQuality: matchQuality(childNode.id, query),
deprecated: childNode.deprecated,
category
Expand Down

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions lib/ex_doc/formatter/html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ defmodule ExDoc.Formatter.HTML do
content: api_reference,
group: nil,
id: "api-reference",
sidebar_style: :flat,
source_path: nil,
source_url: config.source_url,
title: "API Reference",
Expand Down Expand Up @@ -438,6 +439,7 @@ defmodule ExDoc.Formatter.HTML do
content: content_html,
group: group,
id: id,
sidebar_style: input_options[:sidebar_style] || :flat,
source_path: source_path,
source_url: source_url,
search_data: search_data,
Expand Down
47 changes: 43 additions & 4 deletions lib/ex_doc/formatter/html/templates.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ defmodule ExDoc.Formatter.HTML.Templates do
text_to_id: 1
]

@h2_regex ~r/<h2.*?>(.*?)<\/h2>/m
@h2contents_regex ~r/<h2.*?>(.*?)<\/h2>(.*?)(?=<h2|<h1|\z)/ms
@h3_regex ~r/<h3.*?>(.*?)<\/h3>/m

@doc """
Generate content from the module template for a given `node`
"""
Expand Down Expand Up @@ -82,15 +86,16 @@ defmodule ExDoc.Formatter.HTML.Templates do

defp sidebar_extras(extras) do
for extra <- extras do
%{id: id, title: title, group: group, content: content} = extra
%{id: id, title: title, group: group, content: content, sidebar_style: sidebar_style} =
extra

item =
%{
id: to_string(id),
title: to_string(title),
group: to_string(group),
headers: extract_headers(content)
group: to_string(group)
}
|> add_headers_or_node_groups(content, sidebar_style)

case extra do
%{search_data: search_data} when is_list(search_data) ->
Expand All @@ -111,6 +116,41 @@ defmodule ExDoc.Formatter.HTML.Templates do
end
end

defp add_headers_or_node_groups(item, content, :flat) do
Map.put(item, :headers, extract_headers(content))
end

defp add_headers_or_node_groups(item, content, :grouped) do
Map.put(item, :nodeGroups, extract_node_groups(content))
end

defp extract_node_groups(content) do
@h2contents_regex
|> Regex.scan(content, capture: :all_but_first)
|> Enum.filter(fn
["" | _] -> false
_ -> true
end)
|> Enum.map(fn [group, content] ->
nodes =
@h3_regex
|> Regex.scan(content, capture: :all_but_first)
|> List.flatten()
|> Enum.filter(&(&1 != ""))
|> Enum.map(&ExDoc.Utils.strip_tags/1)
|> Enum.map(&%{id: &1, title: &1, anchor: URI.encode(text_to_id(&1)), deprecated: false})

%{
key: group,
name: group,
nodes: nodes,
title: group,
id: text_to_id(group),
anchor: URI.encode(text_to_id(group))
}
end)
end

defp sidebar_module({id, modules}) do
modules =
for module <- modules do
Expand Down Expand Up @@ -182,7 +222,6 @@ defmodule ExDoc.Formatter.HTML.Templates do
end

# TODO: split into sections in Formatter.HTML instead (possibly via DocAST)
@h2_regex ~r/<h2.*?>(.*?)<\/h2>/m
defp extract_headers(content) do
@h2_regex
|> Regex.scan(content, capture: :all_but_first)
Expand Down
2 changes: 2 additions & 0 deletions lib/mix/tasks/docs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ defmodule Mix.Tasks.Docs do
the source file.
* `:source` - The source file of the extra page. This is useful if you want to customize the filename or
title but keep the source file unchanged. *
* `:sidebar_style` - `:flat` or `:grouped`. Defaults to `:flat`. If set to `:grouped`, all h3s will be shown
in collapsible sections of their corresponding h2. The epub format ignores this option.
`:search_data` - A list of terms to be indexed for autocomplete and search. If not provided, the content
of the extra page will be indexed for search. See the section below for more.
Expand Down
2 changes: 1 addition & 1 deletion test/ex_doc/formatter/html/templates_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ defmodule ExDoc.Formatter.HTML.TemplatesTest do
end

test "outputs extras with headers" do
item = %{content: nil, group: nil, id: nil, title: nil}
item = %{content: nil, group: nil, id: nil, title: nil, sidebar_style: :flat}

assert create_sidebar_items(%{}, [%{item | content: "<h2>Foo</h2><h2>Bar</h2>"}])["extras"] ==
[
Expand Down
65 changes: 65 additions & 0 deletions test/ex_doc/formatter/html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,71 @@ defmodule ExDoc.Formatter.HTMLTest do
] = Jason.decode!(content)["extras"]
end

test "extras can group sidebar nodes as h3s grouped by h2", %{tmp_dir: tmp_dir} = context do

Check failure on line 595 in test/ex_doc/formatter/html_test.exs

View workflow job for this annotation

GitHub Actions / elixir (1.14, 25)

test generates extras extras can group sidebar nodes as h3s grouped by h2 (ExDoc.Formatter.HTMLTest)

Check failure on line 595 in test/ex_doc/formatter/html_test.exs

View workflow job for this annotation

GitHub Actions / elixir (1.16, 26)

test generates extras extras can group sidebar nodes as h3s grouped by h2 (ExDoc.Formatter.HTMLTest)

Check failure on line 595 in test/ex_doc/formatter/html_test.exs

View workflow job for this annotation

GitHub Actions / elixir (1.17, 27, true)

test generates extras extras can group sidebar nodes as h3s grouped by h2 (ExDoc.Formatter.HTMLTest)
generate_docs(
doc_config(context,
source_beam: "unknown",
extras: [
{"test/fixtures/ExtraPageWithH3s.md", sidebar_style: :grouped}
]
)
)

"sidebarNodes=" <> content = read_wildcard!(tmp_dir <> "/html/dist/sidebar_items-*.js")

assert [
%{
"group" => "",
"headers" => [],
"id" => "api-reference",
"title" => "API Reference"
},
%{
"group" => "",
"id" => "extrapagewithh3s",
"nodeGroups" => [
%{
"key" => "section-one",
"name" => "Section One",
"nodes" => [
%{
"anchor" => "nested-section-one",
"deprecated" => false,
"id" => "Nested Section One",
"title" => "Nested Section One"
},
%{
"anchor" => "nested-section-two",
"deprecated" => false,
"id" => "Nested Section Two",
"title" => "Nested Section Two"
}
]
},
%{
"key" => "section-two",
"name" => "Section Two",
"nodes" => [
%{
"anchor" => "nested-section-three",
"deprecated" => false,
"id" => "Nested Section Three",
"title" => "Nested Section Three"
},
%{
"anchor" => "nested-section-four",
"deprecated" => false,
"id" => "Nested Section Four",
"title" => "Nested Section Four"
}
]
}
],
"title" => "Extra Page Title"
}
] = Jason.decode!(content)["extras"]
end

test "custom search data is added to the sidebar and search nodes",
%{tmp_dir: tmp_dir} = context do
generate_docs(
Expand Down
27 changes: 27 additions & 0 deletions test/fixtures/ExtraPageWithH3s.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Extra Page Title

some text

## Section One

more text

### Nested Section One

nested section one text

### Nested Section Two

nested section two text

## Section Two

final text

### Nested Section Three

nested section three text

### Nested Section Four

nested section text

0 comments on commit f7af74e

Please sign in to comment.