Skip to content

Commit 6f3fb27

Browse files
committed
Add mix help app:APP, closes #14782
1 parent 8c14d3a commit 6f3fb27

File tree

2 files changed

+64
-7
lines changed

2 files changed

+64
-7
lines changed

lib/mix/lib/mix/tasks/help.ex

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,30 @@
55
defmodule Mix.Tasks.Help do
66
use Mix.Task
77

8-
@shortdoc "Prints help information for tasks"
8+
@shortdoc "Prints help information for tasks, aliases, modules, and applications"
99

1010
@moduledoc """
11-
Lists all tasks and aliases or prints the documentation for a given task or alias.
11+
Prints documentation for tasks, aliases, modules, and applications.
1212
13-
## Arguments
13+
## Examples
14+
15+
Without an explicit argument, this task lists tasks/aliases:
1416
1517
$ mix help - prints all aliases, tasks and their short descriptions
16-
$ mix help ALIAS - prints the definition for the given alias
17-
$ mix help TASK - prints full docs for the given task
1818
$ mix help --search PATTERN - prints all tasks and aliases that contain PATTERN in the name
1919
$ mix help --names - prints all task names and aliases (useful for autocompletion)
2020
$ mix help --aliases - prints all aliases
2121
22+
You can access documentation for a given task/alias:
23+
24+
$ mix help TASK/ALIAS - prints full docs for the given task/alias
25+
26+
But also for modules, functions, and applications:
27+
28+
$ mix help MODULE - prints the definition for the given module
29+
$ mix help MODULE.FUN - prints the definition for the given module+function
30+
$ mix help app:APP - prints a summary of all public modules in application
31+
2232
## Colors
2333
2434
When possible, `mix help` is going to use coloring for formatting
@@ -100,6 +110,31 @@ defmodule Mix.Tasks.Help do
100110

101111
@compile {:no_warn_undefined, IEx.Introspection}
102112

113+
def run(["app:" <> app]) do
114+
loadpaths!()
115+
app = String.to_atom(app)
116+
117+
if modules = Application.spec(app, :modules) do
118+
for module <- modules,
119+
not (module |> Atom.to_string() |> String.starts_with?("Elixir.Mix.Tasks.")),
120+
{:docs_v1, _, :elixir, "text/markdown", %{"en" => <<doc::binary>>}, _, _} <-
121+
[Code.fetch_docs(module)] do
122+
leading = doc |> String.split(["\n\n", "\r\n\r\n"], parts: 2) |> hd()
123+
"# #{inspect(module)}\n#{leading}\n"
124+
end
125+
|> case do
126+
[] ->
127+
Mix.shell().error("No modules with accessible documentation found for #{app}")
128+
129+
listing ->
130+
docs = listing |> Enum.sort() |> Enum.join()
131+
IO.ANSI.Docs.print(docs, "text/markdown", ansi_opts())
132+
end
133+
else
134+
Mix.shell().error("Application #{app} does not exist or is not loaded")
135+
end
136+
end
137+
103138
def run([module = <<first, _::binary>>]) when first in ?A..?Z or first == ?: do
104139
loadpaths!()
105140

@@ -124,8 +159,7 @@ defmodule Mix.Tasks.Help do
124159

125160
def run([task]) do
126161
loadpaths!()
127-
opts = Application.get_env(:mix, :colors)
128-
opts = [width: width(), enabled: ansi_docs?(opts)] ++ opts
162+
opts = ansi_opts()
129163

130164
for doc <- verbose_doc(task) do
131165
print_doc(task, doc, opts)
@@ -138,6 +172,11 @@ defmodule Mix.Tasks.Help do
138172
Mix.raise("Unexpected arguments, expected \"mix help\" or \"mix help TASK\"")
139173
end
140174

175+
defp ansi_opts do
176+
opts = Application.get_env(:mix, :colors)
177+
[width: width(), enabled: ansi_docs?(opts)] ++ opts
178+
end
179+
141180
defp print_doc(task, {doc, location, note}, opts) do
142181
IO.ANSI.Docs.print_headings(["mix #{task}"], opts)
143182
IO.ANSI.Docs.print(doc, "text/markdown", opts)

lib/mix/test/mix/tasks/help_test.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,24 @@ defmodule Mix.Tasks.HelpTest do
195195
end)
196196
end
197197

198+
test "help app:APP", context do
199+
in_tmp(context.test, fn ->
200+
output =
201+
capture_io(fn ->
202+
Mix.Tasks.Help.run(["app:mix"])
203+
end)
204+
205+
assert output =~ "# Mix\n\nMix is a build tool"
206+
end)
207+
end
208+
209+
test "help unknown app:APP", context do
210+
in_tmp(context.test, fn ->
211+
Mix.Tasks.Help.run(["app:foobar"])
212+
assert_received {:mix_shell, :error, ["Application foobar does not exist or is not loaded"]}
213+
end)
214+
end
215+
198216
test "help Elixir MODULE", context do
199217
in_tmp(context.test, fn ->
200218
output =

0 commit comments

Comments
 (0)