diff options
author | Paul Holzinger <paul.holzinger@web.de> | 2020-09-10 22:36:19 +0200 |
---|---|---|
committer | Paul Holzinger <paul.holzinger@web.de> | 2020-11-12 11:32:03 +0100 |
commit | df4bf5c584f264bdefef5cb30d85c036260eff8f (patch) | |
tree | 8d71634cc63feac5020dfdfc745d178457b50805 /vendor/github.com/spf13 | |
parent | ea753128952e1a6d4b56cc80d232f6dbfb420ba5 (diff) | |
download | podman-df4bf5c584f264bdefef5cb30d85c036260eff8f.tar.gz podman-df4bf5c584f264bdefef5cb30d85c036260eff8f.tar.bz2 podman-df4bf5c584f264bdefef5cb30d85c036260eff8f.zip |
Vendor in some cobra PRs to improve the completion experience.
This is only temporary until the cobra following PRs are merged:
- PR#1258 Custom completion handle multiple shorhand flags together
- PR#1249 Fix fish handling of "ShellCompDirectiveNoSpace" and file completion
- PR#1213 Fix zsh completion handling of nospace and file completion
- PR#1146 Bash completion V2 with completion descriptions
Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
Diffstat (limited to 'vendor/github.com/spf13')
-rw-r--r-- | vendor/github.com/spf13/cobra/bash_completions.go | 752 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/custom_completions.go | 101 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/fish_completions.go | 115 | ||||
-rw-r--r-- | vendor/github.com/spf13/cobra/zsh_completions.go | 55 |
4 files changed, 405 insertions, 618 deletions
diff --git a/vendor/github.com/spf13/cobra/bash_completions.go b/vendor/github.com/spf13/cobra/bash_completions.go index 846636d75..2ceebc552 100644 --- a/vendor/github.com/spf13/cobra/bash_completions.go +++ b/vendor/github.com/spf13/cobra/bash_completions.go @@ -5,70 +5,95 @@ import ( "fmt" "io" "os" - "sort" - "strings" - - "github.com/spf13/pflag" ) // Annotations for Bash completion. const ( - BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" + BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" + // BashCompCustom should be avoided as it only works for bash. + // Function RegisterFlagCompletionFunc() should be used instead. BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) -func writePreamble(buf *bytes.Buffer, name string) { - buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) - buf.WriteString(fmt.Sprintf(` -__%[1]s_debug() -{ - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then - echo "$*" >> "${BASH_COMP_DEBUG_FILE}" - fi +// GenBashCompletion generates bash completion file and writes to the passed writer. +func (c *Command) GenBashCompletion(w io.Writer) error { + return c.genBashCompletion(w, false) } -# Homebrew on Macs have version 1.3 of bash-completion which doesn't include -# _init_completion. This is a very minimal version of that function. -__%[1]s_init_completion() -{ - COMPREPLY=() - _get_comp_words_by_ref "$@" cur prev words cword +// GenBashCompletionWithDesc generates bash completion file with descriptions and writes to the passed writer. +func (c *Command) GenBashCompletionWithDesc(w io.Writer) error { + return c.genBashCompletion(w, true) } -__%[1]s_index_of_word() -{ - local w word=$1 - shift - index=0 - for w in "$@"; do - [[ $w = "$word" ]] && return - index=$((index+1)) - done - index=-1 +// GenBashCompletionFile generates bash completion file. +func (c *Command) GenBashCompletionFile(filename string) error { + return c.genBashCompletionFile(filename, false) +} + +// GenBashCompletionFileWithDesc generates bash completion file with descriptions. +func (c *Command) GenBashCompletionFileWithDesc(filename string) error { + return c.genBashCompletionFile(filename, true) +} + +func (c *Command) genBashCompletionFile(filename string, includeDesc bool) error { + outFile, err := os.Create(filename) + if err != nil { + return err + } + defer outFile.Close() + + return c.genBashCompletion(outFile, includeDesc) } -__%[1]s_contains_word() +func (c *Command) genBashCompletion(w io.Writer, includeDesc bool) error { + buf := new(bytes.Buffer) + if len(c.BashCompletionFunction) > 0 { + buf.WriteString(c.BashCompletionFunction + "\n") + } + genBashComp(buf, c.Name(), includeDesc) + + _, err := buf.WriteTo(w) + return err +} + +func genBashComp(buf *bytes.Buffer, name string, includeDesc bool) { + compCmd := ShellCompRequestCmd + if !includeDesc { + compCmd = ShellCompNoDescRequestCmd + } + + buf.WriteString(fmt.Sprintf(`# bash completion for %-36[1]s -*- shell-script -*- + +__%[1]s_debug() { - local w word=$1; shift - for w in "$@"; do - [[ $w = "$word" ]] && return - done - return 1 + if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + echo "$*" >> "${BASH_COMP_DEBUG_FILE}" + fi } -__%[1]s_handle_go_custom_completion() +__%[1]s_perform_completion() { - __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}" + __%[1]s_debug + __%[1]s_debug "========= starting completion logic ==========" + __%[1]s_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $cword location, so we need + # to truncate the command-line ($words) up to the $cword location. + words=("${words[@]:0:$cword+1}") + __%[1]s_debug "Truncated words[*]: ${words[*]}," local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveLegacyCustomComp=%[8]d + local shellCompDirectiveLegacyCustomArgsComp=%[9]d - local out requestComp lastParam lastChar comp directive args + local out requestComp lastParam lastChar comp directive args flagPrefix # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly %[1]s allows to handle aliases @@ -77,16 +102,24 @@ __%[1]s_handle_go_custom_completion() lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} - __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" + __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. - __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter" + __%[1]s_debug "Adding extra empty parameter" requestComp="${requestComp} \"\"" fi - __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}" + # When completing a flag with an = (e.g., %[1]s -n=<TAB>) + # bash focuses on the part after the =, so we need to remove + # the flag part from $cur + if [[ "${cur}" == -*=* ]]; then + flagPrefix="${cur%%%%=*}=" + cur="${cur#*=}" + fi + + __%[1]s_debug "Calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) @@ -98,23 +131,23 @@ __%[1]s_handle_go_custom_completion() # There is not directive specified directive=0 fi - __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" - __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" + __%[1]s_debug "The completion directive is: ${directive}" + __%[1]s_debug "The completions are: ${out[*]}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. - __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" + __%[1]s_debug "Received error from custom completion go code" return else if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "${FUNCNAME[0]}: activating no space" + __%[1]s_debug "Activating no space" compopt -o nospace fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "${FUNCNAME[0]}: activating no file completion" + __%[1]s_debug "Activating no file completion" compopt +o default fi fi @@ -123,6 +156,7 @@ __%[1]s_handle_go_custom_completion() if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local fullFilter filter filteringCmd + # Do not use quotes around the $out variable or else newline # characters will be kept. for filter in ${out[*]}; do @@ -134,545 +168,173 @@ __%[1]s_handle_go_custom_completion() $filteringCmd elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only - local subDir + # Use printf to strip any trailing newline + local subdir subdir=$(printf "%%s" "${out[0]}") if [ -n "$subdir" ]; then __%[1]s_debug "Listing directories in $subdir" - __%[1]s_handle_subdirs_in_dir_flag "$subdir" + pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return else __%[1]s_debug "Listing directories in ." _filedir -d fi + elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then + local cmd + __%[1]s_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" + + # The following variables should get their value through the commands + # we have received as completions and are parsing below. + local last_command + local nouns + + # Execute every command received + while IFS='' read -r cmd; do + __%[1]s_debug "About to execute: $cmd" + eval "$cmd" + done < <(printf "%%s\n" "${out[@]}") + + __%[1]s_debug "last_command: $last_command" + __%[1]s_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" + + if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then + # We should call the global legacy custom completion function, if it is defined + if declare -F __%[1]s_custom_func >/dev/null; then + # Use command name qualified legacy custom func + __%[1]s_debug "About to call: __%[1]s_custom_func" + __%[1]s_custom_func + elif declare -F __custom_func >/dev/null; then + # Otherwise fall back to unqualified legacy custom func for compatibility + __%[1]s_debug "About to call: __custom_func" + __custom_func + fi + fi else + local tab + tab=$(printf '\t') + local longest=0 + # Look for the longest completion so that we can format things nicely while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${out[*]}" -- "$cur") - fi -} - -__%[1]s_handle_reply() -{ - __%[1]s_debug "${FUNCNAME[0]}" - local comp - case $cur in - -*) - if [[ $(type -t compopt) = "builtin" ]]; then - compopt -o nospace - fi - local allflags - if [ ${#must_have_one_flag[@]} -ne 0 ]; then - allflags=("${must_have_one_flag[@]}") - else - allflags=("${flags[*]} ${two_word_flags[*]}") - fi - while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${allflags[*]}" -- "$cur") - if [[ $(type -t compopt) = "builtin" ]]; then - [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace + comp=${comp%%%%$tab*} + if ((${#comp}>longest)); then + longest=${#comp} fi + done < <(printf "%%s\n" "${out[@]}") - # complete after --flag=abc - if [[ $cur == *=* ]]; then - if [[ $(type -t compopt) = "builtin" ]]; then - compopt +o nospace - fi - - local index flag - flag="${cur%%=*}" - __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}" - COMPREPLY=() - if [[ ${index} -ge 0 ]]; then - PREFIX="" - cur="${cur#*=}" - ${flags_completion[${index}]} - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" - fi - fi + local completions=() + while IFS='' read -r comp; do + if [ -z "$comp" ]; then + continue fi - return 0; - ;; - esac - - # check if we are handling a flag with special work handling - local index - __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}" - if [[ ${index} -ge 0 ]]; then - ${flags_completion[${index}]} - return - fi - # we are parsing a flag and don't have a special handler, no completion - if [[ ${cur} != "${words[cword]}" ]]; then - return - fi + __%[1]s_debug "Original comp: $comp" + comp="$(__%[1]s_format_comp_descriptions "$comp" "$longest")" + __%[1]s_debug "Final comp: $comp" + completions+=("$comp") + done < <(printf "%%s\n" "${out[@]}") - local completions - completions=("${commands[@]}") - if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then - completions+=("${must_have_one_noun[@]}") - elif [[ -n "${has_completion_function}" ]]; then - # if a go completion function is provided, defer to that function - __%[1]s_handle_go_custom_completion - fi - if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then - completions+=("${must_have_one_flag[@]}") - fi - while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${completions[*]}" -- "$cur") - - if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then while IFS='' read -r comp; do + # Although this script should only be used for bash + # there may be programs that still convert the bash + # script into a zsh one. To continue supporting those + # programs, we do this single adaptation for zsh + if [ -n "${ZSH_VERSION}" ]; then + # zsh completion needs --flag= prefix + COMPREPLY+=("$flagPrefix$comp") + else + COMPREPLY+=("$comp") + fi + done < <(compgen -W "${completions[*]}" -- "$cur") + + # If there is a single completion left, remove the description text + if [ ${#COMPREPLY[*]} -eq 1 ]; then + __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" + comp="${COMPREPLY[0]%%%% *}" + __%[1]s_debug "Removed description from single completion, which is now: ${comp}" + COMPREPLY=() COMPREPLY+=("$comp") - done < <(compgen -W "${noun_aliases[*]}" -- "$cur") - fi - - if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - if declare -F __%[1]s_custom_func >/dev/null; then - # try command name qualified custom func - __%[1]s_custom_func - else - # otherwise fall back to unqualified for compatibility - declare -F __custom_func >/dev/null && __custom_func - fi - fi - - # available in bash-completion >= 2, not always present on macOS - if declare -F __ltrim_colon_completions >/dev/null; then - __ltrim_colon_completions "$cur" - fi - - # If there is only 1 completion and it is a flag with an = it will be completed - # but we don't want a space after the = - if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then - compopt -o nospace - fi -} - -# The arguments should be in the form "ext1|ext2|extn" -__%[1]s_handle_filename_extension_flag() -{ - local ext="$1" - _filedir "@(${ext})" -} - -__%[1]s_handle_subdirs_in_dir_flag() -{ - local dir="$1" - pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return -} - -__%[1]s_handle_flag() -{ - __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - # if a command required a flag, and we found it, unset must_have_one_flag() - local flagname=${words[c]} - local flagvalue - # if the word contained an = - if [[ ${words[c]} == *"="* ]]; then - flagvalue=${flagname#*=} # take in as flagvalue after the = - flagname=${flagname%%=*} # strip everything after the = - flagname="${flagname}=" # but put the = back - fi - __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}" - if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then - must_have_one_flag=() - fi - - # if you set a flag which only applies to this command, don't show subcommands - if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then - commands=() - fi - - # keep flag value with flagname as flaghash - # flaghash variable is an associative array which is only supported in bash > 3. - if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then - if [ -n "${flagvalue}" ] ; then - flaghash[${flagname}]=${flagvalue} - elif [ -n "${words[ $((c+1)) ]}" ] ; then - flaghash[${flagname}]=${words[ $((c+1)) ]} - else - flaghash[${flagname}]="true" # pad "true" for bool flag - fi - fi - - # skip the argument to a two word flag - if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then - __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" - c=$((c+1)) - # if we are looking for a flags value, don't show commands - if [[ $c -eq $cword ]]; then - commands=() fi fi - c=$((c+1)) - + __%[1]s_handle_special_char "$cur" : + __%[1]s_handle_special_char "$cur" = } -__%[1]s_handle_noun() +__%[1]s_handle_special_char() { - __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then - must_have_one_noun=() - elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then - must_have_one_noun=() + local comp="$1" + local char=$2 + if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then + local word=${comp%%"${comp##*${char}}"} + local idx=${#COMPREPLY[*]} + while [[ $((--idx)) -ge 0 ]]; do + COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"} + done fi - - nouns+=("${words[c]}") - c=$((c+1)) } -__%[1]s_handle_command() +__%[1]s_format_comp_descriptions() { - __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - local next_command - if [[ -n ${last_command} ]]; then - next_command="_${last_command}_${words[c]//:/__}" - else - if [[ $c -eq 0 ]]; then - next_command="_%[1]s_root_command" + local tab + tab=$(printf '\t') + local comp="$1" + local longest=$2 + + # Properly format the description string which follows a tab character if there is one + if [[ "$comp" == *$tab* ]]; then + desc=${comp#*$tab} + comp=${comp%%%%$tab*} + + # $COLUMNS stores the current shell width. + # Remove an extra 4 because we add 2 spaces and 2 parentheses. + maxdesclength=$(( COLUMNS - longest - 4 )) + + # Make sure we can fit a description of at least 8 characters + # if we are to align the descriptions. + if [[ $maxdesclength -gt 8 ]]; then + # Add the proper number of spaces to align the descriptions + for ((i = ${#comp} ; i < longest ; i++)); do + comp+=" " + done else - next_command="_${words[c]//:/__}" + # Don't pad the descriptions so we can fit more text after the completion + maxdesclength=$(( COLUMNS - ${#comp} - 4 )) fi - fi - c=$((c+1)) - __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}" - declare -F "$next_command" >/dev/null && $next_command -} -__%[1]s_handle_word() -{ - if [[ $c -ge $cword ]]; then - __%[1]s_handle_reply - return - fi - __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - if [[ "${words[c]}" == -* ]]; then - __%[1]s_handle_flag - elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then - __%[1]s_handle_command - elif [[ $c -eq 0 ]]; then - __%[1]s_handle_command - elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then - # aliashash variable is an associative array which is only supported in bash > 3. - if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then - words[c]=${aliashash[${words[c]}]} - __%[1]s_handle_command - else - __%[1]s_handle_noun + # If there is enough space for any description text, + # truncate the descriptions that are too long for the shell width + if [ $maxdesclength -gt 0 ]; then + if [ ${#desc} -gt $maxdesclength ]; then + desc=${desc:0:$(( maxdesclength - 1 ))} + desc+="…" + fi + comp+=" ($desc)" fi - else - __%[1]s_handle_noun fi - __%[1]s_handle_word -} -`, name, ShellCompNoDescRequestCmd, - ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) + # Must use printf to escape all special characters + printf "%%q" "${comp}" } -func writePostscript(buf *bytes.Buffer, name string) { - name = strings.Replace(name, ":", "__", -1) - buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) - buf.WriteString(fmt.Sprintf(`{ +__start_%[1]s() +{ local cur prev words cword - declare -A flaghash 2>/dev/null || : - declare -A aliashash 2>/dev/null || : - if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -s || return - else - __%[1]s_init_completion -n "=" || return - fi - local c=0 - local flags=() - local two_word_flags=() - local local_nonpersistent_flags=() - local flags_with_completion=() - local flags_completion=() - local commands=("%[1]s") - local must_have_one_flag=() - local must_have_one_noun=() - local has_completion_function - local last_command - local nouns=() - - __%[1]s_handle_word + COMPREPLY=() + _get_comp_words_by_ref -n "=:" cur prev words cword + + __%[1]s_perform_completion } -`, name)) - buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_%s %s +if [[ $(type -t compopt) = "builtin" ]]; then + complete -o default -F __start_%[1]s %[1]s else - complete -o default -o nospace -F __start_%s %s + complete -o default -o nospace -F __start_%[1]s %[1]s fi -`, name, name, name, name)) - buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") -} - -func writeCommands(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(" commands=()\n") - for _, c := range cmd.Commands() { - if !c.IsAvailableCommand() && c != cmd.helpCommand { - continue - } - buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) - writeCmdAliases(buf, c) - } - buf.WriteString("\n") -} - -func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { - for key, value := range annotations { - switch key { - case BashCompFilenameExt: - buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) - - var ext string - if len(value) > 0 { - ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|") - } else { - ext = "_filedir" - } - buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) - case BashCompCustom: - buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) - if len(value) > 0 { - handlers := strings.Join(value, "; ") - buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) - } else { - buf.WriteString(" flags_completion+=(:)\n") - } - case BashCompSubdirsInDir: - buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) - - var ext string - if len(value) == 1 { - ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0] - } else { - ext = "_filedir -d" - } - buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) - } - } -} - -func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { - name := flag.Shorthand - format := " " - if len(flag.NoOptDefVal) == 0 { - format += "two_word_" - } - format += "flags+=(\"-%s\")\n" - buf.WriteString(fmt.Sprintf(format, name)) - writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) -} - -func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { - name := flag.Name - format := " flags+=(\"--%s" - if len(flag.NoOptDefVal) == 0 { - format += "=" - } - format += "\")\n" - buf.WriteString(fmt.Sprintf(format, name)) - if len(flag.NoOptDefVal) == 0 { - format = " two_word_flags+=(\"--%s\")\n" - buf.WriteString(fmt.Sprintf(format, name)) - } - writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) -} - -func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) { - name := flag.Name - format := " local_nonpersistent_flags+=(\"--%[1]s\")\n" - if len(flag.NoOptDefVal) == 0 { - format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n" - } - buf.WriteString(fmt.Sprintf(format, name)) - if len(flag.Shorthand) > 0 { - buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) - } -} - -// Setup annotations for go completions for registered flags -func prepareCustomAnnotationsForFlags(cmd *Command) { - for flag := range flagCompletionFunctions { - // Make sure the completion script calls the __*_go_custom_completion function for - // every registered flag. We need to do this here (and not when the flag was registered - // for completion) so that we can know the root command name for the prefix - // of __<prefix>_go_custom_completion - if flag.Annotations == nil { - flag.Annotations = map[string][]string{} - } - flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())} - } -} - -func writeFlags(buf *bytes.Buffer, cmd *Command) { - prepareCustomAnnotationsForFlags(cmd) - buf.WriteString(` flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - -`) - localNonPersistentFlags := cmd.LocalNonPersistentFlags() - cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { - if nonCompletableFlag(flag) { - return - } - writeFlag(buf, flag, cmd) - if len(flag.Shorthand) > 0 { - writeShortFlag(buf, flag, cmd) - } - // localNonPersistentFlags are used to stop the completion of subcommands when one is set - // if TraverseChildren is true we should allow to complete subcommands - if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren { - writeLocalNonPersistentFlag(buf, flag) - } - }) - cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { - if nonCompletableFlag(flag) { - return - } - writeFlag(buf, flag, cmd) - if len(flag.Shorthand) > 0 { - writeShortFlag(buf, flag, cmd) - } - }) - - buf.WriteString("\n") -} - -func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(" must_have_one_flag=()\n") - flags := cmd.NonInheritedFlags() - flags.VisitAll(func(flag *pflag.Flag) { - if nonCompletableFlag(flag) { - return - } - for key := range flag.Annotations { - switch key { - case BashCompOneRequiredFlag: - format := " must_have_one_flag+=(\"--%s" - if flag.Value.Type() != "bool" { - format += "=" - } - format += "\")\n" - buf.WriteString(fmt.Sprintf(format, flag.Name)) - - if len(flag.Shorthand) > 0 { - buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) - } - } - } - }) -} - -func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(" must_have_one_noun=()\n") - sort.Sort(sort.StringSlice(cmd.ValidArgs)) - for _, value := range cmd.ValidArgs { - // Remove any description that may be included following a tab character. - // Descriptions are not supported by bash completion. - value = strings.Split(value, "\t")[0] - buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) - } - if cmd.ValidArgsFunction != nil { - buf.WriteString(" has_completion_function=1\n") - } -} - -func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { - if len(cmd.Aliases) == 0 { - return - } - - sort.Sort(sort.StringSlice(cmd.Aliases)) - - buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) - for _, value := range cmd.Aliases { - buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) - buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) - } - buf.WriteString(` fi`) - buf.WriteString("\n") -} -func writeArgAliases(buf *bytes.Buffer, cmd *Command) { - buf.WriteString(" noun_aliases=()\n") - sort.Sort(sort.StringSlice(cmd.ArgAliases)) - for _, value := range cmd.ArgAliases { - buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) - } -} - -func gen(buf *bytes.Buffer, cmd *Command) { - for _, c := range cmd.Commands() { - if !c.IsAvailableCommand() && c != cmd.helpCommand { - continue - } - gen(buf, c) - } - commandName := cmd.CommandPath() - commandName = strings.Replace(commandName, " ", "_", -1) - commandName = strings.Replace(commandName, ":", "__", -1) - - if cmd.Root() == cmd { - buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) - } else { - buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) - } - - buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) - buf.WriteString("\n") - buf.WriteString(" command_aliases=()\n") - buf.WriteString("\n") - - writeCommands(buf, cmd) - writeFlags(buf, cmd) - writeRequiredFlag(buf, cmd) - writeRequiredNouns(buf, cmd) - writeArgAliases(buf, cmd) - buf.WriteString("}\n\n") -} - -// GenBashCompletion generates bash completion file and writes to the passed writer. -func (c *Command) GenBashCompletion(w io.Writer) error { - buf := new(bytes.Buffer) - writePreamble(buf, c.Name()) - if len(c.BashCompletionFunction) > 0 { - buf.WriteString(c.BashCompletionFunction + "\n") - } - gen(buf, c) - writePostscript(buf, c.Name()) - - _, err := buf.WriteTo(w) - return err -} - -func nonCompletableFlag(flag *pflag.Flag) bool { - return flag.Hidden || len(flag.Deprecated) > 0 -} - -// GenBashCompletionFile generates bash completion file. -func (c *Command) GenBashCompletionFile(filename string) error { - outFile, err := os.Create(filename) - if err != nil { - return err - } - defer outFile.Close() - - return c.GenBashCompletion(outFile) +# ex: ts=4 sw=4 et filetype=sh +`, name, compCmd, + ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) } diff --git a/vendor/github.com/spf13/cobra/custom_completions.go b/vendor/github.com/spf13/cobra/custom_completions.go index f9e88e081..e2c68ae1e 100644 --- a/vendor/github.com/spf13/cobra/custom_completions.go +++ b/vendor/github.com/spf13/cobra/custom_completions.go @@ -51,6 +51,11 @@ const ( // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs + // For internal use only. + // Used to maintain backwards-compatibility with the legacy bash custom completions. + shellCompDirectiveLegacyCustomComp + shellCompDirectiveLegacyCustomArgsComp + // =========================================================================== // All directives using iota should be above this one. @@ -94,6 +99,12 @@ func (d ShellCompDirective) string() string { if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } + if d&shellCompDirectiveLegacyCustomComp != 0 { + directives = append(directives, "shellCompDirectiveLegacyCustomComp") + } + if d&shellCompDirectiveLegacyCustomArgsComp != 0 { + directives = append(directives, "shellCompDirectiveLegacyCustomArgsComp") + } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } @@ -149,10 +160,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> @@ -362,6 +369,10 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi var comps []string comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) + } else { + // If there is no Go custom completion defined, check for legacy bash + // custom completion to preserve backwards-compatibility + completions, directive = checkLegacyCustomCompletion(finalCmd, finalArgs, flag, completions, directive) } return finalCmd, completions, directive, nil @@ -442,7 +453,16 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p if len(lastArg) > 0 && lastArg[0] == '-' { if index := strings.Index(lastArg, "="); index >= 0 { // Flag with an = - flagName = strings.TrimLeft(lastArg[:index], "-") + if strings.HasPrefix(lastArg[:index], "--") { + // Flag has full name + flagName = lastArg[2:index] + } else { + // Flag is shorthand + // We have to get the last shorthand flag name + // e.g. `-asd` => d to provide the correct completion + // https://github.com/spf13/cobra/issues/1257 + flagName = lastArg[index-1 : index] + } lastArg = lastArg[index+1:] flagWithEqual = true } else { @@ -459,8 +479,16 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p // If the flag contains an = it means it has already been fully processed, // so we don't need to deal with it here. if index := strings.Index(prevArg, "="); index < 0 { - flagName = strings.TrimLeft(prevArg, "-") - + if strings.HasPrefix(prevArg, "--") { + // Flag has full name + flagName = prevArg[2:] + } else { + // Flag is shorthand + // We have to get the last shorthand flag name + // e.g. `-asd` => d to provide the correct completion + // https://github.com/spf13/cobra/issues/1257 + flagName = prevArg[len(prevArg)-1:] + } // Remove the uncompleted flag or else there could be an error created // for an invalid value for that flag trimmedArgs = args[:len(args)-1] @@ -513,6 +541,65 @@ func findFlag(cmd *Command, name string) *pflag.Flag { return cmd.Flag(name) } +func nonCompletableFlag(flag *pflag.Flag) bool { + return flag.Hidden || len(flag.Deprecated) > 0 +} + +// This function checks if legacy bash custom completion should be performed and if so, +// it provides the shell script with the necessary information. +func checkLegacyCustomCompletion(cmd *Command, args []string, flag *pflag.Flag, completions []string, directive ShellCompDirective) ([]string, ShellCompDirective) { + // Check if any legacy custom completion is defined for the program + if len(cmd.Root().BashCompletionFunction) > 0 { + // Legacy custom completion is only triggered if no other completions were found. + if len(completions) == 0 { + if flag != nil { + // For legacy custom flag completion, we must let the script know the bash + // functions it should call based on the content of the annotation BashCompCustom. + if values, present := flag.Annotations[BashCompCustom]; present { + if len(values) > 0 { + handlers := strings.Join(values, "; ") + // We send the commands to set the shell variables that are needed + // for legacy custom completions followed by the functions to call + // to perform the actual flag completion + completions = append(prepareLegacyCustomCompletionVars(cmd, args), handlers) + directive = directive | shellCompDirectiveLegacyCustomComp + } + } + } else { + // Check if the legacy custom_func is defined. + // This check will work for both "__custom_func" and "__<program>_custom_func". + // This could happen if the program defined some functions for legacy flag completion + // but not the legacy custom_func. + if strings.Contains(cmd.Root().BashCompletionFunction, "_custom_func") { + // For legacy args completion, the script already knows what to call + // so we only need to tell it the commands to set the shell variables needed + completions = prepareLegacyCustomCompletionVars(cmd, args) + directive = directive | shellCompDirectiveLegacyCustomComp | shellCompDirectiveLegacyCustomArgsComp + } + } + } + } + return completions, directive +} + +// The original bash completion script had some shell variables that are used by legacy bash +// custom completions. Let's set those variables to allow those legacy custom completions +// to continue working. +func prepareLegacyCustomCompletionVars(cmd *Command, args []string) []string { + var compVarCmds []string + + // "last_command" variable + commandName := cmd.CommandPath() + commandName = strings.Replace(commandName, " ", "_", -1) + commandName = strings.Replace(commandName, ":", "__", -1) + compVarCmds = append(compVarCmds, fmt.Sprintf("last_command=%s", commandName)) + + // "nouns" array variable + compVarCmds = append(compVarCmds, fmt.Sprintf("nouns=(%s)", strings.Join(args, " "))) + + return compVarCmds +} + // CompDebug prints the specified string to the same file as where the // completion script prints its logs. // Note that completion printouts should never be on stdout as they would diff --git a/vendor/github.com/spf13/cobra/fish_completions.go b/vendor/github.com/spf13/cobra/fish_completions.go index eaae9bca8..aeaef0087 100644 --- a/vendor/github.com/spf13/cobra/fish_completions.go +++ b/vendor/github.com/spf13/cobra/fish_completions.go @@ -28,9 +28,9 @@ function __%[1]s_debug end function __%[1]s_perform_completion - __%[1]s_debug "Starting __%[1]s_perform_completion with: $argv" + __%[1]s_debug "Starting __%[1]s_perform_completion" - set args (string split -- " " "$argv") + set args (string split -- " " (commandline -c)) set lastArg "$args[-1]" __%[1]s_debug "args: $args" @@ -71,31 +71,22 @@ function __%[1]s_perform_completion printf "%%s\n" "$directiveLine" end -# This function does three things: -# 1- Obtain the completions and store them in the global __%[1]s_comp_results -# 2- Set the __%[1]s_comp_do_file_comp flag if file completion should be performed -# and unset it otherwise -# 3- Return true if the completion results are not empty +# This function does two things: +# - Obtain the completions and store them in the global __%[1]s_comp_results +# - Return false if file completion should be performed function __%[1]s_prepare_completions + __%[1]s_debug "" + __%[1]s_debug "========= starting completion logic ==========" + # Start fresh - set --erase __%[1]s_comp_do_file_comp set --erase __%[1]s_comp_results - # Check if the command-line is already provided. This is useful for testing. - if not set --query __%[1]s_comp_commandLine - # Use the -c flag to allow for completion in the middle of the line - set __%[1]s_comp_commandLine (commandline -c) - end - __%[1]s_debug "commandLine is: $__%[1]s_comp_commandLine" - - set results (__%[1]s_perform_completion "$__%[1]s_comp_commandLine") - set --erase __%[1]s_comp_commandLine + set results (__%[1]s_perform_completion) __%[1]s_debug "Completion results: $results" if test -z "$results" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps - set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -110,6 +101,8 @@ function __%[1]s_prepare_completions set shellCompDirectiveNoFileComp %[6]d set shellCompDirectiveFilterFileExt %[7]d set shellCompDirectiveFilterDirs %[8]d + set shellCompDirectiveLegacyCustomComp %[9]d + set shellCompDirectiveLegacyCustomArgsComp %[10]d if test -z "$directive" set directive 0 @@ -119,6 +112,14 @@ function __%[1]s_prepare_completions if test $compErr -eq 1 __%[1]s_debug "Received error directive: aborting." # Might as well do file completion, in case it helps + return 1 + end + + set legacyCustom (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomComp) %% 2) + set legacyCustomArgs (math (math --scale 0 $directive / $shellCompDirectiveLegacyCustomArgsComp) %% 2) + if test $legacyCustom -eq 1; or test $legacyCustomArgs -eq 1 + __%[1]s_debug "Legacy bash custom completion not applicable to fish" + # Do full file completion instead set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -128,7 +129,6 @@ function __%[1]s_prepare_completions if test $filefilter -eq 1; or test $dirfilter -eq 1 __%[1]s_debug "File extension filtering or directory filtering not supported" # Do full file completion instead - set --global __%[1]s_comp_do_file_comp 1 return 1 end @@ -137,27 +137,51 @@ function __%[1]s_prepare_completions __%[1]s_debug "nospace: $nospace, nofiles: $nofiles" - # Important not to quote the variable for count to work - set numComps (count $__%[1]s_comp_results) - __%[1]s_debug "numComps: $numComps" - - if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - __%[1]s_debug "Adding second completion to perform nospace directive" - set --append __%[1]s_comp_results $__%[1]s_comp_results[1]. + # If we want to prevent a space, or if file completion is NOT disabled, + # we need to count the number of valid completions. + # To do so, we will filter on prefix as the completions we have received + # may not already be filtered so as to allow fish to match on different + # criteria than prefix. + if test $nospace -ne 0; or test $nofiles -eq 0 + set prefix (commandline -t) + __%[1]s_debug "prefix: $prefix" + + set completions + for comp in $__%[1]s_comp_results + if test (string match -e -r "^$prefix" "$comp") + set -a completions $comp + end + end + set --global __%[1]s_comp_results $completions + __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results" + + # Important not to quote the variable for count to work + set numComps (count $__%[1]s_comp_results) + __%[1]s_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # To support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + # We must first split on \t to get rid of the descriptions because + # the extra character we add to the fake second completion must be + # before the description. We don't need descriptions anyway since + # there is only a single real completion which the shell will expand + # immediately. + __%[1]s_debug "Adding second completion to perform nospace directive" + set split (string split --max 1 \t $__%[1]s_comp_results[1]) + set --global __%[1]s_comp_results $split[1] $split[1]. + __%[1]s_debug "Completions are now: $__%[1]s_comp_results" + end + + if test $numComps -eq 0; and test $nofiles -eq 0 + # To be consistent with bash and zsh, we only trigger file + # completion when there are no other completions + __%[1]s_debug "Requesting file completion" + return 1 + end end - if test $numComps -eq 0; and test $nofiles -eq 0 - __%[1]s_debug "Requesting file completion" - set --global __%[1]s_comp_do_file_comp 1 - end - - # If we don't want file completion, we must return true even if there - # are no completions found. This is because fish will perform the last - # completion command, even if its condition is false, if no other - # completion command was triggered - return (not set --query __%[1]s_comp_do_file_comp) + return 0 end # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves @@ -170,21 +194,14 @@ complete --do-complete "%[2]s " > /dev/null 2>&1 # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e -# The order in which the below two lines are defined is very important so that __%[1]s_prepare_completions -# is called first. It is __%[1]s_prepare_completions that sets up the __%[1]s_comp_do_file_comp variable. -# -# This completion will be run second as complete commands are added FILO. -# It triggers file completion choices when __%[1]s_comp_do_file_comp is set. -complete -c %[2]s -n 'set --query __%[1]s_comp_do_file_comp' - -# This completion will be run first as complete commands are added FILO. -# The call to __%[1]s_prepare_completions will setup both __%[1]s_comp_results and __%[1]s_comp_do_file_comp. -# It provides the program's completion choices. +# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results +# which provides the program's completion choices. complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) } // GenFishCompletion generates fish completion file and writes to the passed writer. diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go index 92a70394a..ef290e2eb 100644 --- a/vendor/github.com/spf13/cobra/zsh_completions.go +++ b/vendor/github.com/spf13/cobra/zsh_completions.go @@ -94,8 +94,10 @@ _%[1]s() local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d + local shellCompDirectiveLegacyCustomComp=%[8]d + local shellCompDirectiveLegacyCustomArgsComp=%[9]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 ==========" @@ -163,7 +165,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. @@ -175,13 +176,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 @@ -208,25 +213,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 } @@ -236,5 +256,6 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, - ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) + ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, + shellCompDirectiveLegacyCustomComp, shellCompDirectiveLegacyCustomArgsComp)) } |