Skip to content

Commit

Permalink
Fix zsh for DirectiveNoSpace and DirectiveNoFileComp
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
marckhouzam committed Jan 31, 2021
1 parent 9df156e commit a89e419
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 20 deletions.
4 changes: 0 additions & 4 deletions custom_completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,6 @@ func (c *Command) initCompleteCmd(args []string) {
fmt.Fprintln(finalCmd.OutOrStdout(), comp)
}

if directive >= shellCompDirectiveMaxValue {
directive = ShellCompDirectiveDefault
}

// 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 completion script expects :<directive>
Expand Down
50 changes: 34 additions & 16 deletions zsh_completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ _%[1]s()
local shellCompDirectiveFilterFileExt=%[6]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
__%[1]s_debug "\n========= starting completion logic =========="
Expand Down Expand Up @@ -163,7 +163,6 @@ _%[1]s()
return
fi
compCount=0
while IFS='\n' read -r comp; do
if [ -n "$comp" ]; then
# If requested, completions are returned with a description.
Expand All @@ -175,13 +174,17 @@ _%[1]s()
local tab=$(printf '\t')
comp=${comp//$tab/:}
((compCount++))
__%[1]s_debug "Adding completion: ${comp}"
completions+=${comp}
lastComp=$comp
fi
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
# File extension filtering
local filteringCmd
Expand All @@ -208,25 +211,40 @@ _%[1]s()
__%[1]s_debug "Listing directories in ."
fi
local result
_arguments '*:dirname:_files -/'" ${flagPrefix}"
result=$?
if [ -n "$subdir" ]; then
popd >/dev/null 2>&1
fi
elif [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ] && [ ${compCount} -eq 1 ]; then
__%[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"
return $result
else
__%[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
# Perform file completion
__%[1]s_debug "activating file completion"
_arguments '*:filename:_files'" ${flagPrefix}"
__%[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
else
_describe "completions" completions $(echo $flagPrefix)
fi
}
Expand Down

0 comments on commit a89e419

Please sign in to comment.