From 5082a17b81771f667e3050ddb50b2d467081d601 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 7 Jun 2018 11:59:02 +0200 Subject: [PATCH] bash completion: complete fully specified command This contains 2 functional changes: First: resolve_cmd no longer keeps a hash of which arguments were expanded. This information is not required and not used properly: For one it would conflict if the same word appeared twice in a longer subcommand, and secondly we lose the information when recursing into an alias anyway. And lastly, we do not support tab completing multiple parameters simultaneously anyway (as in, `pveum u a` does not become `pveum user add`). So now we simply return the expanded version of the last command or undef if it was unknown in place of the hash we returned previously. The second change is how we use the new returned value: Previously if resolve_cmd() returned a new subcommand in $def we skipped over finishing the last word. Of course, if the command was already fully specified (but no space put after it), we already considered it complete and returned the new $def. This condition can be detected as in this case the $prev command equals the $cur command. (Additionally, the $cur command is either '' (after the space) or also the $prev command (before the space), but checking this would only be required when the same word can actually appear multiple times in a row in a sub command chain...) This case now takes precedence over looking through the nested $def, so that bash will put the space after a full command which requires another subcommand to be added. Signed-off-by: Wolfgang Bumiller --- src/PVE/CLIHandler.pm | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/PVE/CLIHandler.pm b/src/PVE/CLIHandler.pm index 62a61a7..8c911b2 100644 --- a/src/PVE/CLIHandler.pm +++ b/src/PVE/CLIHandler.pm @@ -52,11 +52,12 @@ my $abort = sub { my $expand_command_name = sub { my ($def, $cmd) = @_; - if (!$def->{$cmd}) { - my @expanded = grep { /^\Q$cmd\E/ } keys %$def; - return $expanded[0] if scalar(@expanded) == 1; # enforce exact match - } - return $cmd; + return $cmd if exists $def->{$cmd}; # command is already complete + + my @expanded = grep { /^\Q$cmd\E/ } keys %$def; + return $expanded[0] if scalar(@expanded) == 1; # enforce exact match + + return undef; }; my $get_commands = sub { @@ -78,20 +79,27 @@ sub resolve_cmd { my $cmdstr = $exename; if (ref($argv) eq 'ARRAY') { - my $expanded = {}; + my $expanded_last_arg; my $last_arg_id = scalar(@$argv) - 1; for my $i (0..$last_arg_id) { $cmd = $expand_command_name->($def, $argv->[$i]); + if (defined($cmd)) { + # If the argument was expanded (or was already complete) and it + # is the final argument, tell our caller about it: + $expanded_last_arg = $cmd if $i == $last_arg_id; + } else { + # Otherwise continue with the unexpanded version of it. + $cmd = $argv->[$i]; + } $cmdstr .= " $cmd"; - $expanded->{$argv->[$i]} = $cmd if $cmd ne $argv->[$i]; last if !defined($def->{$cmd}); $def = $def->{$cmd}; if (ref($def) eq 'ARRAY') { # could expand to a real command, rest of $argv are its arguments my $cmd_args = [ @$argv[$i+1..$last_arg_id] ]; - return ($cmd, $def, $cmd_args, $expanded, $cmdstr); + return ($cmd, $def, $cmd_args, $expanded_last_arg, $cmdstr); } if (defined($def->{alias})) { @@ -104,7 +112,7 @@ sub resolve_cmd { # got either a special command (bashcomplete, verifyapi) or an unknown # cmd, just return first entry as cmd and the rest of $argv as cmd_arg my $cmd_args = [ @$argv[1..$last_arg_id] ]; - return ($argv->[0], $def, $cmd_args, $expanded, $cmdstr); + return ($argv->[0], $def, $cmd_args, $expanded_last_arg, $cmdstr); } return ($cmd, $def, undef, undef, $cmdstr); } @@ -313,12 +321,13 @@ my $print_bash_completion = sub { if (!$simple_cmd) { ($cmd, $def, $args, my $expanded) = resolve_cmd($args); - if (ref($def) eq 'HASH') { - &$print_result(@{$get_commands->($def)}); + if (defined($expanded) && $prev ne $expanded) { + print "$expanded\n"; return; } - if (my $expanded_cmd = $expanded->{$cur}) { - print "$expanded_cmd\n"; + + if (ref($def) eq 'HASH') { + &$print_result(@{$get_commands->($def)}); return; } } -- 2.39.2