minimal zsh completion (#20253)

Since zsh can load bash completion files natively, seems reasonable to just turn this on.
The only changes are to switch from `type -t` which zsh doesn't support to using `type`
with a regex and adding a new arm to the sourcing of the completions to allow it to work
for zsh as well as bash.

Could use more bash/dash/etc testing probably, but everything I've thought to try has
worked so far.

Notes:
* unit-test zsh support, fix issues
Specifically fixed word splitting in completion-test, use a different
method to apply sh emulation to zsh loaded bash completion, and fixed
an incompatibility in regex operator quoting requirements.

* compinit now ignores insecure directories
Completion isn't meant to be enabled in non-interactive environments, so
by default compinit will ask the user if they want to ignore insecure
directories or load them anyway.  To pass the spack unit tests in GH
actions, this prompt must be disabled, so ignore explicitly until a
better solution can be found.

* debug functions test also requires bash emulation
COMP_WORDS is a bash-ism that zsh doesn't natively support, turn on
emulation for just that section of tests to allow the comparison to
work.  Does not change the behavior of the functions themselves since
they are already pinned to sh emulation elsewhere.

* propagate change to .in file

* fix comment and update script based on .in
This commit is contained in:
Tom Scogland 2020-12-18 17:26:15 -08:00 committed by GitHub
parent 517413c125
commit 71c77fa8fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 30 deletions

View File

@ -37,6 +37,17 @@
#
# See `man bash` for more details.
if test -n "${ZSH_VERSION:-}" ; then
if [[ "$(emulate)" = zsh ]] ; then
# ensure base completion support is enabled, ignore insecure directories
autoload -U +X compinit && compinit -i
# ensure bash compatible completion support is enabled
autoload -U +X bashcompinit && bashcompinit
emulate sh -c "source '$0:A'"
return # stop interpreting file
fi
fi
# Bash programmable completion for Spack
_bash_completion_spack() {
# In all following examples, let the cursor be denoted by brackets, i.e. []
@ -117,7 +128,9 @@ _bash_completion_spack() {
#_test_vars >> temp
# Make sure function exists before calling it
if [[ "$(type -t $subfunction)" == "function" ]]
local rgx #this dance is necessary to cover bash and zsh regex
rgx="$subfunction.*function.* "
if [[ "$(type $subfunction 2>&1)" =~ $rgx ]]
then
$subfunction
COMPREPLY=($(compgen -W "$SPACK_COMPREPLY" -- "$cur"))

View File

@ -31,13 +31,18 @@ title "Testing spack-completion.$_sp_shell with $_sp_shell"
succeeds which spack
title 'Testing all subcommands'
while IFS= read -r line
# read line into an array portably
READ="read -ra line"
if [ -n "${ZSH_VERSION:-}" ]; then
READ=(read -rA line)
fi
while IFS=' ' $READ
do
# Test that completion with no args works
succeeds _spack_completions ${line[*]} ''
succeeds _spack_completions "${line[@]}" ''
# Test that completion with flags works
contains '-h --help' _spack_completions ${line[*]} -
contains '-h --help' _spack_completions "${line[@]}" -
done <<- EOF
$(spack commands --aliases --format=subcommands)
EOF
@ -58,32 +63,39 @@ contains 'hdf5' _spack_completions spack install -v ''
# XFAIL: Fails for Python 2.6 because pkg_resources not found?
#contains 'compilers.py' _spack_completions spack unit-test ''
title 'Testing debugging functions'
_test_debug_functions() {
title 'Testing debugging functions'
# This is a particularly tricky case that involves the following situation:
# `spack -d [] install `
# Here, [] represents the cursor, which is in the middle of the line.
# We should tab-complete optional flags for `spack`, not optional flags for
# `spack install` or package names.
COMP_LINE='spack -d install '
COMP_POINT=9
COMP_WORDS=(spack -d install)
COMP_CWORD=2
COMP_KEY=9
COMP_TYPE=64
if [ -n "${ZSH_VERSION:-}" ]; then
emulate -L sh
fi
_bash_completion_spack
contains "--all-help" echo "${COMPREPLY[@]}"
# This is a particularly tricky case that involves the following situation:
# `spack -d [] install `
# Here, [] represents the cursor, which is in the middle of the line.
# We should tab-complete optional flags for `spack`, not optional flags for
# `spack install` or package names.
COMP_LINE='spack -d install '
COMP_POINT=9
COMP_WORDS=(spack -d install)
COMP_CWORD=2
COMP_KEY=9
COMP_TYPE=64
contains "['spack', '-d', 'install', '']" _pretty_print COMP_WORDS[@]
_bash_completion_spack
contains "--all-help" echo "${COMPREPLY[@]}"
# Set the rest of the intermediate variables manually
COMP_WORDS_NO_FLAGS=(spack install)
COMP_CWORD_NO_FLAGS=1
subfunction=_spack
cur=
contains "['spack', '-d', 'install', '']" _pretty_print COMP_WORDS[@]
list_options=true
contains "'True'" _test_vars
list_options=false
contains "'False'" _test_vars
# Set the rest of the intermediate variables manually
COMP_WORDS_NO_FLAGS=(spack install)
COMP_CWORD_NO_FLAGS=1
subfunction=_spack
cur=
list_options=true
contains "'True'" _test_vars
list_options=false
contains "'False'" _test_vars
}
_test_debug_functions

View File

@ -43,6 +43,7 @@ fi
# Run the test scripts for their output (these will print nicely)
zsh "$QA_DIR/setup-env-test.sh"
zsh "$QA_DIR/completion-test.sh"
dash "$QA_DIR/setup-env-test.sh"
# Run fish tests

View File

@ -371,7 +371,7 @@ _sp_multi_pathadd MODULEPATH "$_sp_tcl_roots"
# Add programmable tab completion for Bash
#
if [ "$_sp_shell" = bash ]; then
if test "$_sp_shell" = bash || test -n "${ZSH_VERSION:-}"; then
source $_sp_share_dir/spack-completion.bash
fi

View File

@ -37,6 +37,17 @@
#
# See `man bash` for more details.
if test -n "${ZSH_VERSION:-}" ; then
if [[ "$(emulate)" = zsh ]] ; then
# ensure base completion support is enabled, ignore insecure directories
autoload -U +X compinit && compinit -i
# ensure bash compatible completion support is enabled
autoload -U +X bashcompinit && bashcompinit
emulate sh -c "source '$0:A'"
return # stop interpreting file
fi
fi
# Bash programmable completion for Spack
_bash_completion_spack() {
# In all following examples, let the cursor be denoted by brackets, i.e. []
@ -117,7 +128,9 @@ _bash_completion_spack() {
#_test_vars >> temp
# Make sure function exists before calling it
if [[ "$(type -t $subfunction)" == "function" ]]
local rgx #this dance is necessary to cover bash and zsh regex
rgx="$subfunction.*function.* "
if [[ "$(type $subfunction 2>&1)" =~ $rgx ]]
then
$subfunction
COMPREPLY=($(compgen -W "$SPACK_COMPREPLY" -- "$cur"))