github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/cmd/kcfi/completion.go (about) 1 /* 2 Copyright The Helm Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package main 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 24 "github.com/pkg/errors" 25 "github.com/spf13/cobra" 26 ) 27 28 const completionDesc = ` 29 Generate autocompletions script for Helm for the specified shell (bash or zsh). 30 31 This command can generate shell autocompletions. e.g. 32 33 $ helm completion bash 34 35 Can be sourced as such 36 37 $ source <(helm completion bash) 38 ` 39 40 var ( 41 completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{ 42 "bash": runCompletionBash, 43 "zsh": runCompletionZsh, 44 } 45 ) 46 47 func newCompletionCmd(out io.Writer) *cobra.Command { 48 shells := []string{} 49 for s := range completionShells { 50 shells = append(shells, s) 51 } 52 53 cmd := &cobra.Command{ 54 Use: "completion SHELL", 55 Short: "generate autocompletions script for the specified shell (bash or zsh)", 56 Long: completionDesc, 57 RunE: func(cmd *cobra.Command, args []string) error { 58 return runCompletion(out, cmd, args) 59 }, 60 ValidArgs: shells, 61 } 62 63 return cmd 64 } 65 66 func runCompletion(out io.Writer, cmd *cobra.Command, args []string) error { 67 if len(args) == 0 { 68 return errors.New("shell not specified") 69 } 70 if len(args) > 1 { 71 return errors.New("too many arguments, expected only the shell type") 72 } 73 run, found := completionShells[args[0]] 74 if !found { 75 return errors.Errorf("unsupported shell type %q", args[0]) 76 } 77 78 return run(out, cmd) 79 } 80 81 func runCompletionBash(out io.Writer, cmd *cobra.Command) error { 82 err := cmd.Root().GenBashCompletion(out) 83 84 // In case the user renamed the helm binary (e.g., to be able to run 85 // both helm2 and helm3), we hook the new binary name to the completion function 86 if binary := filepath.Base(os.Args[0]); binary != "helm" { 87 renamedBinaryHook := ` 88 # Hook the command used to generate the completion script 89 # to the helm completion function to handle the case where 90 # the user renamed the helm binary 91 if [[ $(type -t compopt) = "builtin" ]]; then 92 complete -o default -F __start_helm %[1]s 93 else 94 complete -o default -o nospace -F __start_helm %[1]s 95 fi 96 ` 97 fmt.Fprintf(out, renamedBinaryHook, binary) 98 } 99 100 return err 101 } 102 103 func runCompletionZsh(out io.Writer, cmd *cobra.Command) error { 104 zshInitialization := `#compdef helm 105 106 __helm_bash_source() { 107 alias shopt=':' 108 alias _expand=_bash_expand 109 alias _complete=_bash_comp 110 emulate -L sh 111 setopt kshglob noshglob braceexpand 112 source "$@" 113 } 114 __helm_type() { 115 # -t is not supported by zsh 116 if [ "$1" == "-t" ]; then 117 shift 118 # fake Bash 4 to disable "complete -o nospace". Instead 119 # "compopt +-o nospace" is used in the code to toggle trailing 120 # spaces. We don't support that, but leave trailing spaces on 121 # all the time 122 if [ "$1" = "__helm_compopt" ]; then 123 echo builtin 124 return 0 125 fi 126 fi 127 type "$@" 128 } 129 __helm_compgen() { 130 local completions w 131 completions=( $(compgen "$@") ) || return $? 132 # filter by given word as prefix 133 while [[ "$1" = -* && "$1" != -- ]]; do 134 shift 135 shift 136 done 137 if [[ "$1" == -- ]]; then 138 shift 139 fi 140 for w in "${completions[@]}"; do 141 if [[ "${w}" = "$1"* ]]; then 142 # Use printf instead of echo beause it is possible that 143 # the value to print is -n, which would be interpreted 144 # as a flag to echo 145 printf "%s\n" "${w}" 146 fi 147 done 148 } 149 __helm_compopt() { 150 true # don't do anything. Not supported by bashcompinit in zsh 151 } 152 __helm_ltrim_colon_completions() 153 { 154 if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then 155 # Remove colon-word prefix from COMPREPLY items 156 local colon_word=${1%${1##*:}} 157 local i=${#COMPREPLY[*]} 158 while [[ $((--i)) -ge 0 ]]; do 159 COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} 160 done 161 fi 162 } 163 __helm_get_comp_words_by_ref() { 164 cur="${COMP_WORDS[COMP_CWORD]}" 165 prev="${COMP_WORDS[${COMP_CWORD}-1]}" 166 words=("${COMP_WORDS[@]}") 167 cword=("${COMP_CWORD[@]}") 168 } 169 __helm_filedir() { 170 local RET OLD_IFS w qw 171 __debug "_filedir $@ cur=$cur" 172 if [[ "$1" = \~* ]]; then 173 # somehow does not work. Maybe, zsh does not call this at all 174 eval echo "$1" 175 return 0 176 fi 177 OLD_IFS="$IFS" 178 IFS=$'\n' 179 if [ "$1" = "-d" ]; then 180 shift 181 RET=( $(compgen -d) ) 182 else 183 RET=( $(compgen -f) ) 184 fi 185 IFS="$OLD_IFS" 186 IFS="," __debug "RET=${RET[@]} len=${#RET[@]}" 187 for w in ${RET[@]}; do 188 if [[ ! "${w}" = "${cur}"* ]]; then 189 continue 190 fi 191 if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then 192 qw="$(__helm_quote "${w}")" 193 if [ -d "${w}" ]; then 194 COMPREPLY+=("${qw}/") 195 else 196 COMPREPLY+=("${qw}") 197 fi 198 fi 199 done 200 } 201 __helm_quote() { 202 if [[ $1 == \'* || $1 == \"* ]]; then 203 # Leave out first character 204 printf %q "${1:1}" 205 else 206 printf %q "$1" 207 fi 208 } 209 autoload -U +X bashcompinit && bashcompinit 210 # use word boundary patterns for BSD or GNU sed 211 LWORD='[[:<:]]' 212 RWORD='[[:>:]]' 213 if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then 214 LWORD='\<' 215 RWORD='\>' 216 fi 217 __helm_convert_bash_to_zsh() { 218 sed \ 219 -e 's/declare -F/whence -w/' \ 220 -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ 221 -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ 222 -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ 223 -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ 224 -e "s/${LWORD}_filedir${RWORD}/__helm_filedir/g" \ 225 -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__helm_get_comp_words_by_ref/g" \ 226 -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \ 227 -e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \ 228 -e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \ 229 -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ 230 -e "s/\\\$(type${RWORD}/\$(__helm_type/g" \ 231 -e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \ 232 -e 's/FUNCNAME/funcstack/g' \ 233 <<'BASH_COMPLETION_EOF' 234 ` 235 out.Write([]byte(zshInitialization)) 236 237 runCompletionBash(out, cmd) 238 239 zshTail := ` 240 BASH_COMPLETION_EOF 241 } 242 __helm_bash_source <(__helm_convert_bash_to_zsh) 243 ` 244 out.Write([]byte(zshTail)) 245 return nil 246 }