Skip to content

Commit c7f0edd

Browse files
authored
Merge pull request #2353 from SlickNik/nm_zsh_completions
Add support for zsh to helm completion
2 parents e81673f + 49e7aa8 commit c7f0edd

File tree

5 files changed

+207
-23
lines changed

5 files changed

+207
-23
lines changed

cmd/helm/completion.go

Lines changed: 192 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,214 @@ limitations under the License.
1616
package main
1717

1818
import (
19+
"bytes"
20+
"fmt"
1921
"io"
2022

2123
"github.com/spf13/cobra"
2224
)
2325

2426
const completionDesc = `
25-
Generate bash autocompletions script for Helm.
27+
Generate autocompletions script for Helm for the specified shell (bash or zsh).
2628
27-
This command can generate shell autocompletions.
29+
This command can generate shell autocompletions. e.g.
2830
29-
$ helm completion
31+
$ helm completion bash
3032
3133
Can be sourced as such
3234
33-
$ source <(helm completion)
35+
$ source <(helm completion bash)
3436
`
3537

38+
var (
39+
completion_shells = map[string]func(out io.Writer, cmd *cobra.Command) error{
40+
"bash": runCompletionBash,
41+
"zsh": runCompletionZsh,
42+
}
43+
)
44+
3645
func newCompletionCmd(out io.Writer) *cobra.Command {
46+
shells := []string{}
47+
for s := range completion_shells {
48+
shells = append(shells, s)
49+
}
50+
3751
cmd := &cobra.Command{
38-
Use: "completion",
39-
Short: "Generate bash autocompletions script",
40-
Long: completionDesc,
41-
Hidden: false,
42-
RunE: func(cmd *cobra.Command, _ []string) error {
43-
return cmd.Root().GenBashCompletion(out)
52+
Use: "completion SHELL",
53+
Short: "Generate autocompletions script for the specified shell (bash or zsh)",
54+
Long: completionDesc,
55+
RunE: func(cmd *cobra.Command, args []string) error {
56+
return RunCompletion(out, cmd, args)
4457
},
58+
ValidArgs: shells,
4559
}
60+
4661
return cmd
4762
}
63+
64+
func RunCompletion(out io.Writer, cmd *cobra.Command, args []string) error {
65+
if len(args) == 0 {
66+
return fmt.Errorf("Shell not specified.")
67+
}
68+
if len(args) > 1 {
69+
return fmt.Errorf("Too many arguments. Expected only the shell type.")
70+
}
71+
run, found := completion_shells[args[0]]
72+
if !found {
73+
return fmt.Errorf("Unsupported shell type %q.", args[0])
74+
}
75+
76+
return run(out, cmd)
77+
}
78+
79+
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
80+
return cmd.Root().GenBashCompletion(out)
81+
}
82+
83+
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
84+
zsh_initialization := `
85+
__helm_bash_source() {
86+
alias shopt=':'
87+
alias _expand=_bash_expand
88+
alias _complete=_bash_comp
89+
emulate -L sh
90+
setopt kshglob noshglob braceexpand
91+
source "$@"
92+
}
93+
__helm_type() {
94+
# -t is not supported by zsh
95+
if [ "$1" == "-t" ]; then
96+
shift
97+
# fake Bash 4 to disable "complete -o nospace". Instead
98+
# "compopt +-o nospace" is used in the code to toggle trailing
99+
# spaces. We don't support that, but leave trailing spaces on
100+
# all the time
101+
if [ "$1" = "__helm_compopt" ]; then
102+
echo builtin
103+
return 0
104+
fi
105+
fi
106+
type "$@"
107+
}
108+
__helm_compgen() {
109+
local completions w
110+
completions=( $(compgen "$@") ) || return $?
111+
# filter by given word as prefix
112+
while [[ "$1" = -* && "$1" != -- ]]; do
113+
shift
114+
shift
115+
done
116+
if [[ "$1" == -- ]]; then
117+
shift
118+
fi
119+
for w in "${completions[@]}"; do
120+
if [[ "${w}" = "$1"* ]]; then
121+
echo "${w}"
122+
fi
123+
done
124+
}
125+
__helm_compopt() {
126+
true # don't do anything. Not supported by bashcompinit in zsh
127+
}
128+
__helm_declare() {
129+
if [ "$1" == "-F" ]; then
130+
whence -w "$@"
131+
else
132+
builtin declare "$@"
133+
fi
134+
}
135+
__helm_ltrim_colon_completions()
136+
{
137+
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
138+
# Remove colon-word prefix from COMPREPLY items
139+
local colon_word=${1%${1##*:}}
140+
local i=${#COMPREPLY[*]}
141+
while [[ $((--i)) -ge 0 ]]; do
142+
COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
143+
done
144+
fi
145+
}
146+
__helm_get_comp_words_by_ref() {
147+
cur="${COMP_WORDS[COMP_CWORD]}"
148+
prev="${COMP_WORDS[${COMP_CWORD}-1]}"
149+
words=("${COMP_WORDS[@]}")
150+
cword=("${COMP_CWORD[@]}")
151+
}
152+
__helm_filedir() {
153+
local RET OLD_IFS w qw
154+
__debug "_filedir $@ cur=$cur"
155+
if [[ "$1" = \~* ]]; then
156+
# somehow does not work. Maybe, zsh does not call this at all
157+
eval echo "$1"
158+
return 0
159+
fi
160+
OLD_IFS="$IFS"
161+
IFS=$'\n'
162+
if [ "$1" = "-d" ]; then
163+
shift
164+
RET=( $(compgen -d) )
165+
else
166+
RET=( $(compgen -f) )
167+
fi
168+
IFS="$OLD_IFS"
169+
IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
170+
for w in ${RET[@]}; do
171+
if [[ ! "${w}" = "${cur}"* ]]; then
172+
continue
173+
fi
174+
if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
175+
qw="$(__helm_quote "${w}")"
176+
if [ -d "${w}" ]; then
177+
COMPREPLY+=("${qw}/")
178+
else
179+
COMPREPLY+=("${qw}")
180+
fi
181+
fi
182+
done
183+
}
184+
__helm_quote() {
185+
if [[ $1 == \'* || $1 == \"* ]]; then
186+
# Leave out first character
187+
printf %q "${1:1}"
188+
else
189+
printf %q "$1"
190+
fi
191+
}
192+
autoload -U +X bashcompinit && bashcompinit
193+
# use word boundary patterns for BSD or GNU sed
194+
LWORD='[[:<:]]'
195+
RWORD='[[:>:]]'
196+
if sed --help 2>&1 | grep -q GNU; then
197+
LWORD='\<'
198+
RWORD='\>'
199+
fi
200+
__helm_convert_bash_to_zsh() {
201+
sed \
202+
-e 's/declare -F/whence -w/' \
203+
-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
204+
-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
205+
-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
206+
-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
207+
-e "s/${LWORD}_filedir${RWORD}/__helm_filedir/g" \
208+
-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__helm_get_comp_words_by_ref/g" \
209+
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \
210+
-e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \
211+
-e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \
212+
-e "s/${LWORD}declare${RWORD}/__helm_declare/g" \
213+
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
214+
<<'BASH_COMPLETION_EOF'
215+
`
216+
out.Write([]byte(zsh_initialization))
217+
218+
buf := new(bytes.Buffer)
219+
cmd.Root().GenBashCompletion(buf)
220+
out.Write(buf.Bytes())
221+
222+
zsh_tail := `
223+
BASH_COMPLETION_EOF
224+
}
225+
__helm_bash_source <(__helm_convert_bash_to_zsh)
226+
`
227+
out.Write([]byte(zsh_tail))
228+
return nil
229+
}

