lib/cli/kit/resolver.rb in cli-kit-3.0.0.pre vs lib/cli/kit/resolver.rb in cli-kit-3.0.0

- old
+ new

@@ -1,24 +1,60 @@ require 'cli/kit' module CLI module Kit class Resolver - def initialize(command_registry:, error_handler:) + def initialize(tool_name:, command_registry:) + @tool_name = tool_name @command_registry = command_registry - @error_handler = error_handler end def call(args) args = args.dup command_name = args.shift - @error_handler.handle_abort do - command, command_name = @command_registry.lookup_command(command_name) - return [command, command_name, args] + command, resolved_name = @command_registry.lookup_command(command_name) + + if command.nil? + command_not_found(command_name) + raise CLI::Kit::AbortSilent # Already output message end - exit CLI::Kit::EXIT_FAILURE_BUT_NOT_BUG + [command, resolved_name, args] + end + + private + + def command_not_found(name) + CLI::UI::Frame.open("Command not found", color: :red, timing: false) do + $stderr.puts(CLI::UI.fmt("{{command:#{@tool_name} #{name}}} was not found")) + end + + cmds = commands_and_aliases + if cmds.all? { |cmd| cmd.is_a?(String) } + possible_matches = cmds.min_by(2) do |cmd| + CLI::Kit::Levenshtein.distance(cmd, name) + end + + # We don't want to match against any possible command + # so reject anything that is too far away + possible_matches.reject! do |possible_match| + CLI::Kit::Levenshtein.distance(possible_match, name) > 3 + end + + # If we have any matches left, tell the user + if possible_matches.any? + CLI::UI::Frame.open("{{bold:Did you mean?}}", timing: false, color: :blue) do + possible_matches.each do |possible_match| + $stderr.puts CLI::UI.fmt("{{command:#{@tool_name} #{possible_match}}}") + end + end + end + end + end + + def commands_and_aliases + @command_registry.command_names + @command_registry.aliases.keys end end end end