|
1 | 1 | package cmd |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "bytes" |
5 | | - "io" |
6 | | - |
7 | 4 | "github.com/spf13/cobra" |
8 | | - |
9 | | - "k8s.io/cli-runtime/pkg/genericclioptions" |
10 | 5 | cmdutil "k8s.io/kubectl/pkg/cmd/util" |
11 | 6 | "k8s.io/kubectl/pkg/util/templates" |
12 | 7 | ) |
@@ -41,183 +36,27 @@ var ( |
41 | 36 | ) |
42 | 37 |
|
43 | 38 | // newCmdCompletion creates the `completion` command |
44 | | -func newCmdCompletion(streams genericclioptions.IOStreams) *cobra.Command { |
45 | | - ops := newCompletionOptions(streams) |
| 39 | +func newCmdCompletion() *cobra.Command { |
46 | 40 | cmd := &cobra.Command{ |
47 | | - Use: "completion SHELL", |
48 | | - Short: "Output shell completion code for the specified shell (bash or zsh)", |
49 | | - Example: completionExample, |
50 | | - DisableAutoGenTag: true, |
51 | | - DisableFlagsInUseLine: true, |
52 | | - Run: func(cmd *cobra.Command, args []string) { |
53 | | - cmdutil.CheckErr(ops.run(cmd, args)) |
| 41 | + Use: "completion SHELL", |
| 42 | + Short: "Output shell completion code for the specified shell (bash or zsh)", |
| 43 | + Example: completionExample, |
| 44 | + DisableAutoGenTag: true, |
| 45 | + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), |
| 46 | + ValidArgs: []string{"bash", "zsh", "fish"}, |
| 47 | + RunE: func(cmd *cobra.Command, args []string) error { |
| 48 | + switch args[0] { |
| 49 | + case "bash": |
| 50 | + return cmd.Parent().GenBashCompletion(cmd.OutOrStdout()) |
| 51 | + case "zsh": |
| 52 | + return cmd.Root().GenZshCompletion(cmd.OutOrStdout()) |
| 53 | + case "fish": |
| 54 | + return cmd.Root().GenFishCompletion(cmd.OutOrStdout(), true) |
| 55 | + default: |
| 56 | + return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0]) |
| 57 | + } |
54 | 58 | }, |
55 | 59 | } |
56 | 60 |
|
57 | 61 | return cmd |
58 | 62 | } |
59 | | - |
60 | | -type completionOptions struct { |
61 | | - supportedShells []string |
62 | | - |
63 | | - genericclioptions.IOStreams |
64 | | -} |
65 | | - |
66 | | -func newCompletionOptions(streams genericclioptions.IOStreams) *completionOptions { |
67 | | - return &completionOptions{ |
68 | | - IOStreams: streams, |
69 | | - } |
70 | | -} |
71 | | - |
72 | | -func (o *completionOptions) run(cmd *cobra.Command, args []string) error { |
73 | | - if len(args) == 0 { |
74 | | - return cmdutil.UsageErrorf(cmd, "Shell not specified.") |
75 | | - } |
76 | | - if len(args) > 1 { |
77 | | - return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.") |
78 | | - } |
79 | | - switch args[0] { |
80 | | - case "bash": |
81 | | - return cmd.Parent().GenBashCompletion(o.Out) |
82 | | - case "zsh": |
83 | | - return genCompletionZsh(o.Out, cmd.Parent()) |
84 | | - default: |
85 | | - return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0]) |
86 | | - } |
87 | | -} |
88 | | - |
89 | | -// Followed the trick https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/completion/completion.go#L145. |
90 | | -func genCompletionZsh(out io.Writer, osdctl *cobra.Command) error { |
91 | | - zshHead := "#compdef osdctl\n" |
92 | | - _, err := out.Write([]byte(zshHead)) |
93 | | - if err != nil { |
94 | | - return err |
95 | | - } |
96 | | - |
97 | | - zshInitialization := ` |
98 | | -__osdctl_bash_source() { |
99 | | - alias shopt=':' |
100 | | - emulate -L sh |
101 | | - setopt kshglob noshglob braceexpand |
102 | | -
|
103 | | - source "$@" |
104 | | -} |
105 | | -
|
106 | | -__osdctl_type() { |
107 | | - # -t is not supported by zsh |
108 | | - if [ "$1" == "-t" ]; then |
109 | | - shift |
110 | | -
|
111 | | - # fake Bash 4 to disable "complete -o nospace". Instead |
112 | | - # "compopt +-o nospace" is used in the code to toggle trailing |
113 | | - # spaces. We don't support that, but leave trailing spaces on |
114 | | - # all the time |
115 | | - if [ "$1" = "__osdctl_compopt" ]; then |
116 | | - echo builtin |
117 | | - return 0 |
118 | | - fi |
119 | | - fi |
120 | | - type "$@" |
121 | | -} |
122 | | -
|
123 | | -__osdctl_compgen() { |
124 | | - local completions w |
125 | | - completions=( $(compgen "$@") ) || return $? |
126 | | -
|
127 | | - # filter by given word as prefix |
128 | | - while [[ "$1" = -* && "$1" != -- ]]; do |
129 | | - shift |
130 | | - shift |
131 | | - done |
132 | | - if [[ "$1" == -- ]]; then |
133 | | - shift |
134 | | - fi |
135 | | - for w in "${completions[@]}"; do |
136 | | - if [[ "${w}" = "$1"* ]]; then |
137 | | - echo "${w}" |
138 | | - fi |
139 | | - done |
140 | | -} |
141 | | -
|
142 | | -__osdctl_compopt() { |
143 | | - true # don't do anything. Not supported by bashcompinit in zsh |
144 | | -} |
145 | | -
|
146 | | -__osdctl_ltrim_colon_completions() |
147 | | -{ |
148 | | - if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then |
149 | | - # Remove colon-word prefix from COMPREPLY items |
150 | | - local colon_word=${1%${1##*:}} |
151 | | - local i=${#COMPREPLY[*]} |
152 | | - while [[ $((--i)) -ge 0 ]]; do |
153 | | - COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} |
154 | | - done |
155 | | - fi |
156 | | -} |
157 | | -
|
158 | | -__osdctl_get_comp_words_by_ref() { |
159 | | - cur="${COMP_WORDS[COMP_CWORD]}" |
160 | | - prev="${COMP_WORDS[${COMP_CWORD}-1]}" |
161 | | - words=("${COMP_WORDS[@]}") |
162 | | - cword=("${COMP_CWORD[@]}") |
163 | | -} |
164 | | -
|
165 | | -__osdctl_filedir() { |
166 | | - # Don't need to do anything here. |
167 | | - # Otherwise we will get trailing space without "compopt -o nospace" |
168 | | - true |
169 | | -} |
170 | | -
|
171 | | -autoload -U +X bashcompinit && bashcompinit |
172 | | -
|
173 | | -# use word boundary patterns for BSD or GNU sed |
174 | | -LWORD='[[:<:]]' |
175 | | -RWORD='[[:>:]]' |
176 | | -if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then |
177 | | - LWORD='\<' |
178 | | - RWORD='\>' |
179 | | -fi |
180 | | -
|
181 | | -__osdctl_convert_bash_to_zsh() { |
182 | | - sed \ |
183 | | - -e 's/declare -F/whence -w/' \ |
184 | | - -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ |
185 | | - -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ |
186 | | - -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ |
187 | | - -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ |
188 | | - -e "s/${LWORD}_filedir${RWORD}/__osdctl_filedir/g" \ |
189 | | - -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__osdctl_get_comp_words_by_ref/g" \ |
190 | | - -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__osdctl_ltrim_colon_completions/g" \ |
191 | | - -e "s/${LWORD}compgen${RWORD}/__osdctl_compgen/g" \ |
192 | | - -e "s/${LWORD}compopt${RWORD}/__osdctl_compopt/g" \ |
193 | | - -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ |
194 | | - -e "s/\\\$(type${RWORD}/\$(__osdctl_type/g" \ |
195 | | - <<'BASH_COMPLETION_EOF' |
196 | | -` |
197 | | - _, err = out.Write([]byte(zshInitialization)) |
198 | | - if err != nil { |
199 | | - return err |
200 | | - } |
201 | | - |
202 | | - buf := new(bytes.Buffer) |
203 | | - err = osdctl.GenBashCompletion(buf) |
204 | | - if err != nil { |
205 | | - return err |
206 | | - } |
207 | | - _, err = out.Write(buf.Bytes()) |
208 | | - if err != nil { |
209 | | - return err |
210 | | - } |
211 | | - |
212 | | - zshTail := ` |
213 | | -BASH_COMPLETION_EOF |
214 | | -} |
215 | | -
|
216 | | -__osdctl_bash_source <(__osdctl_convert_bash_to_zsh) |
217 | | -` |
218 | | - _, err = out.Write([]byte(zshTail)) |
219 | | - if err != nil { |
220 | | - return err |
221 | | - } |
222 | | - return nil |
223 | | -} |
0 commit comments