Handle auto-completion line as argv, not a single argument

It's easier to detect a script name if passed as argv list.
Arguments can be parsed with quoted spaces (e.g. in directory path)
This commit is contained in:
Daniil Fedotov 2017-01-24 14:40:31 +00:00
parent b58da01921
commit a5857e13d8
4 changed files with 51 additions and 53 deletions

View File

@ -18,18 +18,16 @@ defmodule Rabbitmq.CLI.AutoComplete do
alias RabbitMQ.CLI.Core.Parser, as: Parser alias RabbitMQ.CLI.Core.Parser, as: Parser
alias RabbitMQ.CLI.Core.CommandModules, as: CommandModules alias RabbitMQ.CLI.Core.CommandModules, as: CommandModules
@spec complete(String.t) :: [String.t] @spec complete(String.t, [String.t]) :: [String.t]
def complete(script_name, str) do def complete(_, []) do
tokens = String.split(str, " ", trim: true) []
case List.last(tokens) do end
nil -> []; def complete(script_name, args) do
_last -> case Parser.parse_global(args) do
case Parser.parse_global(tokens) do %{script_name: _args_script_name} ->
%{script_name: _args_script_name} -> complete(args);
complete(tokens); _ ->
_ -> complete(["--script-name", script_name | args])
complete(["--script-name", script_name | tokens])
end
end end
end end
@ -48,6 +46,7 @@ defmodule Rabbitmq.CLI.AutoComplete do
{command, _} -> {command, _} ->
complete_command_opts(command, last_token) complete_command_opts(command, last_token)
end end
|> Enum.sort
end end
defp complete_default_opts(opt) do defp complete_default_opts(opt) do

View File

@ -27,12 +27,12 @@ defmodule RabbitMQCtl do
@type options() :: Map.t @type options() :: Map.t
@type command_result() :: {:error, ExitCodes.exit_code, term()} | term() @type command_result() :: {:error, ExitCodes.exit_code, term()} | term()
def main(["--auto-complete", str]) do def main(["--auto-complete" | []]) do
case extract_auto_complete_args(str) do handle_shutdown(:ok)
#Script name is not supported end
nil -> :ok; def main(["--auto-complete", script_name | args]) do
{script_name, args_string} -> auto_complete(script_name, args_string) script_basename = Path.basename(script_name)
end auto_complete(script_basename, args)
end end
def main(unparsed_command) do def main(unparsed_command) do
unparsed_command unparsed_command
@ -91,22 +91,8 @@ defmodule RabbitMQCtl do
exit_program(ExitCodes.exit_ok) exit_program(ExitCodes.exit_ok)
end end
def extract_auto_complete_args(str) do def auto_complete(script_name, args) do
Application.get_env(:rabbitmqctl, :scopes, []) Rabbitmq.CLI.AutoComplete.complete(script_name, args)
|> Enum.reduce(nil,
fn({key, _}, nil) ->
script_name = to_string(key)
case String.split(str, script_name <> " ", parts: 2) do
[_path, args] -> {script_name, args}
[_] -> nil
end;
# Skip remaining script names
(_, val) -> val
end)
end
def auto_complete(script_name, str) do
Rabbitmq.CLI.AutoComplete.complete(script_name, str)
|> Stream.map(&IO.puts/1) |> Stream.run |> Stream.map(&IO.puts/1) |> Stream.run
exit_program(ExitCodes.exit_ok) exit_program(ExitCodes.exit_ok)
end end

View File

