Skip to content

Commit

Permalink
added keep order to bash, zsh, pwsh & fish
Browse files Browse the repository at this point in the history
Closes #1900

Merge spf13/cobra#1903
  • Loading branch information
h4ck3rk3y authored and hoshsadiq committed Apr 14, 2023
1 parent 1e04310 commit 0a524d2
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 9 deletions.
9 changes: 8 additions & 1 deletion completions.go
Expand Up @@ -73,8 +73,11 @@ const (
// zflag option has been provided as a convenience.
ShellCompDirectiveFilterDirs

// ===========================================================================
// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
// in which the completions are provided
ShellCompDirectiveKeepOrder

// ===========================================================================
// All directives using iota should be above this one.
// For internal use.
shellCompDirectiveMaxValue
Expand Down Expand Up @@ -139,6 +142,9 @@ func (d ShellCompDirective) string() string {
if d&ShellCompDirectiveFilterDirs != 0 {
directives = append(directives, "ShellCompDirectiveFilterDirs")
}
if d&ShellCompDirectiveKeepOrder != 0 {
directives = append(directives, "ShellCompDirectiveKeepOrder")
}
if len(directives) == 0 {
directives = append(directives, "ShellCompDirectiveDefault")
}
Expand Down Expand Up @@ -769,5 +775,6 @@ func genTemplateCompletion(buf io.Writer, templateFile string, name string, incl
"ShellCompDirectiveNoFileComp": ShellCompDirectiveNoFileComp,
"ShellCompDirectiveFilterFileExt": ShellCompDirectiveFilterFileExt,
"ShellCompDirectiveFilterDirs": ShellCompDirectiveFilterDirs,
"ShellCompDirectiveKeepOrder": ShellCompDirectiveKeepOrder,
})
}
4 changes: 4 additions & 0 deletions site/content/completions/completion.md
Expand Up @@ -158,6 +158,10 @@ ShellCompDirectiveFilterFileExt
// obtain the same behavior but only for flags. The function FlagOptDirname
// zflag option has been provided as a convenience.
ShellCompDirectiveFilterDirs

// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
// in which the completions are provided
ShellCompDirectiveKeepOrder
```

***Note***: When using the `ValidArgsFunction`, Zulu will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Zulu will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function.
Expand Down
14 changes: 14 additions & 0 deletions templates/completion.bash.gotmpl
Expand Up @@ -66,6 +66,7 @@ __{{ .CMDVarName }}_process_completion_results() {
local shellCompDirectiveNoFileComp={{ .ShellCompDirectiveNoFileComp }}
local shellCompDirectiveFilterFileExt={{ .ShellCompDirectiveFilterFileExt }}
local shellCompDirectiveFilterDirs={{ .ShellCompDirectiveFilterDirs }}
local shellCompDirectiveKeepOrder={{ .ShellCompDirectiveKeepOrder }}

if (((directive & shellCompDirectiveError) != 0)); then
# Error code. No completion.
Expand All @@ -80,6 +81,19 @@ __{{ .CMDVarName }}_process_completion_results() {
__{{ .CMDVarName }}_debug "No space directive not supported in this version of bash"
fi
fi
if (((directive & shellCompDirectiveKeepOrder) != 0)); then
if [[ $(type -t compopt) == builtin ]]; then
# no sort isn't supported for bash less than < 4.4
if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
__{{ .CMDVarName }}_debug "No sort directive not supported in this version of bash"
else
__{{ .CMDVarName }}_debug "Activating keep order"
compopt -o nosort
fi
else
__{{ .CMDVarName }}_debug "No sort directive not supported in this version of bash"
fi
fi
if (((directive & shellCompDirectiveNoFileComp) != 0)); then
if [[ $(type -t compopt) == builtin ]]; then
__{{ .CMDVarName }}_debug "Activating no file completion"
Expand Down
70 changes: 64 additions & 6 deletions templates/completion.fish.gotmpl
Expand Up @@ -55,6 +55,59 @@ function __{{ .CMDVarName }}_perform_completion
printf "%s\n" "$directiveLine"
end

# this function limits calls to __{{ .CMDVarName }}_perform_completion, by caching the result behind $__{{ .CMDVarName }}_perform_completion_once_result
function __{{ .CMDVarName }}_perform_completion_once
__{{ .CMDVarName }}_debug "Starting __{{ .CMDVarName }}_perform_completion_once"

if test -n "$__{{ .CMDVarName }}_perform_completion_once_result"
__{{ .CMDVarName }}_debug "Seems like a valid result already exists, skipping __{{ .CMDVarName }}_perform_completion"
return 0
end

set --global __{{ .CMDVarName }}_perform_completion_once_result (__{{ .CMDVarName }}_perform_completion)
if test -z "$__{{ .CMDVarName }}_perform_completion_once_result"
__{{ .CMDVarName }}_debug "No completions, probably due to a failure"
return 1
end

__{{ .CMDVarName }}_debug "Performed completions and set __{{ .CMDVarName }}_perform_completion_once_result"
return 0
end

# this function is used to clear the $__{{ .CMDVarName }}_perform_completion_once_result variable after completions are run
function __{{ .CMDVarName }}_clear_perform_completion_once_result
__{{ .CMDVarName }}_debug ""
__{{ .CMDVarName }}_debug "========= clearing previously set __{{ .CMDVarName }}_perform_completion_once_result variable =========="
set --erase __{{ .CMDVarName }}_perform_completion_once_result
__{{ .CMDVarName }}_debug "Succesfully erased the variable __{{ .CMDVarName }}_perform_completion_once_result"
end

function __{{ .CMDVarName }}_requires_order_preservation
__{{ .CMDVarName }}_debug ""
__{{ .CMDVarName }}_debug "========= checking if order preservation is required =========="

__{{ .CMDVarName }}_perform_completion_once
if test -z "$__{{ .CMDVarName }}_perform_completion_once_result"
__{{ .CMDVarName }}_debug "Error determining if order preservation is required"
return 1
end

set -l directive (string sub --start 2 $__{{ .CMDVarName }}_perform_completion_once_result[-1])
__{{ .CMDVarName }}_debug "Directive is: $directive"

set -l shellCompDirectiveKeepOrder {{ .ShellCompDirectiveKeepOrder }}
set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2)
__{{ .CMDVarName }}_debug "Keeporder is: $keeporder"

if test $keeporder -ne 0
__{{ .CMDVarName }}_debug "This does require order preservation"
return 0
end

__{{ .CMDVarName }}_debug "This doesn't require order preservation"
return 1
end

# This function does two things:
# - Obtain the completions and store them in the global __{{ .CMDVarName }}_comp_results
# - Return false if file completion should be performed
Expand All @@ -65,17 +118,17 @@ function __{{ .CMDVarName }}_prepare_completions
# Start fresh
set --erase __{{ .CMDVarName }}_comp_results

set -l results (__{{ .CMDVarName }}_perform_completion)
__{{ .CMDVarName }}_debug "Completion results: $results"
__{{ .CMDVarName }}_perform_completion_once
__{{ .CMDVarName }}_debug "Completion results: $__{{ .CMDVarName }}_perform_completion_once_result"

if test -z "$results"
if test -z "$__{{ .CMDVarName }}_perform_completion_once_result"
__{{ .CMDVarName }}_debug "No completion, probably due to a failure"
# Might as well do file completion, in case it helps
return 1
end

set -l directive (string sub --start 2 $results[-1])
set --global __{{ .CMDVarName }}_comp_results $results[1..-2]
set -l directive (string sub --start 2 $__{{ .CMDVarName }}_perform_completion_once_result[-1])
set --global __{{ .CMDVarName }}_comp_results $__{{ .CMDVarName }}_perform_completion_once_result[1..-2]

__{{ .CMDVarName }}_debug "Completions are: $__{{ .CMDVarName }}_comp_results"
__{{ .CMDVarName }}_debug "Directive is: $directive"
Expand Down Expand Up @@ -171,6 +224,11 @@ end
# Remove any pre-existing completions for the program since we will be handling all of them.
complete -c {{ .CMDName }} -e

# this will get called after the two calls below and clear the $__{{ .CMDVarName }}_perform_completion_once_result global
complete -c {{ .CMDName }} -n '__{{ .CMDVarName }}_clear_perform_completion_once_result'
# The call to __{{ .CMDVarName }}_prepare_completions will setup __{{ .CMDVarName }}_comp_results
# which provides the program's completion choices.
complete -c {{ .CMDName }} -n '__{{ .CMDVarName }}_prepare_completions' -f -a '$__{{ .CMDVarName }}_comp_results'
# If this doesn't require order preservation, we don't use the -k flag
complete -c {{ .CMDName }} -n 'not __{{ .CMDVarName }}_requires_order_preservation && __{{ .CMDVarName }}_prepare_completions' -f -a '$__{{ .CMDVarName }}_comp_results'
# otherwise we use the -k flag
complete -k -c {{ .CMDName }} -n '__{{ .CMDVarName }}_requires_order_preservation && __{{ .CMDVarName }}_prepare_completions' -f -a '$__{{ .CMDVarName }}_comp_results'
6 changes: 6 additions & 0 deletions templates/completion.pwsh.gotmpl
Expand Up @@ -41,6 +41,7 @@ Register-ArgumentCompleter -CommandName '{{ .CMDVarName }}' -ScriptBlock {
$ShellCompDirectiveNoFileComp={{ .ShellCompDirectiveNoFileComp }}
$ShellCompDirectiveFilterFileExt={{ .ShellCompDirectiveFilterFileExt }}
$ShellCompDirectiveFilterDirs={{ .ShellCompDirectiveFilterDirs }}
$ShellCompDirectiveKeepOrder={{ .ShellCompDirectiveKeepOrder }}

# Prepare the command to request completions for the program.
# Split the command at the first space to separate the program and arguments.
Expand Down Expand Up @@ -151,6 +152,11 @@ Register-ArgumentCompleter -CommandName '{{ .CMDVarName }}' -ScriptBlock {
}
}

# we sort the values in ascending order by name if keep order isn't passed
if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
$Values = $Values | Sort-Object -Property Name
}
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
__{{ .CMDVarName }}_debug "ShellCompDirectiveNoFileComp is called"
Expand Down
10 changes: 8 additions & 2 deletions templates/completion.zsh.gotmpl
Expand Up @@ -18,8 +18,9 @@ _{{ .CMDVarName }}()
local shellCompDirectiveNoFileComp={{ .ShellCompDirectiveNoFileComp }}
local shellCompDirectiveFilterFileExt={{ .ShellCompDirectiveFilterFileExt }}
local shellCompDirectiveFilterDirs={{ .ShellCompDirectiveFilterDirs }}
local shellCompDirectiveKeepOrder={{ .ShellCompDirectiveKeepOrder }}

local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
local -a completions

__{{ .CMDVarName }}_debug "\n========= starting completion logic =========="
Expand Down Expand Up @@ -109,6 +110,11 @@ _{{ .CMDVarName }}()
noSpace="-S ''"
fi

if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
__{{ .CMDVarName }}_debug "Activating keep order."
keepOrder="-V"
fi

if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
# File extension filtering
local filteringCmd
Expand Down Expand Up @@ -144,7 +150,7 @@ _{{ .CMDVarName }}()
return $result
else
__{{ .CMDVarName }}_debug "Calling _describe"
if eval _describe "completions" completions $flagPrefix $noSpace; then
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
__{{ .CMDVarName }}_debug "_describe found some completions"

# Return the success of having called _describe
Expand Down

0 comments on commit 0a524d2

Please sign in to comment.