Skip to content

Commit 3daa4b9

Browse files
authored
Add keeporder to shell completion (#1903)
This allows programs to request the shell to maintain the order of completions that was returned by the program
1 parent a516d41 commit 3daa4b9

6 files changed

+112
-17
lines changed

bash_completionsV2.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ __%[1]s_process_completion_results() {
101101
local shellCompDirectiveNoFileComp=%[5]d
102102
local shellCompDirectiveFilterFileExt=%[6]d
103103
local shellCompDirectiveFilterDirs=%[7]d
104+
local shellCompDirectiveKeepOrder=%[8]d
104105
105106
if (((directive & shellCompDirectiveError) != 0)); then
106107
# Error code. No completion.
@@ -115,6 +116,19 @@ __%[1]s_process_completion_results() {
115116
__%[1]s_debug "No space directive not supported in this version of bash"
116117
fi
117118
fi
119+
if (((directive & shellCompDirectiveKeepOrder) != 0)); then
120+
if [[ $(type -t compopt) == builtin ]]; then
121+
# no sort isn't supported for bash less than < 4.4
122+
if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
123+
__%[1]s_debug "No sort directive not supported in this version of bash"
124+
else
125+
__%[1]s_debug "Activating keep order"
126+
compopt -o nosort
127+
fi
128+
else
129+
__%[1]s_debug "No sort directive not supported in this version of bash"
130+
fi
131+
fi
118132
if (((directive & shellCompDirectiveNoFileComp) != 0)); then
119133
if [[ $(type -t compopt) == builtin ]]; then
120134
__%[1]s_debug "Activating no file completion"
@@ -183,7 +197,7 @@ __%[1]s_process_completion_results() {
183197
# Separate activeHelp lines from real completions.
184198
# Fills the $activeHelp and $completions arrays.
185199
__%[1]s_extract_activeHelp() {
186-
local activeHelpMarker="%[8]s"
200+
local activeHelpMarker="%[9]s"
187201
local endIndex=${#activeHelpMarker}
188202
189203
while IFS='' read -r comp; do
@@ -360,7 +374,7 @@ fi
360374
# ex: ts=4 sw=4 et filetype=sh
361375
`, name, compCmd,
362376
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
363-
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs,
377+
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
364378
activeHelpMarker))
365379
}
366380

completions.go

+7
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ const (
7777
// obtain the same behavior but only for flags.
7878
ShellCompDirectiveFilterDirs
7979

80+
// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
81+
// in which the completions are provided
82+
ShellCompDirectiveKeepOrder
83+
8084
// ===========================================================================
8185

8286
// All directives using iota should be above this one.
@@ -159,6 +163,9 @@ func (d ShellCompDirective) string() string {
159163
if d&ShellCompDirectiveFilterDirs != 0 {
160164
directives = append(directives, "ShellCompDirectiveFilterDirs")
161165
}
166+
if d&ShellCompDirectiveKeepOrder != 0 {
167+
directives = append(directives, "ShellCompDirectiveKeepOrder")
168+
}
162169
if len(directives) == 0 {
163170
directives = append(directives, "ShellCompDirectiveDefault")
164171
}

fish_completions.go

+67-9
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function __%[1]s_perform_completion
5353
__%[1]s_debug "last arg: $lastArg"
5454
5555
# Disable ActiveHelp which is not supported for fish shell
56-
set -l requestComp "%[9]s=0 $args[1] %[3]s $args[2..-1] $lastArg"
56+
set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg"
5757
5858
__%[1]s_debug "Calling $requestComp"
5959
set -l results (eval $requestComp 2> /dev/null)
@@ -89,6 +89,60 @@ function __%[1]s_perform_completion
8989
printf "%%s\n" "$directiveLine"
9090
end
9191
92+
# this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result
93+
function __%[1]s_perform_completion_once
94+
__%[1]s_debug "Starting __%[1]s_perform_completion_once"
95+
96+
if test -n "$__%[1]s_perform_completion_once_result"
97+
__%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion"
98+
return 0
99+
end
100+
101+
set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion)
102+
if test -z "$__%[1]s_perform_completion_once_result"
103+
__%[1]s_debug "No completions, probably due to a failure"
104+
return 1
105+
end
106+
107+
__%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result"
108+
return 0
109+
end
110+
111+
# this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run
112+
function __%[1]s_clear_perform_completion_once_result
113+
__%[1]s_debug ""
114+
__%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable =========="
115+
set --erase __%[1]s_perform_completion_once_result
116+
__%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result"
117+
end
118+
119+
function __%[1]s_requires_order_preservation
120+
__%[1]s_debug ""
121+
__%[1]s_debug "========= checking if order preservation is required =========="
122+
123+
__%[1]s_perform_completion_once
124+
if test -z "$__%[1]s_perform_completion_once_result"
125+
__%[1]s_debug "Error determining if order preservation is required"
126+
return 1
127+
end
128+
129+
set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
130+
__%[1]s_debug "Directive is: $directive"
131+
132+
set -l shellCompDirectiveKeepOrder %[9]d
133+
set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2)
134+
__%[1]s_debug "Keeporder is: $keeporder"
135+
136+
if test $keeporder -ne 0
137+
__%[1]s_debug "This does require order preservation"
138+
return 0
139+
end
140+
141+
__%[1]s_debug "This doesn't require order preservation"
142+
return 1
143+
end
144+
145+
92146
# This function does two things:
93147
# - Obtain the completions and store them in the global __%[1]s_comp_results
94148
# - Return false if file completion should be performed
@@ -99,17 +153,17 @@ function __%[1]s_prepare_completions
99153
# Start fresh
100154
set --erase __%[1]s_comp_results
101155
102-
set -l results (__%[1]s_perform_completion)
103-
__%[1]s_debug "Completion results: $results"
156+
__%[1]s_perform_completion_once
157+
__%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result"
104158
105-
if test -z "$results"
159+
if test -z "$__%[1]s_perform_completion_once_result"
106160
__%[1]s_debug "No completion, probably due to a failure"
107161
# Might as well do file completion, in case it helps
108162
return 1
109163
end
110164
111-
set -l directive (string sub --start 2 $results[-1])
112-
set --global __%[1]s_comp_results $results[1..-2]
165+
set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
166+
set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2]
113167
114168
__%[1]s_debug "Completions are: $__%[1]s_comp_results"
115169
__%[1]s_debug "Directive is: $directive"
@@ -205,13 +259,17 @@ end
205259
# Remove any pre-existing completions for the program since we will be handling all of them.
206260
complete -c %[2]s -e
207261
262+
# this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global
263+
complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result'
208264
# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results
209265
# which provides the program's completion choices.
210-
complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
211-
266+
# If this doesn't require order preservation, we don't use the -k flag
267+
complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
268+
# otherwise we use the -k flag
269+
complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
212270
`, nameForVar, name, compCmd,
213271
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
214-
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
272+
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
215273
}
216274

217275
// GenFishCompletion generates fish completion file and writes to the passed writer.

powershell_completions.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ filter __%[1]s_escapeStringWithSpecialChars {
7777
$ShellCompDirectiveNoFileComp=%[6]d
7878
$ShellCompDirectiveFilterFileExt=%[7]d
7979
$ShellCompDirectiveFilterDirs=%[8]d
80+
$ShellCompDirectiveKeepOrder=%[9]d
8081
8182
# Prepare the command to request completions for the program.
8283
# Split the command at the first space to separate the program and arguments.
@@ -112,7 +113,7 @@ filter __%[1]s_escapeStringWithSpecialChars {
112113
113114
__%[1]s_debug "Calling $RequestComp"
114115
# First disable ActiveHelp which is not supported for Powershell
115-
$env:%[9]s=0
116+
$env:%[10]s=0
116117
117118
#call the command store the output in $out and redirect stderr and stdout to null
118119
# $Out is an array contains each line per element
@@ -182,6 +183,11 @@ filter __%[1]s_escapeStringWithSpecialChars {
182183
}
183184
}
184185
186+
# we sort the values in ascending order by name if keep order isn't passed
187+
if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
188+
$Values = $Values | Sort-Object -Property Name
189+
}
190+
185191
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
186192
__%[1]s_debug "ShellCompDirectiveNoFileComp is called"
187193
@@ -267,7 +273,7 @@ filter __%[1]s_escapeStringWithSpecialChars {
267273
Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock
268274
`, name, nameForVar, compCmd,
269275
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
270-
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
276+
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
271277
}
272278

273279
func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {

shell_completions.md

+4
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ ShellCompDirectiveFilterFileExt
228228
// return []string{"themes"}, ShellCompDirectiveFilterDirs
229229
//
230230
ShellCompDirectiveFilterDirs
231+
232+
// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
233+
// in which the completions are provided
234+
ShellCompDirectiveKeepOrder
231235
```
232236

233237
***Note***: When using the `ValidArgsFunction`, Cobra 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]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function.

zsh_completions.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ _%[1]s()
108108
local shellCompDirectiveNoFileComp=%[5]d
109109
local shellCompDirectiveFilterFileExt=%[6]d
110110
local shellCompDirectiveFilterDirs=%[7]d
111+
local shellCompDirectiveKeepOrder=%[8]d
111112
112-
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
113+
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
113114
local -a completions
114115
115116
__%[1]s_debug "\n========= starting completion logic =========="
@@ -177,7 +178,7 @@ _%[1]s()
177178
return
178179
fi
179180
180-
local activeHelpMarker="%[8]s"
181+
local activeHelpMarker="%[9]s"
181182
local endIndex=${#activeHelpMarker}
182183
local startIndex=$((${#activeHelpMarker}+1))
183184
local hasActiveHelp=0
@@ -227,6 +228,11 @@ _%[1]s()
227228
noSpace="-S ''"
228229
fi
229230
231+
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
232+
__%[1]s_debug "Activating keep order."
233+
keepOrder="-V"
234+
fi
235+
230236
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
231237
# File extension filtering
232238
local filteringCmd
@@ -262,7 +268,7 @@ _%[1]s()
262268
return $result
263269
else
264270
__%[1]s_debug "Calling _describe"
265-
if eval _describe "completions" completions $flagPrefix $noSpace; then
271+
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
266272
__%[1]s_debug "_describe found some completions"
267273
268274
# Return the success of having called _describe
@@ -296,6 +302,6 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then
296302
fi
297303
`, name, compCmd,
298304
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
299-
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs,
305+
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
300306
activeHelpMarker))
301307
}

0 commit comments

Comments
 (0)