Skip to content

Commit d1c0c96

Browse files
committed
Support specifying packages under search
1 parent 868fa8b commit d1c0c96

File tree

4 files changed

+103
-32
lines changed

4 files changed

+103
-32
lines changed

lib/ex_doc/config.ex

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,23 @@ defmodule ExDoc.Config do
319319

320320
defp normalize_search(search) when is_list(search) do
321321
Enum.map(search, fn
322+
%{packages: _, url: _} ->
323+
raise ArgumentError, "search must provide either :url or :packages, but not both"
324+
322325
%{url: url} = engine when not is_binary(url) ->
323326
bad_search!(engine)
324327

325328
%{name: name, help: help} = engine
326329
when is_binary(name) and is_binary(help) ->
327-
Map.put_new_lazy(engine, :url, fn -> "search.html?q=" end)
330+
engine
331+
|> Map.delete(:packages)
332+
|> Map.put_new_lazy(:url, fn ->
333+
if packages = engine[:packages] do
334+
"https://hexdocs.pm/?packages=#{normalize_package_search(packages)}&q="
335+
else
336+
"search.html?q="
337+
end
338+
end)
328339

329340
other ->
330341
bad_search!(other)
@@ -335,8 +346,37 @@ defmodule ExDoc.Config do
335346
raise ArgumentError, "search must be a list of maps, got: #{inspect(other)}"
336347
end
337348

349+
defp normalize_package_search([]) do
350+
raise ArgumentError, ":packages requires a non-empty list"
351+
end
352+
353+
defp normalize_package_search(packages) do
354+
packages
355+
|> Enum.map_join(",", fn
356+
package when is_atom(package) ->
357+
"#{package}:latest"
358+
359+
{package, version} when is_atom(package) and is_binary(version) ->
360+
"#{package}:#{version}"
361+
362+
other ->
363+
raise ArgumentError,
364+
"entries in :packages must be either a package name or a package-version tuple, got: #{inspect(other)}"
365+
end)
366+
|> URI.encode_www_form()
367+
end
368+
338369
defp bad_search!(other) do
339370
raise ArgumentError,
340-
"search entries must be a map with :name, :help, and optional :url keys with string values, got: #{inspect(other)}"
371+
"""
372+
search entries must be a map with:
373+
374+
* required :name as string
375+
* required :help as string
376+
* optional :url as a string
377+
* optional :packages as a non-empty list of packages and versions
378+
379+
got: #{inspect(other)}
380+
"""
341381
end
342382
end

lib/mix/tasks/docs.ex

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,7 @@ defmodule Mix.Tasks.Docs do
164164
value is the path to redirect to. The extension is omitted in both cases, i.e `%{"old-readme" => "readme"}`.
165165
See the "Changing documentation over time" section below for more.
166166
167-
* `:search` - A list of search engine configurations. Each search engine is a map with the following keys:
168-
169-
* `:name` - The display name of the search engine (required)
170-
* `:help` - A help text describing what the search engine does (required)
171-
* `:url` - The optional search URL template, usually ending with `q=`
172-
The default uses ExDoc built-in Lunr's search engine
173-
174-
When multiple search engines are configured, a dropdown selector will appear next to the search bar
175-
allowing users to choose which engine to use. For example:
176-
177-
search: [
178-
%{name: "FooBar", help: "Search on FooBar", url: "https://example.com/?q="},
179-
%{name: "Local", help: "In-browser search"}
180-
]
181-
182-
By default, the built-in Lunr's search engine is configured. If only one search engine
183-
is configured, the dropdown selector will be hidden.
167+
* `:search` - A list of search engine configurations. See the "Search engines" section
184168
185169
* `:skip_undefined_reference_warnings_on` - ExDoc warns when it can't create a `Mod.fun/arity`
186170
reference in the current project docs (for example, because of a typo). This option controls when to
@@ -422,21 +406,36 @@ defmodule Mix.Tasks.Docs do
422406
Groups in the sidebar and main page body are ordered according to the following
423407
rules:
424408
425-
* First, groups defined as `@moduledoc groups: [...]` in the given order.
426-
* Then groups defined as keys in the `:groups_for_docs` configuration.
427-
* Then default groups: Types, Callbacks and Functions.
428-
* Finally, other groups returned by `:default_group_for_doc` by alphabetical
429-
order.
409+
* First, groups defined as `@moduledoc groups: [...]` in the given order.
410+
* Then groups defined as keys in the `:groups_for_docs` configuration.
411+
* Then default groups: Types, Callbacks and Functions.
412+
* Finally, other groups returned by `:default_group_for_doc` by alphabetical order.
430413
431-
## Meta-tags configuration
414+
## Search engines
432415
433-
It is also possible to configure some of ExDoc behaviour using meta tags.
434-
These meta tags can be inserted using `before_closing_head_tag`.
416+
ExDoc allows custom search engines via the `:search` key. Each search engine
417+
is a map with the following keys:
435418
436-
* `exdoc:autocomplete` - when set to "off", it disables autocompletion.
419+
* `:name` - The display name of the search engine (required)
420+
* `:help` - A help text describing what the search engine does (required)
421+
* `:url` - The optional search URL template, usually ending with `q=`
422+
* `:packages` - An optional list of packages (or package-versions) to search on
423+
https://hexdocs.pm. For example: `[:plug, :phoenix, ecto: "3.0.0", ecto_sql: "3.0.0"]`.
424+
If no version is specified, it uses the package latest
437425
438-
* `exdoc:autocomplete-limit` - Set to an integer to configure how many results
439-
appear in the autocomplete dropdown. Defaults to 10.
426+
If none of `:url` or `:packages` are given, ExDoc will use its default search engine
427+
powered by Lunr.
428+
429+
When multiple search engines are configured, a dropdown selector will appear next to
430+
the search bar allowing users to choose which engine to use. For example:
431+
432+
search: [
433+
%{name: "FooBar", help: "Search on FooBar", url: "https://example.com/?q="},
434+
%{name: "Local", help: "In-browser search"}
435+
]
436+
437+
If only one search engine is configured, the dropdown selector will be hidden.
438+
If no search engine is configured, only the built-in Lunr's is shown.
440439
441440
## Nesting
442441
@@ -495,7 +494,7 @@ defmodule Mix.Tasks.Docs do
495494
* `:title` - The title of the extra page. If not provided, the title will be inferred from the extra name.
496495
* `:url` - The external url to link to from the sidebar.
497496
498-
### Customizing Search Data
497+
### Customizing search data
499498
500499
It is possible to fully customize the way a given extra is indexed, both in autocomplete and in search.
501500
In most cases, this makes sense for _generated_ documentation. If `search_data` is provided, it completely
@@ -518,6 +517,16 @@ defmodule Mix.Tasks.Docs do
518517
mix cmd mix docs
519518
520519
See `mix help cmd` for more information.
520+
521+
## Meta-tags configuration
522+
523+
It is also possible to configure some of ExDoc behaviour using meta tags.
524+
These meta tags can be inserted using `before_closing_head_tag`.
525+
526+
* `exdoc:autocomplete` - when set to "off", it disables autocompletion.
527+
528+
* `exdoc:autocomplete-limit` - Set to an integer to configure how many results
529+
appear in the autocomplete dropdown. Defaults to 10.
521530
"""
522531

523532
@switches [

mix.exs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ defmodule ExDoc.Mixfile do
8787
[
8888
search: [
8989
%{
90-
name: "ExDoc ecosystem",
90+
name: "ExDoc + Elixir",
91+
help: "Search latest ExDoc + Elixir",
92+
packages: [:ex_doc, elixir: "main"]
93+
},
94+
%{
95+
name: "ExDoc on Google",
9196
help: "Search everything about ExDoc on Google",
9297
url: "https://google.com/?q="
9398
},

test/ex_doc/config_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,23 @@ defmodule ExDoc.ConfigTest do
187187
]
188188
end
189189

190+
test "accepts list of maps with name, help, and packages" do
191+
config =
192+
build(
193+
search: [
194+
%{name: "Local", help: "Search locally", packages: [:ex_doc, elixir: "main"]}
195+
]
196+
)
197+
198+
assert config.search == [
199+
%{
200+
name: "Local",
201+
help: "Search locally",
202+
url: "https://hexdocs.pm/?packages=ex_doc%3Alatest%2Celixir%3Amain&q="
203+
}
204+
]
205+
end
206+
190207
test "defaults url to search.html?q= when not provided" do
191208
config =
192209
build(

0 commit comments

Comments
 (0)