Suggest commands by Levenshtein distance
This commit is contained in:
parent
f82a274902
commit
80dc953ee5
|
|
@ -16,7 +16,8 @@
|
|||
alias RabbitMQ.CLI.Core.CommandModules, as: CommandModules
|
||||
|
||||
defmodule RabbitMQ.CLI.Core.Parser do
|
||||
alias RabbitMQ.CLI.Core.Helpers, as: Helpers
|
||||
|
||||
@levenshtein_distance_limit 5
|
||||
|
||||
@spec parse(String.t) :: {command :: :no_command | atom(),
|
||||
command_name :: String.t,
|
||||
|
|
@ -33,6 +34,8 @@ defmodule RabbitMQ.CLI.Core.Parser do
|
|||
case command_module do
|
||||
nil ->
|
||||
{:no_command, command_name, arguments, options, invalid};
|
||||
{:suggest, _} = suggest ->
|
||||
{suggest, command_name, arguments, options, invalid};
|
||||
command_module when is_atom(command_module) ->
|
||||
{[^command_name | cmd_arguments], cmd_options, cmd_invalid} =
|
||||
parse_command_specific(input, command_module)
|
||||
|
|
@ -43,12 +46,35 @@ defmodule RabbitMQ.CLI.Core.Parser do
|
|||
defp look_up_command(parsed_args) do
|
||||
case parsed_args do
|
||||
[cmd_name | arguments] ->
|
||||
{cmd_name, CommandModules.module_map[cmd_name], arguments}
|
||||
module_map = CommandModules.module_map
|
||||
command = case module_map[cmd_name] do
|
||||
nil -> closest_similar_command(cmd_name, module_map)
|
||||
command -> command
|
||||
end
|
||||
{cmd_name, command, arguments}
|
||||
[] ->
|
||||
{"", nil, []}
|
||||
end
|
||||
end
|
||||
|
||||
defp closest_similar_command(_cmd_name, empty) when empty == %{} do
|
||||
nil
|
||||
end
|
||||
defp closest_similar_command(cmd_name, module_map) do
|
||||
suggestion = module_map
|
||||
|> Map.keys
|
||||
|> Enum.map(fn(cmd) ->
|
||||
{cmd, Simetric.Levenshtein.compare(cmd, cmd_name)}
|
||||
end)
|
||||
|> Enum.min_by(fn({_,distance}) -> distance end)
|
||||
case suggestion do
|
||||
{cmd, distance} when distance < @levenshtein_distance_limit ->
|
||||
{:suggest, cmd};
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def parse_command_specific(input, command) do
|
||||
switches = build_switches(default_switches(), command)
|
||||
aliases = build_aliases(default_aliases(), command)
|
||||
|
|
|
|||
|
|
@ -36,11 +36,14 @@ defmodule RabbitMQCtl do
|
|||
end
|
||||
def main(unparsed_command) do
|
||||
{command, command_name, arguments, parsed_options, invalid} = parse(unparsed_command)
|
||||
|
||||
case {command, invalid} do
|
||||
{:no_command, _} ->
|
||||
usage_string = HelpCommand.all_usage()
|
||||
{:error, ExitCodes.exit_usage, usage_string};
|
||||
{{:suggest, suggested}, _} ->
|
||||
suggest_message = "Command '#{command_name}' not found."<>
|
||||
" Did you mean '#{suggested}'?"
|
||||
{:error, ExitCodes.exit_usage, suggest_message};
|
||||
{_, [_|_]} ->
|
||||
validation_error({:bad_option, invalid}, command_name, unparsed_command);
|
||||
_ ->
|
||||
|
|
@ -225,10 +228,6 @@ defmodule RabbitMQCtl do
|
|||
end
|
||||
defp format_validation_error(err, _), do: inspect err
|
||||
|
||||
defp command_flags(command) do
|
||||
apply_if_exported(command, :switches, [], []) |> Keyword.keys
|
||||
end
|
||||
|
||||
defp exit_program(code) do
|
||||
:net_kernel.stop
|
||||
exit({:shutdown, code})
|
||||
|
|
|
|||
|
|
@ -110,16 +110,10 @@ defmodule RabbitMQCtl.MixfileBase do
|
|||
compile: make,
|
||||
override: true
|
||||
},
|
||||
{
|
||||
:amqp, "~> 0.1.5",
|
||||
only: :test
|
||||
},
|
||||
{
|
||||
:json, "~> 1.0.0"
|
||||
},
|
||||
{
|
||||
:csv, "~> 1.4.2"
|
||||
}
|
||||
{:amqp, "~> 0.1.5", only: :test},
|
||||
{:json, "~> 1.0.0"},
|
||||
{:csv, "~> 1.4.2"},
|
||||
{:simetric, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue