Make mix help list aliases #7805
Conversation
Not sure why the latest build failed: https://travis-ci.org/elixir-lang/elixir/jobs/397193995. Does the new code introduce something flaky? |
I was just wanting this exact functionality. Thanks for doing the pull request. |
The build failure seems unrelated. I rested the failed suite. |
|
||
defp task_str(task_name) when is_list(task_name) do | ||
task_strs = Enum.map(task_name, &task_str/1) |> Enum.join(", ") | ||
"[" <> task_strs <> "]" |
michalmuskala
Jun 27, 2018
Member
I wonder about the format here with the list, but I'm not really sure what would be better.
I wonder about the format here with the list, but I'm not really sure what would be better.
josevalim
Jun 27, 2018
Member
I would say we should just call inspect
. It is the simplest implementation and it would be similar to what we have today. But I am not sure.
I guess the formatting was the biggest concern for including aliases in the mix help
, as they can get quite verbose. Another option is to just have a simple text: "# Alias defined in mix.exs" and call it a day.
Thoughts?
I would say we should just call inspect
. It is the simplest implementation and it would be similar to what we have today. But I am not sure.
I guess the formatting was the biggest concern for including aliases in the mix help
, as they can get quite verbose. Another option is to just have a simple text: "# Alias defined in mix.exs" and call it a day.
Thoughts?
I think in order for this feature to be complete, this PR should support This might also simplify the formatting of the aliases in
Thoughts? |
I like this solution. |
I like:
OR
|
@smorin If you want to add descriptions and what not, then I would say you should just go ahead and define a proper task. |
@josevalim Why force someone to write a task for something that takes 3 words. It's using a sledge hammer to drive a nail. Especially in cases, where the command is 2 or 3 separate words and the description is the longest part. Why clutter a code base, adding steps for a developer, and more lines of code to maintain. npm does it will good effect: https://docs.npmjs.com/misc/scripts |
{docs, max} = build_task_doc_list(modules) | ||
aliases = | ||
load_aliases() | ||
|> Enum.filter(&String.contains?(elem(&1, 0), pattern)) |
whatyouhide
Jun 27, 2018
Member
We try to avoid single pipes. I would suggest to refactor this with a for
comprehension:
aliases =
for {alias_name, _} = alias <- load_aliases(),
String.contains?(alias_name, pattern),
do: alias
We try to avoid single pipes. I would suggest to refactor this with a for
comprehension:
aliases =
for {alias_name, _} = alias <- load_aliases(),
String.contains?(alias_name, pattern),
do: alias
Sihui
Jun 27, 2018
Author
Contributor
why do we try to avoid single pipes?
why do we try to avoid single pipes?
josevalim
Jun 27, 2018
Member
@Sihui mostly because if we don't have multiple expressions, the pipeline ends up being more noise than helping readability. We know it is subjective but that's why we decided to add a "rule", so it is a clear path and we remove subjectivity. :)
@Sihui mostly because if we don't have multiple expressions, the pipeline ends up being more noise than helping readability. We know it is subjective but that's why we decided to add a "rule", so it is a clear path and we remove subjectivity. :)
Sihui
Jun 27, 2018
•
Author
Contributor
@josevalim
A (weak) counter argument will be using single pipe makes adding additional pipe easier. But that's for another discussion.
I don't think we should avoid Enum.filter
b/c of avoiding single pipes though. I think Enum.filter
expresses our intention clearer than the for
comprehension. How about
Enum.filter(load_aliases, &String.contains?(elem(&1, 0), pattern))
A side note, we have quite a few usages of single pipes throughout the file (and the codebase), does it mean we want to replace all of them?
@josevalim
A (weak) counter argument will be using single pipe makes adding additional pipe easier. But that's for another discussion.
I don't think we should avoid Enum.filter
b/c of avoiding single pipes though. I think Enum.filter
expresses our intention clearer than the for
comprehension. How about
Enum.filter(load_aliases, &String.contains?(elem(&1, 0), pattern))
A side note, we have quite a few usages of single pipes throughout the file (and the codebase), does it mean we want to replace all of them?
whatyouhide
Jun 27, 2018
Member
Eventually we want to replace all pipes, but we try to avoid small changes like that in the codebase in order to keep the number of changes down. Enum.filter/2
is also very much fine with me, I just don't love elem/2
since it hides what the tuple looks like. What about:
Enum.filter(load_aliases(), fn {name, _} -> String.contains?(name, pattern) end)
Eventually we want to replace all pipes, but we try to avoid small changes like that in the codebase in order to keep the number of changes down. Enum.filter/2
is also very much fine with me, I just don't love elem/2
since it hides what the tuple looks like. What about:
Enum.filter(load_aliases(), fn {name, _} -> String.contains?(name, pattern) end)
Sihui
Jun 27, 2018
Author
Contributor
good call! I not a fan of elem
either.
good call! I not a fan of elem
either.
@@ -116,6 +122,11 @@ defmodule Mix.Tasks.Help do | |||
|> Enum.filter(&(Mix.Task.moduledoc(&1) != false)) | |||
end | |||
|
|||
defp load_aliases() do | |||
Mix.Project.config()[:aliases] | |||
|> Enum.map(fn {k, v} -> {Atom.to_string(k), v} end) |
whatyouhide
Jun 27, 2018
Member
We try to avoid single pipes and single-letter variables. Maybe rewrite this as:
defp load_aliases() do
for {name, value} <- Mix.Project.config()[:aliases] do
{Atom.to_string(name), value}
end
end
We try to avoid single pipes and single-letter variables. Maybe rewrite this as:
defp load_aliases() do
for {name, value} <- Mix.Project.config()[:aliases] do
{Atom.to_string(name), value}
end
end
Sihui
Jun 27, 2018
Author
Contributor
updated to
defp load_aliases() do
Mix.Project.config()[:aliases]
|> Enum.map(fn {k, v} -> {Atom.to_string(k), v} end)
|> Map.new()
end
to make verbose_doc
easier. (turning it to a map also makes more sense.)
updated to
defp load_aliases() do
Mix.Project.config()[:aliases]
|> Enum.map(fn {k, v} -> {Atom.to_string(k), v} end)
|> Map.new()
end
to make verbose_doc
easier. (turning it to a map also makes more sense.)
fertapric
Jun 27, 2018
Member
What do you think about these names to address the single-letter variables?
defp load_aliases() do
Mix.Project.config()[:aliases]
|> Enum.map(fn {alias_name, alias_tasks} -> {Atom.to_string(alias_name), alias_tasks} end)
|> Map.new()
end
What do you think about these names to address the single-letter variables?
defp load_aliases() do
Mix.Project.config()[:aliases]
|> Enum.map(fn {alias_name, alias_tasks} -> {Atom.to_string(alias_name), alias_tasks} end)
|> Map.new()
end
whatyouhide
Jun 27, 2018
Member
You're doing two passes this way. I didn't know you needed a map but if you do, I'd much rather go with into: %{}
in the for
comprehension or just Map.new/2
:
aliases = Mix.Project.config()[:aliases]
Map.new(aliases, fn {alias_name, alias_tasks} -> {Atom.to_string(alias_name), alias_tasks} end)
(also, definitely let's avoid single-letter variables!)
You're doing two passes this way. I didn't know you needed a map but if you do, I'd much rather go with into: %{}
in the for
comprehension or just Map.new/2
:
aliases = Mix.Project.config()[:aliases]
Map.new(aliases, fn {alias_name, alias_tasks} -> {Atom.to_string(alias_name), alias_tasks} end)
(also, definitely let's avoid single-letter variables!)
Sihui
Jun 27, 2018
Author
Contributor
@whatyouhide I used map b/c I couldn't find an easy way to accomplish Map.has_key?(aliases, task)
. If there's a way without having to use map, I would love to learn that! :)
@whatyouhide I used map b/c I couldn't find an easy way to accomplish Map.has_key?(aliases, task)
. If there's a way without having to use map, I would love to learn that! :)
"[" <> task_strs <> "]" | ||
end | ||
|
||
defp task_str(task_name) when is_bitstring(task_name), do: task_name |
whatyouhide
Jun 27, 2018
Member
Instead of is_bitstring/1
, maybe use is_binary/1
here?
Instead of is_bitstring/1
, maybe use is_binary/1
here?
I would say that ad-hoc aliases would end-up being more clutter than well-structured tasks but I see your point. We should move the discussion about revamping aliases elsewhere then. Maybe a proposal to the elixir-lang-core mailing list as the scope of this particular issue is well defined based on how aliases work today and we should not go off-topic. |
assert output =~ "# mix nested.h\n\n" | ||
|
||
assert output =~ | ||
~r/Alias for \[#Function<.*\/1 in .*ComplexAliases.project\/0>, \"h foo bar\"\]\n/ |
Sihui
Jun 27, 2018
•
Author
Contributor
Couldn't find an easy way to format "nested.h": [&Mix.shell().info(inspect(&1)), "h foo bar"]
nicely.
Thoughts?
Couldn't find an easy way to format "nested.h": [&Mix.shell().info(inspect(&1)), "h foo bar"]
nicely.
Thoughts?
^^ Updated to:
|
## Arguments | ||
mix help - prints all tasks and their shortdoc | ||
mix help - prints all aliases, tasks and task shortdoc |
fertapric
Jun 27, 2018
Member
I think there is a typo here "prints all aliases, tasks and their shortdoc"
I think there is a typo here "prints all aliases, tasks and their shortdoc"
Sihui
Jun 27, 2018
Author
Contributor
I don't think we have shortdoc for aliases. shortdoc are marked with @shortdoc
I don't think we have shortdoc for aliases. shortdoc are marked with @shortdoc
fertapric
Jun 27, 2018
Member
"and their summary" or "and their short description"?
"and their summary" or "and their short description"?
@@ -4,11 +4,12 @@ defmodule Mix.Tasks.Help do | |||
@shortdoc "Prints help information for tasks" | |||
|
|||
@moduledoc """ | |||
Lists all tasks or prints the documentation for a given task. | |||
Lists all aliases and tasks or prints the documentation for a given task. |
fertapric
Jun 27, 2018
•
Member
Minor nitpick: I would change the order of "tasks" and "aliases" here (and below). For some reason, in my head 😅 , it makes more sense to put "tasks" at the beginning as they are more relevant for this specific task :) So, "Lists all tasks and aliases or prints the documentation for a given task or alias."
Minor nitpick: I would change the order of "tasks" and "aliases" here (and below). For some reason, in my head
@@ -29,6 +29,20 @@ defmodule Mix.Tasks.HelpTest do | |||
end | |||
end | |||
|
|||
test "help list alias task", context do |
fertapric
Jun 27, 2018
Member
Minor nitpick: I think it should be "lists", and maybe "help lists all aliases"?
Minor nitpick: I think it should be "lists", and maybe "help lists all aliases"?
|
||
in_tmp(context.test, fn -> | ||
Mix.Tasks.Help.run(["--search", "c"]) | ||
assert_received {:mix_shell, :info, ["mix c" <> _]} |
fertapric
Jun 27, 2018
Member
Could this test give false positives due to the match of other tasks like mix compile
?
Could this test give false positives due to the match of other tasks like mix compile
?
## Arguments | ||
mix help - prints all tasks and their shortdoc | ||
mix help - prints all aliases, tasks and task shortdoc | ||
mix help ALIAS - prints the definition for the given alias | ||
mix help TASK - prints full docs for the given task | ||
mix help --search PATTERN - prints all tasks that contain PATTERN in the name |
fertapric
Jun 27, 2018
Member
"prints all tasks and aliases that contain PATTERN in the name"?
"prints all tasks and aliases that contain PATTERN in the name"?
Sihui
Jun 27, 2018
Author
Contributor
good catch!
good catch!
|
||
in_tmp(context.test, fn -> | ||
Mix.Tasks.Help.run(["--search", "h"]) | ||
assert_received {:mix_shell, :info, ["mix h" <> _]} |
fertapric
Jun 27, 2018
Member
Unfortunately, this might also give false positives with mix help
itself, mix hex
or any mix hex.*
.
Unfortunately, this might also give false positives with mix help
itself, mix hex
or any mix hex.*
.
fertapric
Jun 27, 2018
•
Member
I would either include a space, or match on the message live above:
assert_received {:mix_shell, :info, ["mix h" <> message]}
assert message =~ ~r/# Alias defined in mix.exs/
I would either include a space, or match on the message live above:
assert_received {:mix_shell, :info, ["mix h" <> message]}
assert message =~ ~r/# Alias defined in mix.exs/
@josevalim makes sense moving to core-mailing list.
|
@josevalim @michalmuskala @whatyouhide Can this be merged? Anything else I need to do? |
We need two reviews to merge, so we are waiting on the second one. :) Once
somebody has some time available, they will review it and likely merge.
--
*José Valimwww.plataformatec.com.br
<http://www.plataformatec.com.br/>Founder and Director of R&D*
|
|> Enum.map(fn {k, _} -> Atom.to_string(k) end) | ||
Enum.map(Mix.Project.config()[:aliases], fn {alias_name, _} -> | ||
Atom.to_string(alias_name) | ||
end) |
whatyouhide
Jun 30, 2018
Member
Why not use aliases = load_aliases()
here too?
Why not use aliases = load_aliases()
here too?
whatyouhide
Jun 30, 2018
Member
I mean that and then Enum.sort(Map.to_list(aliases) ++ tasks)
.
I mean that and then Enum.sort(Map.to_list(aliases) ++ tasks)
.
Sihui
Jun 30, 2018
Author
Contributor
Map.to_list(aliases)
won't work because aliases is [{alias1, taks1}, {alias2, task2}, ...]
.
If we want to use load_aliases()
, we might need to do
aliases = Enum.map(load_aliases(), fn {alias_name, _} -> alias_name end)
for info <- Enum.sort(aliases ++ tasks) do
Mix.shell().info(info)
end
At that point, we might as well use Mix.Project.config()[:aliases]
directly, so we don't have to iterate twice.
Map.to_list(aliases)
won't work because aliases is [{alias1, taks1}, {alias2, task2}, ...]
.
If we want to use load_aliases()
, we might need to do
aliases = Enum.map(load_aliases(), fn {alias_name, _} -> alias_name end)
for info <- Enum.sort(aliases ++ tasks) do
Mix.shell().info(info)
end
At that point, we might as well use Mix.Project.config()[:aliases]
directly, so we don't have to iterate twice.
whatyouhide
Jun 30, 2018
Member
Oh I missed that it was just the alias name, sorry!
Oh I missed that it was just the alias name, sorry!
6b5cb70
into
elixir-lang:master
Thanks @Sihui! |
Thank you all for reviewing! |
ref: https://trello.com/c/9DzdXbp4/4-make-mix-help-list-aliases
cc @michalmuskala