22 func writePreamble(buf io.StringWriter, name string) { |
22 func writePreamble(buf io.StringWriter, name string) { |
23 WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) |
23 WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) |
24 WriteStringAndCheck(buf, fmt.Sprintf(` |
24 WriteStringAndCheck(buf, fmt.Sprintf(` |
25 __%[1]s_debug() |
25 __%[1]s_debug() |
26 { |
26 { |
27 if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then |
27 if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then |
28 echo "$*" >> "${BASH_COMP_DEBUG_FILE}" |
28 echo "$*" >> "${BASH_COMP_DEBUG_FILE}" |
29 fi |
29 fi |
30 } |
30 } |
31 |
31 |
32 # Homebrew on Macs have version 1.3 of bash-completion which doesn't include |
32 # Homebrew on Macs have version 1.3 of bash-completion which doesn't include |
71 local out requestComp lastParam lastChar comp directive args |
71 local out requestComp lastParam lastChar comp directive args |
72 |
72 |
73 # Prepare the command to request completions for the program. |
73 # Prepare the command to request completions for the program. |
74 # Calling ${words[0]} instead of directly %[1]s allows to handle aliases |
74 # Calling ${words[0]} instead of directly %[1]s allows to handle aliases |
75 args=("${words[@]:1}") |
75 args=("${words[@]:1}") |
76 requestComp="${words[0]} %[2]s ${args[*]}" |
76 # Disable ActiveHelp which is not supported for bash completion v1 |
|
77 requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}" |
77 |
78 |
78 lastParam=${words[$((${#words[@]}-1))]} |
79 lastParam=${words[$((${#words[@]}-1))]} |
79 lastChar=${lastParam:$((${#lastParam}-1)):1} |
80 lastChar=${lastParam:$((${#lastParam}-1)):1} |
80 __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" |
81 __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" |
81 |
82 |
97 if [ "${directive}" = "${out}" ]; then |
98 if [ "${directive}" = "${out}" ]; then |
98 # There is not directive specified |
99 # There is not directive specified |
99 directive=0 |
100 directive=0 |
100 fi |
101 fi |
101 __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" |
102 __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" |
102 __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" |
103 __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out}" |
103 |
104 |
104 if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then |
105 if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then |
105 # Error code. No completion. |
106 # Error code. No completion. |
106 __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" |
107 __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" |
107 return |
108 return |
123 if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then |
124 if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then |
124 # File extension filtering |
125 # File extension filtering |
125 local fullFilter filter filteringCmd |
126 local fullFilter filter filteringCmd |
126 # Do not use quotes around the $out variable or else newline |
127 # Do not use quotes around the $out variable or else newline |
127 # characters will be kept. |
128 # characters will be kept. |
128 for filter in ${out[*]}; do |
129 for filter in ${out}; do |
129 fullFilter+="$filter|" |
130 fullFilter+="$filter|" |
130 done |
131 done |
131 |
132 |
132 filteringCmd="_filedir $fullFilter" |
133 filteringCmd="_filedir $fullFilter" |
133 __%[1]s_debug "File filtering command: $filteringCmd" |
134 __%[1]s_debug "File filtering command: $filteringCmd" |
134 $filteringCmd |
135 $filteringCmd |
135 elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then |
136 elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then |
136 # File completion for directories only |
137 # File completion for directories only |
137 local subDir |
138 local subdir |
138 # Use printf to strip any trailing newline |
139 # Use printf to strip any trailing newline |
139 subdir=$(printf "%%s" "${out[0]}") |
140 subdir=$(printf "%%s" "${out}") |
140 if [ -n "$subdir" ]; then |
141 if [ -n "$subdir" ]; then |
141 __%[1]s_debug "Listing directories in $subdir" |
142 __%[1]s_debug "Listing directories in $subdir" |
142 __%[1]s_handle_subdirs_in_dir_flag "$subdir" |
143 __%[1]s_handle_subdirs_in_dir_flag "$subdir" |
143 else |
144 else |
144 __%[1]s_debug "Listing directories in ." |
145 __%[1]s_debug "Listing directories in ." |
145 _filedir -d |
146 _filedir -d |
146 fi |
147 fi |
147 else |
148 else |
148 while IFS='' read -r comp; do |
149 while IFS='' read -r comp; do |
149 COMPREPLY+=("$comp") |
150 COMPREPLY+=("$comp") |
150 done < <(compgen -W "${out[*]}" -- "$cur") |
151 done < <(compgen -W "${out}" -- "$cur") |
151 fi |
152 fi |
152 } |
153 } |
153 |
154 |
154 __%[1]s_handle_reply() |
155 __%[1]s_handle_reply() |
155 { |
156 { |
185 COMPREPLY=() |
186 COMPREPLY=() |
186 if [[ ${index} -ge 0 ]]; then |
187 if [[ ${index} -ge 0 ]]; then |
187 PREFIX="" |
188 PREFIX="" |
188 cur="${cur#*=}" |
189 cur="${cur#*=}" |
189 ${flags_completion[${index}]} |
190 ${flags_completion[${index}]} |
190 if [ -n "${ZSH_VERSION}" ]; then |
191 if [ -n "${ZSH_VERSION:-}" ]; then |
191 # zsh completion needs --flag= prefix |
192 # zsh completion needs --flag= prefix |
192 eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" |
193 eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" |
193 fi |
194 fi |
194 fi |
195 fi |
195 fi |
196 fi |
196 return 0; |
197 |
|
198 if [[ -z "${flag_parsing_disabled}" ]]; then |
|
199 # If flag parsing is enabled, we have completed the flags and can return. |
|
200 # If flag parsing is disabled, we may not know all (or any) of the flags, so we fallthrough |
|
201 # to possibly call handle_go_custom_completion. |
|
202 return 0; |
|
203 fi |
197 ;; |
204 ;; |
198 esac |
205 esac |
199 |
206 |
200 # check if we are handling a flag with special work handling |
207 # check if we are handling a flag with special work handling |
201 local index |
208 local index |
230 COMPREPLY+=("$comp") |
237 COMPREPLY+=("$comp") |
231 done < <(compgen -W "${noun_aliases[*]}" -- "$cur") |
238 done < <(compgen -W "${noun_aliases[*]}" -- "$cur") |
232 fi |
239 fi |
233 |
240 |
234 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then |
241 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then |
235 if declare -F __%[1]s_custom_func >/dev/null; then |
242 if declare -F __%[1]s_custom_func >/dev/null; then |
236 # try command name qualified custom func |
243 # try command name qualified custom func |
237 __%[1]s_custom_func |
244 __%[1]s_custom_func |
238 else |
245 else |
239 # otherwise fall back to unqualified for compatibility |
246 # otherwise fall back to unqualified for compatibility |
240 declare -F __custom_func >/dev/null && __custom_func |
247 declare -F __custom_func >/dev/null && __custom_func |
241 fi |
248 fi |
242 fi |
249 fi |
243 |
250 |
244 # available in bash-completion >= 2, not always present on macOS |
251 # available in bash-completion >= 2, not always present on macOS |
245 if declare -F __ltrim_colon_completions >/dev/null; then |
252 if declare -F __ltrim_colon_completions >/dev/null; then |
246 __ltrim_colon_completions "$cur" |
253 __ltrim_colon_completions "$cur" |
270 { |
277 { |
271 __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" |
278 __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" |
272 |
279 |
273 # if a command required a flag, and we found it, unset must_have_one_flag() |
280 # if a command required a flag, and we found it, unset must_have_one_flag() |
274 local flagname=${words[c]} |
281 local flagname=${words[c]} |
275 local flagvalue |
282 local flagvalue="" |
276 # if the word contained an = |
283 # if the word contained an = |
277 if [[ ${words[c]} == *"="* ]]; then |
284 if [[ ${words[c]} == *"="* ]]; then |
278 flagvalue=${flagname#*=} # take in as flagvalue after the = |
285 flagvalue=${flagname#*=} # take in as flagvalue after the = |
279 flagname=${flagname%%=*} # strip everything after the = |
286 flagname=${flagname%%=*} # strip everything after the = |
280 flagname="${flagname}=" # but put the = back |
287 flagname="${flagname}=" # but put the = back |
289 commands=() |
296 commands=() |
290 fi |
297 fi |
291 |
298 |
292 # keep flag value with flagname as flaghash |
299 # keep flag value with flagname as flaghash |
293 # flaghash variable is an associative array which is only supported in bash > 3. |
300 # flaghash variable is an associative array which is only supported in bash > 3. |
294 if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then |
301 if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then |
295 if [ -n "${flagvalue}" ] ; then |
302 if [ -n "${flagvalue}" ] ; then |
296 flaghash[${flagname}]=${flagvalue} |
303 flaghash[${flagname}]=${flagvalue} |
297 elif [ -n "${words[ $((c+1)) ]}" ] ; then |
304 elif [ -n "${words[ $((c+1)) ]}" ] ; then |
298 flaghash[${flagname}]=${words[ $((c+1)) ]} |
305 flaghash[${flagname}]=${words[ $((c+1)) ]} |
299 else |
306 else |
301 fi |
308 fi |
302 fi |
309 fi |
303 |
310 |
304 # skip the argument to a two word flag |
311 # skip the argument to a two word flag |
305 if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then |
312 if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then |
306 __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" |
313 __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" |
307 c=$((c+1)) |
314 c=$((c+1)) |
308 # if we are looking for a flags value, don't show commands |
315 # if we are looking for a flags value, don't show commands |
309 if [[ $c -eq $cword ]]; then |
316 if [[ $c -eq $cword ]]; then |
310 commands=() |
317 commands=() |
311 fi |
318 fi |
361 __%[1]s_handle_command |
368 __%[1]s_handle_command |
362 elif [[ $c -eq 0 ]]; then |
369 elif [[ $c -eq 0 ]]; then |
363 __%[1]s_handle_command |
370 __%[1]s_handle_command |
364 elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then |
371 elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then |
365 # aliashash variable is an associative array which is only supported in bash > 3. |
372 # aliashash variable is an associative array which is only supported in bash > 3. |
366 if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then |
373 if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then |
367 words[c]=${aliashash[${words[c]}]} |
374 words[c]=${aliashash[${words[c]}]} |
368 __%[1]s_handle_command |
375 __%[1]s_handle_command |
369 else |
376 else |
370 __%[1]s_handle_noun |
377 __%[1]s_handle_noun |
371 fi |
378 fi |
375 __%[1]s_handle_word |
382 __%[1]s_handle_word |
376 } |
383 } |
377 |
384 |
378 `, name, ShellCompNoDescRequestCmd, |
385 `, name, ShellCompNoDescRequestCmd, |
379 ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, |
386 ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, |
380 ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) |
387 ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) |
381 } |
388 } |
382 |
389 |
383 func writePostscript(buf io.StringWriter, name string) { |
390 func writePostscript(buf io.StringWriter, name string) { |
384 name = strings.Replace(name, ":", "__", -1) |
391 name = strings.ReplaceAll(name, ":", "__") |
385 WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name)) |
392 WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name)) |
386 WriteStringAndCheck(buf, fmt.Sprintf(`{ |
393 WriteStringAndCheck(buf, fmt.Sprintf(`{ |
387 local cur prev words cword split |
394 local cur prev words cword split |
388 declare -A flaghash 2>/dev/null || : |
395 declare -A flaghash 2>/dev/null || : |
389 declare -A aliashash 2>/dev/null || : |
396 declare -A aliashash 2>/dev/null || : |
392 else |
399 else |
393 __%[1]s_init_completion -n "=" || return |
400 __%[1]s_init_completion -n "=" || return |
394 fi |
401 fi |
395 |
402 |
396 local c=0 |
403 local c=0 |
|
404 local flag_parsing_disabled= |
397 local flags=() |
405 local flags=() |
398 local two_word_flags=() |
406 local two_word_flags=() |
399 local local_nonpersistent_flags=() |
407 local local_nonpersistent_flags=() |
400 local flags_with_completion=() |
408 local flags_with_completion=() |
401 local flags_completion=() |
409 local flags_completion=() |
402 local commands=("%[1]s") |
410 local commands=("%[1]s") |
403 local command_aliases=() |
411 local command_aliases=() |
404 local must_have_one_flag=() |
412 local must_have_one_flag=() |
405 local must_have_one_noun=() |
413 local must_have_one_noun=() |
406 local has_completion_function |
414 local has_completion_function="" |
407 local last_command |
415 local last_command="" |
408 local nouns=() |
416 local nouns=() |
409 local noun_aliases=() |
417 local noun_aliases=() |
410 |
418 |
411 __%[1]s_handle_word |
419 __%[1]s_handle_word |
412 } |
420 } |
533 local_nonpersistent_flags=() |
541 local_nonpersistent_flags=() |
534 flags_with_completion=() |
542 flags_with_completion=() |
535 flags_completion=() |
543 flags_completion=() |
536 |
544 |
537 `) |
545 `) |
|
546 |
|
547 if cmd.DisableFlagParsing { |
|
548 WriteStringAndCheck(buf, " flag_parsing_disabled=1\n") |
|
549 } |
|
550 |
538 localNonPersistentFlags := cmd.LocalNonPersistentFlags() |
551 localNonPersistentFlags := cmd.LocalNonPersistentFlags() |
539 cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { |
552 cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { |
540 if nonCompletableFlag(flag) { |
553 if nonCompletableFlag(flag) { |
541 return |
554 return |
542 } |
555 } |
607 return |
620 return |
608 } |
621 } |
609 |
622 |
610 sort.Strings(cmd.Aliases) |
623 sort.Strings(cmd.Aliases) |
611 |
624 |
612 WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) |
625 WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then`, "\n")) |
613 for _, value := range cmd.Aliases { |
626 for _, value := range cmd.Aliases { |
614 WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value)) |
627 WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value)) |
615 WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) |
628 WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) |
616 } |
629 } |
617 WriteStringAndCheck(buf, ` fi`) |
630 WriteStringAndCheck(buf, ` fi`) |
631 continue |
644 continue |
632 } |
645 } |
633 gen(buf, c) |
646 gen(buf, c) |
634 } |
647 } |
635 commandName := cmd.CommandPath() |
648 commandName := cmd.CommandPath() |
636 commandName = strings.Replace(commandName, " ", "_", -1) |
649 commandName = strings.ReplaceAll(commandName, " ", "_") |
637 commandName = strings.Replace(commandName, ":", "__", -1) |
650 commandName = strings.ReplaceAll(commandName, ":", "__") |
638 |
651 |
639 if cmd.Root() == cmd { |
652 if cmd.Root() == cmd { |
640 WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName)) |
653 WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName)) |
641 } else { |
654 } else { |
642 WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName)) |
655 WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName)) |