Add support for zsh to helm completion

Updated the 'helm completion' command to take in the shell
as a parameter. Currently acceptable options are 'bash' and
'zsh'. Also fixed the completions to work with zsh in a manner
similar to what kubectl does.

Also updated the docs to reflect this change.

Closes #2201
pull/2353/head
Nikhil Manchanda 8 years ago
parent 12300745d3
commit 49e7aa854a

@ -16,32 +16,214 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"fmt"
"io" "io"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const completionDesc = ` const completionDesc = `
Generate bash autocompletions script for Helm. Generate autocompletions script for Helm for the specified shell (bash or zsh).
This command can generate shell autocompletions. This command can generate shell autocompletions. e.g.
$ helm completion $ helm completion bash
Can be sourced as such Can be sourced as such
$ source <(helm completion) $ source <(helm completion bash)
` `
var (
completion_shells = map[string]func(out io.Writer, cmd *cobra.Command) error{
"bash": runCompletionBash,
"zsh": runCompletionZsh,
}
)
func newCompletionCmd(out io.Writer) *cobra.Command { func newCompletionCmd(out io.Writer) *cobra.Command {
shells := []string{}
for s := range completion_shells {
shells = append(shells, s)
}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "completion", Use: "completion SHELL",
Short: "Generate bash autocompletions script", Short: "Generate autocompletions script for the specified shell (bash or zsh)",
Long: completionDesc, Long: completionDesc,
Hidden: false, RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error { return RunCompletion(out, cmd, args)
return cmd.Root().GenBashCompletion(out)
}, },
ValidArgs: shells,
} }
return cmd return cmd
} }
func RunCompletion(out io.Writer, cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("Shell not specified.")
}
if len(args) > 1 {
return fmt.Errorf("Too many arguments. Expected only the shell type.")
}
run, found := completion_shells[args[0]]
if !found {
return fmt.Errorf("Unsupported shell type %q.", args[0])
}
return run(out, cmd)
}
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
return cmd.Root().GenBashCompletion(out)
}
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
zsh_initialization := `
__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
echo "${w}"
fi
done
}
__helm_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__helm_declare() {
if [ "$1" == "-F" ]; then
whence -w "$@"
else
builtin declare "$@"
fi
}
__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; 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}/__helm_declare/g" \
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
<<'BASH_COMPLETION_EOF'
`
out.Write([]byte(zsh_initialization))
buf := new(bytes.Buffer)
cmd.Root().GenBashCompletion(buf)
out.Write(buf.Bytes())
zsh_tail := `
BASH_COMPLETION_EOF
}
__helm_bash_source <(__helm_convert_bash_to_zsh)
`
out.Write([]byte(zsh_tail))
return nil
}

@ -40,7 +40,7 @@ Environment:
``` ```
### SEE ALSO ### SEE ALSO
* [helm completion](helm_completion.md) - Generate bash autocompletions script * [helm completion](helm_completion.md) - Generate autocompletions script for the specified shell (bash or zsh)
* [helm create](helm_create.md) - create a new chart with the given name * [helm create](helm_create.md) - create a new chart with the given name
* [helm delete](helm_delete.md) - given a release name, delete the release from Kubernetes * [helm delete](helm_delete.md) - given a release name, delete the release from Kubernetes
* [helm dependency](helm_dependency.md) - manage a chart's dependencies * [helm dependency](helm_dependency.md) - manage a chart's dependencies

@ -1,24 +1,24 @@
## helm completion ## helm completion
Generate bash autocompletions script Generate autocompletions script for the specified shell (bash or zsh)
### Synopsis ### Synopsis
Generate bash autocompletions script for Helm. Generate autocompletions script for Helm for the specified shell (bash or zsh).
This command can generate shell autocompletions. This command can generate shell autocompletions. e.g.
$ helm completion $ helm completion bash
Can be sourced as such Can be sourced as such
$ source <(helm completion) $ source <(helm completion bash)
``` ```
helm completion helm completion SHELL
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

@ -5,26 +5,26 @@
.SH NAME .SH NAME
.PP .PP
helm\-completion \- Generate bash autocompletions script helm\-completion \- Generate autocompletions script for the specified shell (bash or zsh)
.SH SYNOPSIS .SH SYNOPSIS
.PP .PP
\fBhelm completion\fP \fBhelm completion SHELL\fP
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
Generate bash autocompletions script for Helm. Generate autocompletions script for Helm for the specified shell (bash or zsh).
.PP .PP
This command can generate shell autocompletions. This command can generate shell autocompletions. e.g.
.PP .PP
.RS .RS
.nf .nf
$ helm completion $ helm completion bash
.fi .fi
.RE .RE
@ -36,7 +36,7 @@ Can be sourced as such
.RS .RS
.nf .nf
$ source <(helm completion) $ source <(helm completion bash)
.fi .fi
.RE .RE

@ -242,6 +242,8 @@ _helm_completion()
must_have_one_flag=() must_have_one_flag=()
must_have_one_noun=() must_have_one_noun=()
must_have_one_noun+=("bash")
must_have_one_noun+=("zsh")
noun_aliases=() noun_aliases=()
} }

Loading…
Cancel
Save