@ -21,33 +21,41 @@ defmodule AutoCompleteTest do
test "Auto-completes a command" do test "Auto-completes a command" do
["canis_lupus", "canis_latrans", "canis_aureus"] = @subject.complete("rabbitmqctl", "canis") ["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis"])
["canis_lupus", "canis_latrans", "canis_aureus"] = @subject.complete("rabbitmqctl", "canis_") ["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis_"])
["canis_lupus", "canis_latrans"] = @subject.complete("rabbitmqctl", "canis_l") ["canis_latrans", "canis_lupus"] = @subject.complete("rabbitmqctl", ["canis_l"])
["canis_latrans"] = @subject.complete("rabbitmqctl", "canis_la") ["canis_latrans"] = @subject.complete("rabbitmqctl", ["canis_la"])
["canis_aureus"] = @subject.complete("rabbitmqctl", "canis_a") ["canis_aureus"] = @subject.complete("rabbitmqctl", ["canis_a"])
["canis_aureus"] = @subject.complete("rabbitmqctl", "--node foo --quet canis_a") ["canis_aureus"] = @subject.complete("rabbitmqctl", ["--node", "foo", "--quet", "canis_a"])
end end
test "Auto-completes default options if command is not specified" do test "Auto-completes default options if command is not specified" do
["--vhost"] = @subject.complete("rabbitmqctl", "--vh") ["--vhost"] = @subject.complete("rabbitmqctl", ["--vh"])
## Prints script_name as script-name ## Prints script_name as script-name
["--script-name"] = @subject.complete("rabbitmqctl", "--script") ["--script-name"] = @subject.complete("rabbitmqctl", ["--script"])
["--script-name"] = @subject.complete("rabbitmqctl", "--node foo --script") ["--script-name"] = @subject.complete("rabbitmqctl", ["--node", "foo", "--script"])
end end
test "Auto-completes the command options if full command is specified" do test "Auto-completes the command options if full command is specified" do
["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", "canis_lupus -") ["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "-"])
["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", "canis_lupus --") ["--colour", "--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "--"])
["--dingo", "--dog"] = @subject.complete("rabbitmqctl", "canis_lupus --d") ["--dingo", "--dog"] = @subject.complete("rabbitmqctl", ["canis_lupus", "--d"])
end end
test "Auto-completes scoped command" do test "Auto-completes scoped command" do
["enable"] = @subject.complete("rabbitmq-plugins", "enab") ["enable"] = @subject.complete("rabbitmq-plugins", ["enab"])
scopes = Application.get_env(:rabbitmqctl, :scopes)
scopes_with_wolf = Keyword.put(scopes, :rabbitmq_wolf, :wolf)
Application.put_env(:rabbitmqctl, :scopes, scopes_with_wolf)
on_exit(fn() ->
Application.put_env(:rabbitmqctl, :scopes, scopes)
end)
["canis_aureus", "canis_latrans", "canis_lupus"] = @subject.complete("rabbitmq_wolf", ["canis"])
end end
test "Auto-completes scoped command with --script-name flag" do test "Auto-completes scoped command with --script-name flag" do
["enable"] = @subject.complete("rabbitmqctl", "--script-name rabbitmq-plugins enab") ["enable"] = @subject.complete("rabbitmqctl", ["--script-name", "rabbitmq-plugins", "enab"])
end end
end end

View File

@ -165,11 +165,16 @@ defmodule RabbitMQCtlTest do
## ------------------------- Auto-complete ------------------------------------ ## ------------------------- Auto-complete ------------------------------------
test "rabbitmqctl auto-completes commands" do test "rabbitmqctl auto-completes commands" do
check_output(["--auto-complete", "rabbitmqctl list_q"], "list_queues\n") check_output(["--auto-complete", "rabbitmqctl", "list_q"], "list_queues\n")
check_output(["--auto-complete", "/usr/bin/rabbitmqctl list_q"], "list_queues\n") check_output(["--auto-complete", "/usr/bin/rabbitmqctl", "list_q"], "list_queues\n")
check_output(["--auto-complete", "/my/custom/path/rabbitmqctl list_q"], "list_queues\n") check_output(["--auto-complete", "/my/custom/path/rabbitmqctl", "list_q"], "list_queues\n")
check_output(["--auto-complete", "rabbitmq-plugins enab"], "enable\n") check_output(["--auto-complete", "rabbitmq-plugins", "enab"], "enable\n")
check_output(["--auto-complete", "/path/to/rabbitmq-plugins enab"], "enable\n") check_output(["--auto-complete", "/path/to/rabbitmq-plugins", "enab"], "enable\n")
end
test "invalid script name does not autocomplete" do
check_output(["--auto-complete", "rabbitmqinvalid list"], "")
check_output(["--auto-complete", "rabbitmqinvalid --script-name rabbitmqctl list"], "")
end end
defp check_output(cmd, out) do defp check_output(cmd, out) do