cmd/completion.go
changeset 0 5abace724584
child 185 564d92b54b00
equal deleted inserted replaced
-1:000000000000 0:5abace724584
       
     1 // Copyright © 2017 Mikael Berthe <mikael@lilotux.net>
       
     2 //
       
     3 // Licensed under the MIT license.
       
     4 // Please see the LICENSE file is this directory.
       
     5 
       
     6 package cmd
       
     7 
       
     8 import (
       
     9 	"bytes"
       
    10 	"io"
       
    11 	"os"
       
    12 	"strings"
       
    13 
       
    14 	"github.com/spf13/cobra"
       
    15 )
       
    16 
       
    17 var completionCmd = &cobra.Command{
       
    18 	Use:       "completion bash|zsh",
       
    19 	Short:     "Generate shell completion",
       
    20 	ValidArgs: []string{"bash", "zsh"},
       
    21 	Run: func(cmd *cobra.Command, args []string) {
       
    22 		if len(args) < 1 {
       
    23 			errPrint("Please specify your shell")
       
    24 			os.Exit(1)
       
    25 		}
       
    26 
       
    27 		switch args[0] {
       
    28 		case "bash":
       
    29 			if err := runCompletionBash(os.Stdout, RootCmd); err != nil {
       
    30 				errPrint("Error: %s", err.Error())
       
    31 				os.Exit(1)
       
    32 			}
       
    33 		case "zsh":
       
    34 			if err := runCompletionZsh(os.Stdout, RootCmd); err != nil {
       
    35 				errPrint("Error: %s", err.Error())
       
    36 				os.Exit(1)
       
    37 			}
       
    38 		default:
       
    39 			errPrint("Only bash is supported at the moment")
       
    40 			os.Exit(1)
       
    41 		}
       
    42 	},
       
    43 }
       
    44 
       
    45 func init() {
       
    46 	RootCmd.AddCommand(completionCmd)
       
    47 }
       
    48 
       
    49 func runCompletionBash(out io.Writer, c *cobra.Command) error {
       
    50 	return c.GenBashCompletion(out)
       
    51 }
       
    52 
       
    53 // Many thanks to the Kubernetes project for this one!
       
    54 func runCompletionZsh(out io.Writer, c *cobra.Command) error {
       
    55 	const zshInitialization = `# Copyright 2016 The Kubernetes Authors.
       
    56 #
       
    57 # Licensed under the Apache License, Version 2.0 (the "License");
       
    58 # you may not use this file except in compliance with the License.
       
    59 # You may obtain a copy of the License at
       
    60 #
       
    61 #     http://www.apache.org/licenses/LICENSE-2.0
       
    62 #
       
    63 # Unless required by applicable law or agreed to in writing, software
       
    64 # distributed under the License is distributed on an "AS IS" BASIS,
       
    65 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    66 # See the License for the specific language governing permissions and
       
    67 # limitations under the License.
       
    68 
       
    69 __madonctl_bash_source() {
       
    70 	alias shopt=':'
       
    71 	alias _expand=_bash_expand
       
    72 	alias _complete=_bash_comp
       
    73 	emulate -L sh
       
    74 	setopt kshglob noshglob braceexpand
       
    75 
       
    76 	source "$@"
       
    77 }
       
    78 
       
    79 __madonctl_type() {
       
    80 	# -t is not supported by zsh
       
    81 	if [ "$1" == "-t" ]; then
       
    82 		shift
       
    83 
       
    84 		# fake Bash 4 to disable "complete -o nospace". Instead
       
    85 		# "compopt +-o nospace" is used in the code to toggle trailing
       
    86 		# spaces. We don't support that, but leave trailing spaces on
       
    87 		# all the time
       
    88 		if [ "$1" = "__madonctl_compopt" ]; then
       
    89 			echo builtin
       
    90 			return 0
       
    91 		fi
       
    92 	fi
       
    93 	type "$@"
       
    94 }
       
    95 
       
    96 __madonctl_compgen() {
       
    97 	local completions w
       
    98 	completions=( $(compgen "$@") ) || return $?
       
    99 
       
   100 	# filter by given word as prefix
       
   101 	while [[ "$1" = -* && "$1" != -- ]]; do
       
   102 		shift
       
   103 		shift
       
   104 	done
       
   105 	if [[ "$1" == -- ]]; then
       
   106 		shift
       
   107 	fi
       
   108 	for w in "${completions[@]}"; do
       
   109 		if [[ "${w}" = "$1"* ]]; then
       
   110 			echo "${w}"
       
   111 		fi
       
   112 	done
       
   113 }
       
   114 
       
   115 __madonctl_compopt() {
       
   116 	true # don't do anything. Not supported by bashcompinit in zsh
       
   117 }
       
   118 
       
   119 __madonctl_declare() {
       
   120 	if [ "$1" == "-F" ]; then
       
   121 		whence -w "$@"
       
   122 	else
       
   123 		builtin declare "$@"
       
   124 	fi
       
   125 }
       
   126 
       
   127 __madonctl_ltrim_colon_completions()
       
   128 {
       
   129 	if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
       
   130 		# Remove colon-word prefix from COMPREPLY items
       
   131 		local colon_word=${1%${1##*:}}
       
   132 		local i=${#COMPREPLY[*]}
       
   133 		while [[ $((--i)) -ge 0 ]]; do
       
   134 			COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
       
   135 		done
       
   136 	fi
       
   137 }
       
   138 
       
   139 __madonctl_get_comp_words_by_ref() {
       
   140 	cur="${COMP_WORDS[COMP_CWORD]}"
       
   141 	prev="${COMP_WORDS[${COMP_CWORD}-1]}"
       
   142 	words=("${COMP_WORDS[@]}")
       
   143 	cword=("${COMP_CWORD[@]}")
       
   144 }
       
   145 
       
   146 __madonctl_filedir() {
       
   147 	local RET OLD_IFS w qw
       
   148 
       
   149 	__debug "_filedir $@ cur=$cur"
       
   150 	if [[ "$1" = \~* ]]; then
       
   151 		# somehow does not work. Maybe, zsh does not call this at all
       
   152 		eval echo "$1"
       
   153 		return 0
       
   154 	fi
       
   155 
       
   156 	OLD_IFS="$IFS"
       
   157 	IFS=$'\n'
       
   158 	if [ "$1" = "-d" ]; then
       
   159 		shift
       
   160 		RET=( $(compgen -d) )
       
   161 	else
       
   162 		RET=( $(compgen -f) )
       
   163 	fi
       
   164 	IFS="$OLD_IFS"
       
   165 
       
   166 	IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
       
   167 
       
   168 	for w in ${RET[@]}; do
       
   169 		if [[ ! "${w}" = "${cur}"* ]]; then
       
   170 			continue
       
   171 		fi
       
   172 		if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
       
   173 			qw="$(__madonctl_quote "${w}")"
       
   174 			if [ -d "${w}" ]; then
       
   175 				COMPREPLY+=("${qw}/")
       
   176 			else
       
   177 				COMPREPLY+=("${qw}")
       
   178 			fi
       
   179 		fi
       
   180 	done
       
   181 }
       
   182 
       
   183 __madonctl_quote() {
       
   184     if [[ $1 == \'* || $1 == \"* ]]; then
       
   185         # Leave out first character
       
   186         printf %q "${1:1}"
       
   187     else
       
   188         printf %q "$1"
       
   189     fi
       
   190 }
       
   191 
       
   192 autoload -U +X bashcompinit && bashcompinit
       
   193 
       
   194 # use word boundary patterns for BSD or GNU sed
       
   195 LWORD='[[:<:]]'
       
   196 RWORD='[[:>:]]'
       
   197 if sed --help 2>&1 | grep -q GNU; then
       
   198 	LWORD='\<'
       
   199 	RWORD='\>'
       
   200 fi
       
   201 
       
   202 __madonctl_convert_bash_to_zsh() {
       
   203 	sed \
       
   204 	-e 's/declare -F/whence -w/' \
       
   205 	-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
       
   206 	-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
       
   207 	-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
       
   208 	-e "s/${LWORD}_filedir${RWORD}/__madonctl_filedir/g" \
       
   209 	-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__madonctl_get_comp_words_by_ref/g" \
       
   210 	-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__madonctl_ltrim_colon_completions/g" \
       
   211 	-e "s/${LWORD}compgen${RWORD}/__madonctl_compgen/g" \
       
   212 	-e "s/${LWORD}compopt${RWORD}/__madonctl_compopt/g" \
       
   213 	-e "s/${LWORD}declare${RWORD}/__madonctl_declare/g" \
       
   214 	-e "s/\\\$(type${RWORD}/\$(__madonctl_type/g" \
       
   215 	<<'BASH_COMPLETION_EOF'
       
   216 `
       
   217 
       
   218 	const zshTail = `
       
   219 BASH_COMPLETION_EOF
       
   220 }
       
   221 
       
   222 __madonctl_bash_source <(__madonctl_convert_bash_to_zsh)
       
   223 `
       
   224 
       
   225 	buf := new(bytes.Buffer)
       
   226 	out.Write([]byte(zshInitialization))
       
   227 	err := c.GenBashCompletion(buf)
       
   228 	script := strings.Replace(buf.String(), "flaghash[${flagname}]", `flaghash[workaround]`, -1)
       
   229 	out.Write([]byte(script))
       
   230 	out.Write([]byte(zshTail))
       
   231 	return err
       
   232 }