5
0
mirror of git://git.proxmox.com/git/pve-common.git synced 2024-12-24 01:33:48 +03:00

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<tab>` 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 <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2018-06-07 11:59:02 +02:00 committed by Dietmar Maurer
parent 0ed806982c
commit 5082a17b81

View File

@ -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;
}
}