github.com/simonferquel/app@v0.6.1-0.20181012141724-68b7cccf26ac/cmd/docker-app/completion.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/docker/cli/cli" 9 "github.com/docker/cli/cli/command" 10 "github.com/spf13/cobra" 11 ) 12 13 func completionCmd(dockerCli command.Cli, rootCmd *cobra.Command) *cobra.Command { 14 return &cobra.Command{ 15 Use: "completion SHELL", 16 Short: "Generates completion scripts for the specified shell (bash or zsh)", 17 Long: `# Load the docker-app completion code for bash into the current shell 18 . <(docker-app completion bash) 19 # Set the docker-app completion code for bash to autoload on startup in your ~/.bashrc, 20 # ~/.profile or ~/.bash_profile 21 . <(docker-app completion bash) 22 # Note: bash-completion is needed. 23 24 # Load the docker-app completion code for zsh into the current shell 25 source <(docker-app completion zsh) 26 # Set the docker-app completion code for zsh to autoload on startup in your ~/.zshrc 27 source <(docker-app completion zsh) 28 `, 29 Args: cli.RequiresMaxArgs(1), 30 RunE: func(cmd *cobra.Command, args []string) error { 31 switch { 32 case len(args) == 0: 33 return rootCmd.GenBashCompletion(dockerCli.Out()) 34 case args[0] == "bash": 35 return rootCmd.GenBashCompletion(dockerCli.Out()) 36 case args[0] == "zsh": 37 return runCompletionZsh(dockerCli.Out(), rootCmd) 38 default: 39 return fmt.Errorf("%q is not a supported shell", args[0]) 40 } 41 }, 42 } 43 } 44 45 const ( 46 // Largely inspired by https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/completion.go 47 zshHead = `#compdef dockerapp 48 __dockerapp_bash_source() { 49 alias shopt=':' 50 alias _expand=_bash_expand 51 alias _complete=_bash_comp 52 emulate -L sh 53 setopt kshglob noshglob braceexpand 54 source "$@" 55 } 56 __dockerapp_type() { 57 # -t is not supported by zsh 58 if [ "$1" == "-t" ]; then 59 shift 60 # fake Bash 4 to disable "complete -o nospace". Instead 61 # "compopt +-o nospace" is used in the code to toggle trailing 62 # spaces. We don't support that, but leave trailing spaces on 63 # all the time 64 if [ "$1" = "__dockerapp_compopt" ]; then 65 echo builtin 66 return 0 67 fi 68 fi 69 type "$@" 70 } 71 __dockerapp_compgen() { 72 local completions w 73 completions=( $(compgen "$@") ) || return $? 74 # filter by given word as prefix 75 while [[ "$1" = -* && "$1" != -- ]]; do 76 shift 77 shift 78 done 79 if [[ "$1" == -- ]]; then 80 shift 81 fi 82 for w in "${completions[@]}"; do 83 if [[ "${w}" = "$1"* ]]; then 84 echo "${w}" 85 fi 86 done 87 } 88 __dockerapp_compopt() { 89 true # don't do anything. Not supported by bashcompinit in zsh 90 } 91 __dockerapp_ltrim_colon_completions() 92 { 93 if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then 94 # Remove colon-word prefix from COMPREPLY items 95 local colon_word=${1%${1##*:}} 96 local i=${#COMPREPLY[*]} 97 while [[ $((--i)) -ge 0 ]]; do 98 COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} 99 done 100 fi 101 } 102 __dockerapp_get_comp_words_by_ref() { 103 cur="${COMP_WORDS[COMP_CWORD]}" 104 prev="${COMP_WORDS[${COMP_CWORD}-1]}" 105 words=("${COMP_WORDS[@]}") 106 cword=("${COMP_CWORD[@]}") 107 } 108 __dockerapp_filedir() { 109 local RET OLD_IFS w qw 110 __debug "_filedir $@ cur=$cur" 111 if [[ "$1" = \~* ]]; then 112 # somehow does not work. Maybe, zsh does not call this at all 113 eval echo "$1" 114 return 0 115 fi 116 OLD_IFS="$IFS" 117 IFS=$'\n' 118 if [ "$1" = "-d" ]; then 119 shift 120 RET=( $(compgen -d) ) 121 else 122 RET=( $(compgen -f) ) 123 fi 124 IFS="$OLD_IFS" 125 IFS="," __debug "RET=${RET[@]} len=${#RET[@]}" 126 for w in ${RET[@]}; do 127 if [[ ! "${w}" = "${cur}"* ]]; then 128 continue 129 fi 130 if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then 131 qw="$(__dockerapp_quote "${w}")" 132 if [ -d "${w}" ]; then 133 COMPREPLY+=("${qw}/") 134 else 135 COMPREPLY+=("${qw}") 136 fi 137 fi 138 done 139 } 140 __dockerapp_quote() { 141 if [[ $1 == \'* || $1 == \"* ]]; then 142 # Leave out first character 143 printf %q "${1:1}" 144 else 145 printf %q "$1" 146 fi 147 } 148 autoload -U +X bashcompinit && bashcompinit 149 # use word boundary patterns for BSD or GNU sed 150 LWORD='[[:<:]]' 151 RWORD='[[:>:]]' 152 if sed --help 2>&1 | grep -q GNU; then 153 LWORD='\<' 154 RWORD='\>' 155 fi 156 __dockerapp_convert_bash_to_zsh() { 157 sed \ 158 -e 's/declare -F/whence -w/' \ 159 -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ 160 -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ 161 -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ 162 -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ 163 -e "s/${LWORD}_filedir${RWORD}/__dockerapp_filedir/g" \ 164 -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__dockerapp_get_comp_words_by_ref/g" \ 165 -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__dockerapp_ltrim_colon_completions/g" \ 166 -e "s/${LWORD}compgen${RWORD}/__dockerapp_compgen/g" \ 167 -e "s/${LWORD}compopt${RWORD}/__dockerapp_compopt/g" \ 168 -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ 169 -e "s/\\\$(type${RWORD}/\$(__dockerapp_type/g" \ 170 <<'BASH_COMPLETION_EOF' 171 ` 172 zshTail = ` 173 BASH_COMPLETION_EOF 174 } 175 __dockerapp_bash_source <(__dockerapp_convert_bash_to_zsh) 176 _complete dockerapp 2>/dev/null 177 ` 178 ) 179 180 func runCompletionZsh(out io.Writer, rootCmd *cobra.Command) error { 181 fmt.Fprint(out, zshHead) 182 buf := new(bytes.Buffer) 183 rootCmd.GenBashCompletion(buf) 184 fmt.Fprint(out, buf.String()) 185 fmt.Fprint(out, zshTail) 186 return nil 187 }