completion: add alias handling

Bash completion is now smarter about handling aliases. In particular, if all completions
for some input command are aliased to the same thing, we'll just complete with that thing.

If you've already *typed* the full alias for a command, we'll complete the alias.

So, for example, here there's more than one real command involved, so all aliases are
shown:

```console
$ spack con
concretise    concretize    config        containerise  containerize
```

Here, there are two possibilities: `concretise` and `concretize`, but both map to
`concretize` so we just complete that:

```console
$ spack conc
concretize
```

And here, the user has already typed `concretis`, so we just go with it as there is only
one option:

```console
 spack concretis
concretise
```
This commit is contained in:
Todd Gamblin 2023-08-18 16:53:00 -07:00
parent a3ecd7efed
commit 396f219011
4 changed files with 103 additions and 0 deletions

View File

@ -812,6 +812,9 @@ def bash(args: Namespace, out: IO) -> None:
parser = spack.main.make_argument_parser()
spack.main.add_all_commands(parser)
aliases = ";".join(f"{key}:{val}" for key, val in spack.main.aliases.items())
out.write(f'SPACK_ALIASES="{aliases}"\n\n')
writer = BashCompletionWriter(parser.prog, out, args.aliases)
writer.write(parser)

View File

@ -139,6 +139,9 @@ _bash_completion_spack() {
$subfunction
COMPREPLY=($(compgen -W "$SPACK_COMPREPLY" -- "$cur"))
fi
# if every completion is an alias for the same thing, just return that thing.
_spack_compress_aliases
}
# Helper functions for subcommands
@ -328,6 +331,49 @@ _spacktivate() {
_spack_env_activate
}
# Simple function to get the spack alias for a command
_spack_get_alias() {
local possible_alias="${1-}"
local IFS=";"
# spack aliases are a ;-separated list of :-separated pairs
for item in $SPACK_ALIASES; do
# maps a possible alias to its command
eval "local real_command=\"\${item#*${possible_alias}:}\""
if [ "$real_command" != "$item" ]; then
SPACK_ALIAS="$real_command"
return
fi
done
# no alias found -- just return $1
SPACK_ALIAS="$possible_alias"
}
# If all commands in COMPREPLY alias to the same thing, set COMPREPLY to
# just the real command, not the aliases.
_spack_compress_aliases() {
# if there's only one thing, don't bother compressing aliases; complete the alias
if [ "${#COMPREPLY[@]}" == "1" ]; then
return
fi
# get the alias of the first thing in the list of completions
_spack_get_alias "${COMPREPLY[0]}"
local first_alias="$SPACK_ALIAS"
# if anything in the list would alias to something different, stop
for comp in "${COMPREPLY[@]:1}"; do
_spack_get_alias "$comp"
if [ "$SPACK_ALIAS" != "$first_alias" ]; then
return
fi
done
# all commands alias to first alias; just return that
COMPREPLY=("$first_alias")
}
# Spack commands
#
# Everything below here is auto-generated.

View File

@ -61,6 +61,12 @@ contains 'python' _spack_completions spack extensions ''
contains 'hdf5' _spack_completions spack -d install --jobs 8 ''
contains 'hdf5' _spack_completions spack install -v ''
title 'Testing alias handling'
contains 'concretize' _spack_completions spack c
contains 'concretise' _spack_completions spack c
contains 'concretize' _spack_completions spack conc
does_not_contain 'concretise' _spack_completions spack conc
# XFAIL: Fails for Python 2.6 because pkg_resources not found?
#contains 'compilers.py' _spack_completions spack unit-test ''

View File

@ -139,6 +139,9 @@ _bash_completion_spack() {
$subfunction
COMPREPLY=($(compgen -W "$SPACK_COMPREPLY" -- "$cur"))
fi
# if every completion is an alias for the same thing, just return that thing.
_spack_compress_aliases
}
# Helper functions for subcommands
@ -328,9 +331,54 @@ _spacktivate() {
_spack_env_activate
}
# Simple function to get the spack alias for a command
_spack_get_alias() {
local possible_alias="${1-}"
local IFS=";"
# spack aliases are a ;-separated list of :-separated pairs
for item in $SPACK_ALIASES; do
# maps a possible alias to its command
eval "local real_command=\"\${item#*${possible_alias}:}\""
if [ "$real_command" != "$item" ]; then
SPACK_ALIAS="$real_command"
return
fi
done
# no alias found -- just return $1
SPACK_ALIAS="$possible_alias"
}
# If all commands in COMPREPLY alias to the same thing, set COMPREPLY to
# just the real command, not the aliases.
_spack_compress_aliases() {
# if there's only one thing, don't bother compressing aliases; complete the alias
if [ "${#COMPREPLY[@]}" == "1" ]; then
return
fi
# get the alias of the first thing in the list of completions
_spack_get_alias "${COMPREPLY[0]}"
local first_alias="$SPACK_ALIAS"
# if anything in the list would alias to something different, stop
for comp in "${COMPREPLY[@]:1}"; do
_spack_get_alias "$comp"
if [ "$SPACK_ALIAS" != "$first_alias" ]; then
return
fi
done
# all commands alias to first alias; just return that
COMPREPLY=("$first_alias")
}
# Spack commands
#
# Everything below here is auto-generated.
SPACK_ALIASES="concretise:concretize;containerise:containerize;rm:remove"
_spack() {
if $list_options