OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / spf13 / cobra / bash_completions.go
1 package cobra
2
3 import (
4         "bytes"
5         "fmt"
6         "io"
7         "os"
8         "sort"
9         "strings"
10
11         "github.com/spf13/pflag"
12 )
13
14 // Annotations for Bash completion.
15 const (
16         BashCompFilenameExt     = "cobra_annotation_bash_completion_filename_extensions"
17         BashCompCustom          = "cobra_annotation_bash_completion_custom"
18         BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
19         BashCompSubdirsInDir    = "cobra_annotation_bash_completion_subdirs_in_dir"
20 )
21
22 func writePreamble(buf *bytes.Buffer, name string) {
23         buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
24         buf.WriteString(`
25 __debug()
26 {
27     if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
28         echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
29     fi
30 }
31
32 # Homebrew on Macs have version 1.3 of bash-completion which doesn't include
33 # _init_completion. This is a very minimal version of that function.
34 __my_init_completion()
35 {
36     COMPREPLY=()
37     _get_comp_words_by_ref "$@" cur prev words cword
38 }
39
40 __index_of_word()
41 {
42     local w word=$1
43     shift
44     index=0
45     for w in "$@"; do
46         [[ $w = "$word" ]] && return
47         index=$((index+1))
48     done
49     index=-1
50 }
51
52 __contains_word()
53 {
54     local w word=$1; shift
55     for w in "$@"; do
56         [[ $w = "$word" ]] && return
57     done
58     return 1
59 }
60
61 __handle_reply()
62 {
63     __debug "${FUNCNAME[0]}"
64     case $cur in
65         -*)
66             if [[ $(type -t compopt) = "builtin" ]]; then
67                 compopt -o nospace
68             fi
69             local allflags
70             if [ ${#must_have_one_flag[@]} -ne 0 ]; then
71                 allflags=("${must_have_one_flag[@]}")
72             else
73                 allflags=("${flags[*]} ${two_word_flags[*]}")
74             fi
75             COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") )
76             if [[ $(type -t compopt) = "builtin" ]]; then
77                 [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace
78             fi
79
80             # complete after --flag=abc
81             if [[ $cur == *=* ]]; then
82                 if [[ $(type -t compopt) = "builtin" ]]; then
83                     compopt +o nospace
84                 fi
85
86                 local index flag
87                 flag="${cur%%=*}"
88                 __index_of_word "${flag}" "${flags_with_completion[@]}"
89                 COMPREPLY=()
90                 if [[ ${index} -ge 0 ]]; then
91                     PREFIX=""
92                     cur="${cur#*=}"
93                     ${flags_completion[${index}]}
94                     if [ -n "${ZSH_VERSION}" ]; then
95                         # zsh completion needs --flag= prefix
96                         eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )"
97                     fi
98                 fi
99             fi
100             return 0;
101             ;;
102     esac
103
104     # check if we are handling a flag with special work handling
105     local index
106     __index_of_word "${prev}" "${flags_with_completion[@]}"
107     if [[ ${index} -ge 0 ]]; then
108         ${flags_completion[${index}]}
109         return
110     fi
111
112     # we are parsing a flag and don't have a special handler, no completion
113     if [[ ${cur} != "${words[cword]}" ]]; then
114         return
115     fi
116
117     local completions
118     completions=("${commands[@]}")
119     if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
120         completions=("${must_have_one_noun[@]}")
121     fi
122     if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
123         completions+=("${must_have_one_flag[@]}")
124     fi
125     COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
126
127     if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then
128         COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") )
129     fi
130
131     if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
132         declare -F __custom_func >/dev/null && __custom_func
133     fi
134
135     # available in bash-completion >= 2, not always present on macOS
136     if declare -F __ltrim_colon_completions >/dev/null; then
137         __ltrim_colon_completions "$cur"
138     fi
139 }
140
141 # The arguments should be in the form "ext1|ext2|extn"
142 __handle_filename_extension_flag()
143 {
144     local ext="$1"
145     _filedir "@(${ext})"
146 }
147
148 __handle_subdirs_in_dir_flag()
149 {
150     local dir="$1"
151     pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
152 }
153
154 __handle_flag()
155 {
156     __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
157
158     # if a command required a flag, and we found it, unset must_have_one_flag()
159     local flagname=${words[c]}
160     local flagvalue
161     # if the word contained an =
162     if [[ ${words[c]} == *"="* ]]; then
163         flagvalue=${flagname#*=} # take in as flagvalue after the =
164         flagname=${flagname%%=*} # strip everything after the =
165         flagname="${flagname}=" # but put the = back
166     fi
167     __debug "${FUNCNAME[0]}: looking for ${flagname}"
168     if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
169         must_have_one_flag=()
170     fi
171
172     # if you set a flag which only applies to this command, don't show subcommands
173     if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
174       commands=()
175     fi
176
177     # keep flag value with flagname as flaghash
178     if [ -n "${flagvalue}" ] ; then
179         flaghash[${flagname}]=${flagvalue}
180     elif [ -n "${words[ $((c+1)) ]}" ] ; then
181         flaghash[${flagname}]=${words[ $((c+1)) ]}
182     else
183         flaghash[${flagname}]="true" # pad "true" for bool flag
184     fi
185
186     # skip the argument to a two word flag
187     if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
188         c=$((c+1))
189         # if we are looking for a flags value, don't show commands
190         if [[ $c -eq $cword ]]; then
191             commands=()
192         fi
193     fi
194
195     c=$((c+1))
196
197 }
198
199 __handle_noun()
200 {
201     __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
202
203     if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
204         must_have_one_noun=()
205     elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then
206         must_have_one_noun=()
207     fi
208
209     nouns+=("${words[c]}")
210     c=$((c+1))
211 }
212
213 __handle_command()
214 {
215     __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
216
217     local next_command
218     if [[ -n ${last_command} ]]; then
219         next_command="_${last_command}_${words[c]//:/__}"
220     else
221         if [[ $c -eq 0 ]]; then
222             next_command="_$(basename "${words[c]//:/__}")"
223         else
224             next_command="_${words[c]//:/__}"
225         fi
226     fi
227     c=$((c+1))
228     __debug "${FUNCNAME[0]}: looking for ${next_command}"
229     declare -F "$next_command" >/dev/null && $next_command
230 }
231
232 __handle_word()
233 {
234     if [[ $c -ge $cword ]]; then
235         __handle_reply
236         return
237     fi
238     __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
239     if [[ "${words[c]}" == -* ]]; then
240         __handle_flag
241     elif __contains_word "${words[c]}" "${commands[@]}"; then
242         __handle_command
243     elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
244         __handle_command
245     else
246         __handle_noun
247     fi
248     __handle_word
249 }
250
251 `)
252 }
253
254 func writePostscript(buf *bytes.Buffer, name string) {
255         name = strings.Replace(name, ":", "__", -1)
256         buf.WriteString(fmt.Sprintf("__start_%s()\n", name))
257         buf.WriteString(fmt.Sprintf(`{
258     local cur prev words cword
259     declare -A flaghash 2>/dev/null || :
260     if declare -F _init_completion >/dev/null 2>&1; then
261         _init_completion -s || return
262     else
263         __my_init_completion -n "=" || return
264     fi
265
266     local c=0
267     local flags=()
268     local two_word_flags=()
269     local local_nonpersistent_flags=()
270     local flags_with_completion=()
271     local flags_completion=()
272     local commands=("%s")
273     local must_have_one_flag=()
274     local must_have_one_noun=()
275     local last_command
276     local nouns=()
277
278     __handle_word
279 }
280
281 `, name))
282         buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
283     complete -o default -F __start_%s %s
284 else
285     complete -o default -o nospace -F __start_%s %s
286 fi
287
288 `, name, name, name, name))
289         buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n")
290 }
291
292 func writeCommands(buf *bytes.Buffer, cmd *Command) {
293         buf.WriteString("    commands=()\n")
294         for _, c := range cmd.Commands() {
295                 if !c.IsAvailableCommand() || c == cmd.helpCommand {
296                         continue
297                 }
298                 buf.WriteString(fmt.Sprintf("    commands+=(%q)\n", c.Name()))
299         }
300         buf.WriteString("\n")
301 }
302
303 func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string) {
304         for key, value := range annotations {
305                 switch key {
306                 case BashCompFilenameExt:
307                         buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
308
309                         var ext string
310                         if len(value) > 0 {
311                                 ext = "__handle_filename_extension_flag " + strings.Join(value, "|")
312                         } else {
313                                 ext = "_filedir"
314                         }
315                         buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
316                 case BashCompCustom:
317                         buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
318                         if len(value) > 0 {
319                                 handlers := strings.Join(value, "; ")
320                                 buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", handlers))
321                         } else {
322                                 buf.WriteString("    flags_completion+=(:)\n")
323                         }
324                 case BashCompSubdirsInDir:
325                         buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
326
327                         var ext string
328                         if len(value) == 1 {
329                                 ext = "__handle_subdirs_in_dir_flag " + value[0]
330                         } else {
331                                 ext = "_filedir -d"
332                         }
333                         buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
334                 }
335         }
336 }
337
338 func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
339         name := flag.Shorthand
340         format := "    "
341         if len(flag.NoOptDefVal) == 0 {
342                 format += "two_word_"
343         }
344         format += "flags+=(\"-%s\")\n"
345         buf.WriteString(fmt.Sprintf(format, name))
346         writeFlagHandler(buf, "-"+name, flag.Annotations)
347 }
348
349 func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
350         name := flag.Name
351         format := "    flags+=(\"--%s"
352         if len(flag.NoOptDefVal) == 0 {
353                 format += "="
354         }
355         format += "\")\n"
356         buf.WriteString(fmt.Sprintf(format, name))
357         writeFlagHandler(buf, "--"+name, flag.Annotations)
358 }
359
360 func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
361         name := flag.Name
362         format := "    local_nonpersistent_flags+=(\"--%s"
363         if len(flag.NoOptDefVal) == 0 {
364                 format += "="
365         }
366         format += "\")\n"
367         buf.WriteString(fmt.Sprintf(format, name))
368 }
369
370 func writeFlags(buf *bytes.Buffer, cmd *Command) {
371         buf.WriteString(`    flags=()
372     two_word_flags=()
373     local_nonpersistent_flags=()
374     flags_with_completion=()
375     flags_completion=()
376
377 `)
378         localNonPersistentFlags := cmd.LocalNonPersistentFlags()
379         cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
380                 if nonCompletableFlag(flag) {
381                         return
382                 }
383                 writeFlag(buf, flag)
384                 if len(flag.Shorthand) > 0 {
385                         writeShortFlag(buf, flag)
386                 }
387                 if localNonPersistentFlags.Lookup(flag.Name) != nil {
388                         writeLocalNonPersistentFlag(buf, flag)
389                 }
390         })
391         cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
392                 if nonCompletableFlag(flag) {
393                         return
394                 }
395                 writeFlag(buf, flag)
396                 if len(flag.Shorthand) > 0 {
397                         writeShortFlag(buf, flag)
398                 }
399         })
400
401         buf.WriteString("\n")
402 }
403
404 func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
405         buf.WriteString("    must_have_one_flag=()\n")
406         flags := cmd.NonInheritedFlags()
407         flags.VisitAll(func(flag *pflag.Flag) {
408                 if nonCompletableFlag(flag) {
409                         return
410                 }
411                 for key := range flag.Annotations {
412                         switch key {
413                         case BashCompOneRequiredFlag:
414                                 format := "    must_have_one_flag+=(\"--%s"
415                                 if flag.Value.Type() != "bool" {
416                                         format += "="
417                                 }
418                                 format += "\")\n"
419                                 buf.WriteString(fmt.Sprintf(format, flag.Name))
420
421                                 if len(flag.Shorthand) > 0 {
422                                         buf.WriteString(fmt.Sprintf("    must_have_one_flag+=(\"-%s\")\n", flag.Shorthand))
423                                 }
424                         }
425                 }
426         })
427 }
428
429 func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
430         buf.WriteString("    must_have_one_noun=()\n")
431         sort.Sort(sort.StringSlice(cmd.ValidArgs))
432         for _, value := range cmd.ValidArgs {
433                 buf.WriteString(fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
434         }
435 }
436
437 func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
438         buf.WriteString("    noun_aliases=()\n")
439         sort.Sort(sort.StringSlice(cmd.ArgAliases))
440         for _, value := range cmd.ArgAliases {
441                 buf.WriteString(fmt.Sprintf("    noun_aliases+=(%q)\n", value))
442         }
443 }
444
445 func gen(buf *bytes.Buffer, cmd *Command) {
446         for _, c := range cmd.Commands() {
447                 if !c.IsAvailableCommand() || c == cmd.helpCommand {
448                         continue
449                 }
450                 gen(buf, c)
451         }
452         commandName := cmd.CommandPath()
453         commandName = strings.Replace(commandName, " ", "_", -1)
454         commandName = strings.Replace(commandName, ":", "__", -1)
455         buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
456         buf.WriteString(fmt.Sprintf("    last_command=%q\n", commandName))
457         writeCommands(buf, cmd)
458         writeFlags(buf, cmd)
459         writeRequiredFlag(buf, cmd)
460         writeRequiredNouns(buf, cmd)
461         writeArgAliases(buf, cmd)
462         buf.WriteString("}\n\n")
463 }
464
465 // GenBashCompletion generates bash completion file and writes to the passed writer.
466 func (c *Command) GenBashCompletion(w io.Writer) error {
467         buf := new(bytes.Buffer)
468         writePreamble(buf, c.Name())
469         if len(c.BashCompletionFunction) > 0 {
470                 buf.WriteString(c.BashCompletionFunction + "\n")
471         }
472         gen(buf, c)
473         writePostscript(buf, c.Name())
474
475         _, err := buf.WriteTo(w)
476         return err
477 }
478
479 func nonCompletableFlag(flag *pflag.Flag) bool {
480         return flag.Hidden || len(flag.Deprecated) > 0
481 }
482
483 // GenBashCompletionFile generates bash completion file.
484 func (c *Command) GenBashCompletionFile(filename string) error {
485         outFile, err := os.Create(filename)
486         if err != nil {
487                 return err
488         }
489         defer outFile.Close()
490
491         return c.GenBashCompletion(outFile)
492 }
493
494 // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag, if it exists.
495 func (c *Command) MarkFlagRequired(name string) error {
496         return MarkFlagRequired(c.Flags(), name)
497 }
498
499 // MarkPersistentFlagRequired adds the BashCompOneRequiredFlag annotation to the named persistent flag, if it exists.
500 func (c *Command) MarkPersistentFlagRequired(name string) error {
501         return MarkFlagRequired(c.PersistentFlags(), name)
502 }
503
504 // MarkFlagRequired adds the BashCompOneRequiredFlag annotation to the named flag in the flag set, if it exists.
505 func MarkFlagRequired(flags *pflag.FlagSet, name string) error {
506         return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"})
507 }
508
509 // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag, if it exists.
510 // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
511 func (c *Command) MarkFlagFilename(name string, extensions ...string) error {
512         return MarkFlagFilename(c.Flags(), name, extensions...)
513 }
514
515 // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists.
516 // Generated bash autocompletion will call the bash function f for the flag.
517 func (c *Command) MarkFlagCustom(name string, f string) error {
518         return MarkFlagCustom(c.Flags(), name, f)
519 }
520
521 // MarkPersistentFlagFilename adds the BashCompFilenameExt annotation to the named persistent flag, if it exists.
522 // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
523 func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error {
524         return MarkFlagFilename(c.PersistentFlags(), name, extensions...)
525 }
526
527 // MarkFlagFilename adds the BashCompFilenameExt annotation to the named flag in the flag set, if it exists.
528 // Generated bash autocompletion will select filenames for the flag, limiting to named extensions if provided.
529 func MarkFlagFilename(flags *pflag.FlagSet, name string, extensions ...string) error {
530         return flags.SetAnnotation(name, BashCompFilenameExt, extensions)
531 }
532
533 // MarkFlagCustom adds the BashCompCustom annotation to the named flag in the flag set, if it exists.
534 // Generated bash autocompletion will call the bash function f for the flag.
535 func MarkFlagCustom(flags *pflag.FlagSet, name string, f string) error {
536         return flags.SetAnnotation(name, BashCompCustom, []string{f})
537 }