docs/helm/helm.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Environment:
4040
```
4141

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

docs/helm/helm_completion.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
## helm completion
22

3-
Generate bash autocompletions script
3+
Generate autocompletions script for the specified shell (bash or zsh)
44

55
### Synopsis
66

77

88

9-
Generate bash autocompletions script for Helm.
9+
Generate autocompletions script for Helm for the specified shell (bash or zsh).
1010

11-
This command can generate shell autocompletions.
11+
This command can generate shell autocompletions. e.g.
1212

13-
$ helm completion
13+
$ helm completion bash
1414

1515
Can be sourced as such
1616

17-
$ source <(helm completion)
17+
$ source <(helm completion bash)
1818

1919

2020
```
21-
helm completion
21+
helm completion SHELL
2222
```
2323

2424
### Options inherited from parent commands

docs/man/man1/helm_completion.1

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,26 @@
55

66
.SH NAME
77
.PP
8-
helm\-completion \- Generate bash autocompletions script
8+
helm\-completion \- Generate autocompletions script for the specified shell (bash or zsh)
99

1010

1111
.SH SYNOPSIS
1212
.PP
13-
\fBhelm completion\fP
13+
\fBhelm completion SHELL\fP
1414

1515

1616
.SH DESCRIPTION
1717
.PP
18-
Generate bash autocompletions script for Helm.
18+
Generate autocompletions script for Helm for the specified shell (bash or zsh).
1919

2020
.PP
21-
This command can generate shell autocompletions.
21+
This command can generate shell autocompletions. e.g.
2222

2323
.PP
2424
.RS
2525

2626
.nf
27-
$ helm completion
27+
$ helm completion bash
2828

2929
.fi
3030
.RE
@@ -36,7 +36,7 @@ Can be sourced as such
3636
.RS
3737

3838
.nf
39-
$ source <(helm completion)
39+
$ source <(helm completion bash)
4040

4141
.fi
4242
.RE

scripts/completions.bash

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ _helm_completion()
242242

243243
must_have_one_flag=()
244244
must_have_one_noun=()
245+
must_have_one_noun+=("bash")
246+
must_have_one_noun+=("zsh")
245247
noun_aliases=()
246248
}
247249

0 commit comments

Comments
 (0)