Fix zsh for DirectiveNoSpace and DirectiveNoFileComp (#1213)

Fixes #1211

When handling ShellCompDirectiveNoSpace we must still properly handle
descriptions.  To do so we cannot simply use 'compadd', but must use
zsh's '_describe' function.

Also, when handling ShellCompDirectiveNoSpace we cannot assume that
only a single completion will be given to the script.  In fact,
ValidArgsFunction can return multiple completions, even if they don't
match the 'toComplete' argument prefix.  Therefore, we cannot use the
number of completions received in the completion script to determine
if we should activate the "no space" directive.  Instead, we can leave
it all to the '_describe' function.

Fixes #1212

When handling ShellCompDirectiveNoFileComp we cannot base ourself on
the script receiving no valid completion. In fact,
ValidArgsFunction can return multiple completions, even if they don't
match the 'toComplete' argument prefix at all.  Therefore, we cannot use
the number of completions received by the completion script to determine
if we should activate the "no file comp" directive.  Instead, we can
check if the '_describe' function has found any completions.

Finally, it is important for the script to return the return code of the
called zsh functions (_describe, _arguments).  This tells zsh if
completions were found or not, which if not, will trigger different
matching attempts, such as matching what the user typed with the the
content of possible completions (instead of just as the prefix).

Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>
This commit is contained in:
Marc Khouzam 2021-05-03 12:54:00 -04:00 committed by GitHub
parent 2d94892a8b
commit 95d23d24ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 21 deletions

View File

@ -175,10 +175,6 @@ func (c *Command) initCompleteCmd(args []string) {
fmt.Fprintln(finalCmd.OutOrStdout(), comp) fmt.Fprintln(finalCmd.OutOrStdout(), comp)
} }
if directive >= shellCompDirectiveMaxValue {
directive = ShellCompDirectiveDefault
}
// As the last printout, print the completion directive for the completion script to parse. // As the last printout, print the completion directive for the completion script to parse.
// The directive integer must be that last character following a single colon (:). // The directive integer must be that last character following a single colon (:).
// The completion script expects :<directive> // The completion script expects :<directive>

View File

@ -95,7 +95,7 @@ _%[1]s()
local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterFileExt=%[6]d
local shellCompDirectiveFilterDirs=%[7]d local shellCompDirectiveFilterDirs=%[7]d
local lastParam lastChar flagPrefix requestComp out directive compCount comp lastComp local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
local -a completions local -a completions
__%[1]s_debug "\n========= starting completion logic ==========" __%[1]s_debug "\n========= starting completion logic =========="
@ -163,7 +163,6 @@ _%[1]s()
return return
fi fi
compCount=0
while IFS='\n' read -r comp; do while IFS='\n' read -r comp; do
if [ -n "$comp" ]; then if [ -n "$comp" ]; then
# If requested, completions are returned with a description. # If requested, completions are returned with a description.
@ -175,13 +174,17 @@ _%[1]s()
local tab=$(printf '\t') local tab=$(printf '\t')
comp=${comp//$tab/:} comp=${comp//$tab/:}
((compCount++))
__%[1]s_debug "Adding completion: ${comp}" __%[1]s_debug "Adding completion: ${comp}"
completions+=${comp} completions+=${comp}
lastComp=$comp lastComp=$comp
fi fi
done < <(printf "%%s\n" "${out[@]}") done < <(printf "%%s\n" "${out[@]}")
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
__%[1]s_debug "Activating nospace."
noSpace="-S ''"
fi
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
# File extension filtering # File extension filtering
local filteringCmd local filteringCmd
@ -208,25 +211,40 @@ _%[1]s()
__%[1]s_debug "Listing directories in ." __%[1]s_debug "Listing directories in ."
fi fi
local result
_arguments '*:dirname:_files -/'" ${flagPrefix}" _arguments '*:dirname:_files -/'" ${flagPrefix}"
result=$?
if [ -n "$subdir" ]; then if [ -n "$subdir" ]; then
popd >/dev/null 2>&1 popd >/dev/null 2>&1
fi fi
elif [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ] && [ ${compCount} -eq 1 ]; then return $result
__%[1]s_debug "Activating nospace."
# We can use compadd here as there is no description when
# there is only one completion.
compadd -S '' "${lastComp}"
elif [ ${compCount} -eq 0 ]; then
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
__%[1]s_debug "deactivating file completion"
else
# Perform file completion
__%[1]s_debug "activating file completion"
_arguments '*:filename:_files'" ${flagPrefix}"
fi
else else
_describe "completions" completions $(echo $flagPrefix) __%[1]s_debug "Calling _describe"
if eval _describe "completions" completions $flagPrefix $noSpace; then
__%[1]s_debug "_describe found some completions"
# Return the success of having called _describe
return 0
else
__%[1]s_debug "_describe did not find completions."
__%[1]s_debug "Checking if we should do file completion."
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
__%[1]s_debug "deactivating file completion"
# We must return an error code here to let zsh know that there were no
# completions found by _describe; this is what will trigger other
# matching algorithms to attempt to find completions.
# For example zsh can match letters in the middle of words.
return 1
else
# Perform file completion
__%[1]s_debug "Activating file completion"
# We must return the result of this command, so it must be the
# last command, or else we must store its result to return it.
_arguments '*:filename:_files'" ${flagPrefix}"
fi
fi
fi fi
} }