From 0490c288f5aa02c26c03eb8472b9aff235bed1ed Mon Sep 17 00:00:00 2001 From: knrt10 Date: Wed, 28 Oct 2020 12:47:15 +0530 Subject: [PATCH] completion: move to native zshCompletion Cobra https://github.com/spf13/cobra/releases/tag/v1.1.1 fixed the issue which did not need zshCompletion to be changed to bash. closes: #8893 Signed-off-by: knrt10 --- cmd/helm/completion.go | 160 ++++++---------------------------------- cmd/helm/search_repo.go | 19 ----- 2 files changed, 21 insertions(+), 158 deletions(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 275483f45..68c6a9b59 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -103,6 +103,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command { return runCompletionZsh(out, cmd) }, } + zsh.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText) fish := &cobra.Command{ Use: "fish", @@ -145,148 +146,29 @@ fi } func runCompletionZsh(out io.Writer, cmd *cobra.Command) error { - zshInitialization := `#compdef helm - -__helm_bash_source() { - alias shopt=':' - alias _expand=_bash_expand - alias _complete=_bash_comp - emulate -L sh - setopt kshglob noshglob braceexpand - source "$@" -} -__helm_type() { - # -t is not supported by zsh - if [ "$1" == "-t" ]; then - shift - # fake Bash 4 to disable "complete -o nospace". Instead - # "compopt +-o nospace" is used in the code to toggle trailing - # spaces. We don't support that, but leave trailing spaces on - # all the time - if [ "$1" = "__helm_compopt" ]; then - echo builtin - return 0 - fi - fi - type "$@" -} -__helm_compgen() { - local completions w - completions=( $(compgen "$@") ) || return $? - # filter by given word as prefix - while [[ "$1" = -* && "$1" != -- ]]; do - shift - shift - done - if [[ "$1" == -- ]]; then - shift - fi - for w in "${completions[@]}"; do - if [[ "${w}" = "$1"* ]]; then - # Use printf instead of echo because it is possible that - # the value to print is -n, which would be interpreted - # as a flag to echo - printf "%s\n" "${w}" - fi - done -} -__helm_compopt() { - true # don't do anything. Not supported by bashcompinit in zsh -} -__helm_ltrim_colon_completions() -{ - if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then - # Remove colon-word prefix from COMPREPLY items - local colon_word=${1%${1##*:}} - local i=${#COMPREPLY[*]} - while [[ $((--i)) -ge 0 ]]; do - COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} - done - fi -} -__helm_get_comp_words_by_ref() { - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[${COMP_CWORD}-1]}" - words=("${COMP_WORDS[@]}") - cword=("${COMP_CWORD[@]}") -} -__helm_filedir() { - local RET OLD_IFS w qw - __debug "_filedir $@ cur=$cur" - if [[ "$1" = \~* ]]; then - # somehow does not work. Maybe, zsh does not call this at all - eval echo "$1" - return 0 - fi - OLD_IFS="$IFS" - IFS=$'\n' - if [ "$1" = "-d" ]; then - shift - RET=( $(compgen -d) ) - else - RET=( $(compgen -f) ) - fi - IFS="$OLD_IFS" - IFS="," __debug "RET=${RET[@]} len=${#RET[@]}" - for w in ${RET[@]}; do - if [[ ! "${w}" = "${cur}"* ]]; then - continue - fi - if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then - qw="$(__helm_quote "${w}")" - if [ -d "${w}" ]; then - COMPREPLY+=("${qw}/") - else - COMPREPLY+=("${qw}") - fi - fi - done -} -__helm_quote() { - if [[ $1 == \'* || $1 == \"* ]]; then - # Leave out first character - printf %q "${1:1}" - else - printf %q "$1" - fi -} -autoload -U +X bashcompinit && bashcompinit -# use word boundary patterns for BSD or GNU sed -LWORD='[[:<:]]' -RWORD='[[:>:]]' -if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then - LWORD='\<' - RWORD='\>' -fi -__helm_convert_bash_to_zsh() { - sed \ - -e 's/declare -F/whence -w/' \ - -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ - -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ - -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ - -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ - -e "s/${LWORD}_filedir${RWORD}/__helm_filedir/g" \ - -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__helm_get_comp_words_by_ref/g" \ - -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \ - -e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \ - -e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \ - -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ - -e "s/\\\$(type${RWORD}/\$(__helm_type/g" \ - -e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \ - -e 's/FUNCNAME/funcstack/g' \ - <<'BASH_COMPLETION_EOF' + var err error + if disableCompDescriptions { + err = cmd.Root().GenZshCompletionNoDesc(out) + } else { + err = cmd.Root().GenZshCompletion(out) + } + + // In case the user renamed the helm binary (e.g., to be able to run + // both helm2 and helm3), we hook the new binary name to the completion function + if binary := filepath.Base(os.Args[0]); binary != "helm" { + renamedBinaryHook := ` +# Hook the command used to generate the completion script +# to the helm completion function to handle the case where +# the user renamed the helm binary +compdef _helm %[1]s ` - out.Write([]byte(zshInitialization)) + fmt.Fprintf(out, renamedBinaryHook, binary) + } - runCompletionBash(out, cmd) + // Cobra doesn't source zsh completion file, explicitly doing it here + fmt.Fprintf(out, "compdef _helm helm") - zshTail := ` -BASH_COMPLETION_EOF -} -__helm_bash_source <(__helm_convert_bash_to_zsh) -` - out.Write([]byte(zshTail)) - return nil + return err } func runCompletionFish(out io.Writer, cmd *cobra.Command) error { diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index bf82a6051..8c52b15b0 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -360,9 +360,6 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell } if noSpace { directive = directive | cobra.ShellCompDirectiveNoSpace - // The cobra.ShellCompDirective flags do not work for zsh right now. - // We handle it ourselves instead. - completions = compEnforceNoSpace(completions) } if !includeFiles { // If we should not include files in the completions, @@ -371,19 +368,3 @@ func compListCharts(toComplete string, includeFiles bool) ([]string, cobra.Shell } return completions, directive } - -// This function prevents the shell from adding a space after -// a completion by adding a second, fake completion. -// It is only needed for zsh, but we cannot tell which shell -// is being used here, so we do the fake completion all the time; -// there are no real downsides to doing this for bash as well. -func compEnforceNoSpace(completions []string) []string { - // To prevent the shell from adding space after the completion, - // we trick it by pretending there is a second, longer match. - // We only do this if there is a single choice for completion. - if len(completions) == 1 { - completions = append(completions, completions[0]+".") - cobra.CompDebugln(fmt.Sprintf("compEnforceNoSpace: completions now are %v", completions), settings.Debug) - } - return completions -}