vendor/github.com/spf13/cobra/zsh_completions.go
changeset 256 6d9efbef00a9
parent 251 1c52a0eeb952
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
     1 package cobra
     1 package cobra
     2 
     2 
     3 import (
     3 import (
     4 	"encoding/json"
     4 	"bytes"
     5 	"fmt"
     5 	"fmt"
     6 	"io"
     6 	"io"
     7 	"os"
     7 	"os"
     8 	"sort"
       
     9 	"strings"
       
    10 	"text/template"
       
    11 
       
    12 	"github.com/spf13/pflag"
       
    13 )
     8 )
    14 
     9 
    15 const (
    10 // GenZshCompletionFile generates zsh completion file including descriptions.
    16 	zshCompArgumentAnnotation   = "cobra_annotations_zsh_completion_argument_annotation"
       
    17 	zshCompArgumentFilenameComp = "cobra_annotations_zsh_completion_argument_file_completion"
       
    18 	zshCompArgumentWordComp     = "cobra_annotations_zsh_completion_argument_word_completion"
       
    19 	zshCompDirname              = "cobra_annotations_zsh_dirname"
       
    20 )
       
    21 
       
    22 var (
       
    23 	zshCompFuncMap = template.FuncMap{
       
    24 		"genZshFuncName":              zshCompGenFuncName,
       
    25 		"extractFlags":                zshCompExtractFlag,
       
    26 		"genFlagEntryForZshArguments": zshCompGenFlagEntryForArguments,
       
    27 		"extractArgsCompletions":      zshCompExtractArgumentCompletionHintsForRendering,
       
    28 	}
       
    29 	zshCompletionText = `
       
    30 {{/* should accept Command (that contains subcommands) as parameter */}}
       
    31 {{define "argumentsC" -}}
       
    32 {{ $cmdPath := genZshFuncName .}}
       
    33 function {{$cmdPath}} {
       
    34   local -a commands
       
    35 
       
    36   _arguments -C \{{- range extractFlags .}}
       
    37     {{genFlagEntryForZshArguments .}} \{{- end}}
       
    38     "1: :->cmnds" \
       
    39     "*::arg:->args"
       
    40 
       
    41   case $state in
       
    42   cmnds)
       
    43     commands=({{range .Commands}}{{if not .Hidden}}
       
    44       "{{.Name}}:{{.Short}}"{{end}}{{end}}
       
    45     )
       
    46     _describe "command" commands
       
    47     ;;
       
    48   esac
       
    49 
       
    50   case "$words[1]" in {{- range .Commands}}{{if not .Hidden}}
       
    51   {{.Name}})
       
    52     {{$cmdPath}}_{{.Name}}
       
    53     ;;{{end}}{{end}}
       
    54   esac
       
    55 }
       
    56 {{range .Commands}}{{if not .Hidden}}
       
    57 {{template "selectCmdTemplate" .}}
       
    58 {{- end}}{{end}}
       
    59 {{- end}}
       
    60 
       
    61 {{/* should accept Command without subcommands as parameter */}}
       
    62 {{define "arguments" -}}
       
    63 function {{genZshFuncName .}} {
       
    64 {{"  _arguments"}}{{range extractFlags .}} \
       
    65     {{genFlagEntryForZshArguments . -}}
       
    66 {{end}}{{range extractArgsCompletions .}} \
       
    67     {{.}}{{end}}
       
    68 }
       
    69 {{end}}
       
    70 
       
    71 {{/* dispatcher for commands with or without subcommands */}}
       
    72 {{define "selectCmdTemplate" -}}
       
    73 {{if .Hidden}}{{/* ignore hidden*/}}{{else -}}
       
    74 {{if .Commands}}{{template "argumentsC" .}}{{else}}{{template "arguments" .}}{{end}}
       
    75 {{- end}}
       
    76 {{- end}}
       
    77 
       
    78 {{/* template entry point */}}
       
    79 {{define "Main" -}}
       
    80 #compdef _{{.Name}} {{.Name}}
       
    81 
       
    82 {{template "selectCmdTemplate" .}}
       
    83 {{end}}
       
    84 `
       
    85 )
       
    86 
       
    87 // zshCompArgsAnnotation is used to encode/decode zsh completion for
       
    88 // arguments to/from Command.Annotations.
       
    89 type zshCompArgsAnnotation map[int]zshCompArgHint
       
    90 
       
    91 type zshCompArgHint struct {
       
    92 	// Indicates the type of the completion to use. One of:
       
    93 	// zshCompArgumentFilenameComp or zshCompArgumentWordComp
       
    94 	Tipe string `json:"type"`
       
    95 
       
    96 	// A value for the type above (globs for file completion or words)
       
    97 	Options []string `json:"options"`
       
    98 }
       
    99 
       
   100 // GenZshCompletionFile generates zsh completion file.
       
   101 func (c *Command) GenZshCompletionFile(filename string) error {
    11 func (c *Command) GenZshCompletionFile(filename string) error {
       
    12 	return c.genZshCompletionFile(filename, true)
       
    13 }
       
    14 
       
    15 // GenZshCompletion generates zsh completion file including descriptions
       
    16 // and writes it to the passed writer.
       
    17 func (c *Command) GenZshCompletion(w io.Writer) error {
       
    18 	return c.genZshCompletion(w, true)
       
    19 }
       
    20 
       
    21 // GenZshCompletionFileNoDesc generates zsh completion file without descriptions.
       
    22 func (c *Command) GenZshCompletionFileNoDesc(filename string) error {
       
    23 	return c.genZshCompletionFile(filename, false)
       
    24 }
       
    25 
       
    26 // GenZshCompletionNoDesc generates zsh completion file without descriptions
       
    27 // and writes it to the passed writer.
       
    28 func (c *Command) GenZshCompletionNoDesc(w io.Writer) error {
       
    29 	return c.genZshCompletion(w, false)
       
    30 }
       
    31 
       
    32 // MarkZshCompPositionalArgumentFile only worked for zsh and its behavior was
       
    33 // not consistent with Bash completion. It has therefore been disabled.
       
    34 // Instead, when no other completion is specified, file completion is done by
       
    35 // default for every argument. One can disable file completion on a per-argument
       
    36 // basis by using ValidArgsFunction and ShellCompDirectiveNoFileComp.
       
    37 // To achieve file extension filtering, one can use ValidArgsFunction and
       
    38 // ShellCompDirectiveFilterFileExt.
       
    39 //
       
    40 // Deprecated
       
    41 func (c *Command) MarkZshCompPositionalArgumentFile(argPosition int, patterns ...string) error {
       
    42 	return nil
       
    43 }
       
    44 
       
    45 // MarkZshCompPositionalArgumentWords only worked for zsh. It has therefore
       
    46 // been disabled.
       
    47 // To achieve the same behavior across all shells, one can use
       
    48 // ValidArgs (for the first argument only) or ValidArgsFunction for
       
    49 // any argument (can include the first one also).
       
    50 //
       
    51 // Deprecated
       
    52 func (c *Command) MarkZshCompPositionalArgumentWords(argPosition int, words ...string) error {
       
    53 	return nil
       
    54 }
       
    55 
       
    56 func (c *Command) genZshCompletionFile(filename string, includeDesc bool) error {
   102 	outFile, err := os.Create(filename)
    57 	outFile, err := os.Create(filename)
   103 	if err != nil {
    58 	if err != nil {
   104 		return err
    59 		return err
   105 	}
    60 	}
   106 	defer outFile.Close()
    61 	defer outFile.Close()
   107 
    62 
   108 	return c.GenZshCompletion(outFile)
    63 	return c.genZshCompletion(outFile, includeDesc)
   109 }
    64 }
   110 
    65 
   111 // GenZshCompletion generates a zsh completion file and writes to the passed
    66 func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
   112 // writer. The completion always run on the root command regardless of the
    67 	buf := new(bytes.Buffer)
   113 // command it was called from.
    68 	genZshComp(buf, c.Name(), includeDesc)
   114 func (c *Command) GenZshCompletion(w io.Writer) error {
    69 	_, err := buf.WriteTo(w)
   115 	tmpl, err := template.New("Main").Funcs(zshCompFuncMap).Parse(zshCompletionText)
    70 	return err
   116 	if err != nil {
    71 }
   117 		return fmt.Errorf("error creating zsh completion template: %v", err)
    72 
       
    73 func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
       
    74 	compCmd := ShellCompRequestCmd
       
    75 	if !includeDesc {
       
    76 		compCmd = ShellCompNoDescRequestCmd
   118 	}
    77 	}
   119 	return tmpl.Execute(w, c.Root())
    78 	WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
   120 }
    79 
   121 
    80 # zsh completion for %-36[1]s -*- shell-script -*-
   122 // MarkZshCompPositionalArgumentFile marks the specified argument (first
    81 
   123 // argument is 1) as completed by file selection. patterns (e.g. "*.txt") are
    82 __%[1]s_debug()
   124 // optional - if not provided the completion will search for all files.
    83 {
   125 func (c *Command) MarkZshCompPositionalArgumentFile(argPosition int, patterns ...string) error {
    84     local file="$BASH_COMP_DEBUG_FILE"
   126 	if argPosition < 1 {
    85     if [[ -n ${file} ]]; then
   127 		return fmt.Errorf("Invalid argument position (%d)", argPosition)
    86         echo "$*" >> "${file}"
   128 	}
    87     fi
   129 	annotation, err := c.zshCompGetArgsAnnotations()
    88 }
   130 	if err != nil {
    89 
   131 		return err
    90 _%[1]s()
   132 	}
    91 {
   133 	if c.zshcompArgsAnnotationnIsDuplicatePosition(annotation, argPosition) {
    92     local shellCompDirectiveError=%[3]d
   134 		return fmt.Errorf("Duplicate annotation for positional argument at index %d", argPosition)
    93     local shellCompDirectiveNoSpace=%[4]d
   135 	}
    94     local shellCompDirectiveNoFileComp=%[5]d
   136 	annotation[argPosition] = zshCompArgHint{
    95     local shellCompDirectiveFilterFileExt=%[6]d
   137 		Tipe:    zshCompArgumentFilenameComp,
    96     local shellCompDirectiveFilterDirs=%[7]d
   138 		Options: patterns,
    97 
   139 	}
    98     local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
   140 	return c.zshCompSetArgsAnnotations(annotation)
    99     local -a completions
   141 }
   100 
   142 
   101     __%[1]s_debug "\n========= starting completion logic =========="
   143 // MarkZshCompPositionalArgumentWords marks the specified positional argument
   102     __%[1]s_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
   144 // (first argument is 1) as completed by the provided words. At east one word
   103 
   145 // must be provided, spaces within words will be offered completion with
   104     # The user could have moved the cursor backwards on the command-line.
   146 // "word\ word".
   105     # We need to trigger completion from the $CURRENT location, so we need
   147 func (c *Command) MarkZshCompPositionalArgumentWords(argPosition int, words ...string) error {
   106     # to truncate the command-line ($words) up to the $CURRENT location.
   148 	if argPosition < 1 {
   107     # (We cannot use $CURSOR as its value does not work when a command is an alias.)
   149 		return fmt.Errorf("Invalid argument position (%d)", argPosition)
   108     words=("${=words[1,CURRENT]}")
   150 	}
   109     __%[1]s_debug "Truncated words[*]: ${words[*]},"
   151 	if len(words) == 0 {
   110 
   152 		return fmt.Errorf("Trying to set empty word list for positional argument %d", argPosition)
   111     lastParam=${words[-1]}
   153 	}
   112     lastChar=${lastParam[-1]}
   154 	annotation, err := c.zshCompGetArgsAnnotations()
   113     __%[1]s_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
   155 	if err != nil {
   114 
   156 		return err
   115     # For zsh, when completing a flag with an = (e.g., %[1]s -n=<TAB>)
   157 	}
   116     # completions must be prefixed with the flag
   158 	if c.zshcompArgsAnnotationnIsDuplicatePosition(annotation, argPosition) {
   117     setopt local_options BASH_REMATCH
   159 		return fmt.Errorf("Duplicate annotation for positional argument at index %d", argPosition)
   118     if [[ "${lastParam}" =~ '-.*=' ]]; then
   160 	}
   119         # We are dealing with a flag with an =
   161 	annotation[argPosition] = zshCompArgHint{
   120         flagPrefix="-P ${BASH_REMATCH}"
   162 		Tipe:    zshCompArgumentWordComp,
   121     fi
   163 		Options: words,
   122 
   164 	}
   123     # Prepare the command to obtain completions
   165 	return c.zshCompSetArgsAnnotations(annotation)
   124     requestComp="${words[1]} %[2]s ${words[2,-1]}"
   166 }
   125     if [ "${lastChar}" = "" ]; then
   167 
   126         # If the last parameter is complete (there is a space following it)
   168 func zshCompExtractArgumentCompletionHintsForRendering(c *Command) ([]string, error) {
   127         # We add an extra empty parameter so we can indicate this to the go completion code.
   169 	var result []string
   128         __%[1]s_debug "Adding extra empty parameter"
   170 	annotation, err := c.zshCompGetArgsAnnotations()
   129         requestComp="${requestComp} \"\""
   171 	if err != nil {
   130     fi
   172 		return nil, err
   131 
   173 	}
   132     __%[1]s_debug "About to call: eval ${requestComp}"
   174 	for k, v := range annotation {
   133 
   175 		s, err := zshCompRenderZshCompArgHint(k, v)
   134     # Use eval to handle any environment variables and such
   176 		if err != nil {
   135     out=$(eval ${requestComp} 2>/dev/null)
   177 			return nil, err
   136     __%[1]s_debug "completion output: ${out}"
   178 		}
   137 
   179 		result = append(result, s)
   138     # Extract the directive integer following a : from the last line
   180 	}
   139     local lastLine
   181 	if len(c.ValidArgs) > 0 {
   140     while IFS='\n' read -r line; do
   182 		if _, positionOneExists := annotation[1]; !positionOneExists {
   141         lastLine=${line}
   183 			s, err := zshCompRenderZshCompArgHint(1, zshCompArgHint{
   142     done < <(printf "%%s\n" "${out[@]}")
   184 				Tipe:    zshCompArgumentWordComp,
   143     __%[1]s_debug "last line: ${lastLine}"
   185 				Options: c.ValidArgs,
   144 
   186 			})
   145     if [ "${lastLine[1]}" = : ]; then
   187 			if err != nil {
   146         directive=${lastLine[2,-1]}
   188 				return nil, err
   147         # Remove the directive including the : and the newline
   189 			}
   148         local suffix
   190 			result = append(result, s)
   149         (( suffix=${#lastLine}+2))
   191 		}
   150         out=${out[1,-$suffix]}
   192 	}
   151     else
   193 	sort.Strings(result)
   152         # There is no directive specified.  Leave $out as is.
   194 	return result, nil
   153         __%[1]s_debug "No directive found.  Setting do default"
   195 }
   154         directive=0
   196 
   155     fi
   197 func zshCompRenderZshCompArgHint(i int, z zshCompArgHint) (string, error) {
   156 
   198 	switch t := z.Tipe; t {
   157     __%[1]s_debug "directive: ${directive}"
   199 	case zshCompArgumentFilenameComp:
   158     __%[1]s_debug "completions: ${out}"
   200 		var globs []string
   159     __%[1]s_debug "flagPrefix: ${flagPrefix}"
   201 		for _, g := range z.Options {
   160 
   202 			globs = append(globs, fmt.Sprintf(`-g "%s"`, g))
   161     if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
   203 		}
   162         __%[1]s_debug "Completion received error. Ignoring completions."
   204 		return fmt.Sprintf(`'%d: :_files %s'`, i, strings.Join(globs, " ")), nil
   163         return
   205 	case zshCompArgumentWordComp:
   164     fi
   206 		var words []string
   165 
   207 		for _, w := range z.Options {
   166     while IFS='\n' read -r comp; do
   208 			words = append(words, fmt.Sprintf("%q", w))
   167         if [ -n "$comp" ]; then
   209 		}
   168             # If requested, completions are returned with a description.
   210 		return fmt.Sprintf(`'%d: :(%s)'`, i, strings.Join(words, " ")), nil
   169             # The description is preceded by a TAB character.
   211 	default:
   170             # For zsh's _describe, we need to use a : instead of a TAB.
   212 		return "", fmt.Errorf("Invalid zsh argument completion annotation: %s", t)
   171             # We first need to escape any : as part of the completion itself.
   213 	}
   172             comp=${comp//:/\\:}
   214 }
   173 
   215 
   174             local tab=$(printf '\t')
   216 func (c *Command) zshcompArgsAnnotationnIsDuplicatePosition(annotation zshCompArgsAnnotation, position int) bool {
   175             comp=${comp//$tab/:}
   217 	_, dup := annotation[position]
   176 
   218 	return dup
   177             __%[1]s_debug "Adding completion: ${comp}"
   219 }
   178             completions+=${comp}
   220 
   179             lastComp=$comp
   221 func (c *Command) zshCompGetArgsAnnotations() (zshCompArgsAnnotation, error) {
   180         fi
   222 	annotation := make(zshCompArgsAnnotation)
   181     done < <(printf "%%s\n" "${out[@]}")
   223 	annotationString, ok := c.Annotations[zshCompArgumentAnnotation]
   182 
   224 	if !ok {
   183     if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
   225 		return annotation, nil
   184         __%[1]s_debug "Activating nospace."
   226 	}
   185         noSpace="-S ''"
   227 	err := json.Unmarshal([]byte(annotationString), &annotation)
   186     fi
   228 	if err != nil {
   187 
   229 		return annotation, fmt.Errorf("Error unmarshaling zsh argument annotation: %v", err)
   188     if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
   230 	}
   189         # File extension filtering
   231 	return annotation, nil
   190         local filteringCmd
   232 }
   191         filteringCmd='_files'
   233 
   192         for filter in ${completions[@]}; do
   234 func (c *Command) zshCompSetArgsAnnotations(annotation zshCompArgsAnnotation) error {
   193             if [ ${filter[1]} != '*' ]; then
   235 	jsn, err := json.Marshal(annotation)
   194                 # zsh requires a glob pattern to do file filtering
   236 	if err != nil {
   195                 filter="\*.$filter"
   237 		return fmt.Errorf("Error marshaling zsh argument annotation: %v", err)
   196             fi
   238 	}
   197             filteringCmd+=" -g $filter"
   239 	if c.Annotations == nil {
   198         done
   240 		c.Annotations = make(map[string]string)
   199         filteringCmd+=" ${flagPrefix}"
   241 	}
   200 
   242 	c.Annotations[zshCompArgumentAnnotation] = string(jsn)
   201         __%[1]s_debug "File filtering command: $filteringCmd"
   243 	return nil
   202         _arguments '*:filename:'"$filteringCmd"
   244 }
   203     elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
   245 
   204         # File completion for directories only
   246 func zshCompGenFuncName(c *Command) string {
   205         local subDir
   247 	if c.HasParent() {
   206         subdir="${completions[1]}"
   248 		return zshCompGenFuncName(c.Parent()) + "_" + c.Name()
   207         if [ -n "$subdir" ]; then
   249 	}
   208             __%[1]s_debug "Listing directories in $subdir"
   250 	return "_" + c.Name()
   209             pushd "${subdir}" >/dev/null 2>&1
   251 }
   210         else
   252 
   211             __%[1]s_debug "Listing directories in ."
   253 func zshCompExtractFlag(c *Command) []*pflag.Flag {
   212         fi
   254 	var flags []*pflag.Flag
   213 
   255 	c.LocalFlags().VisitAll(func(f *pflag.Flag) {
   214         local result
   256 		if !f.Hidden {
   215         _arguments '*:dirname:_files -/'" ${flagPrefix}"
   257 			flags = append(flags, f)
   216         result=$?
   258 		}
   217         if [ -n "$subdir" ]; then
   259 	})
   218             popd >/dev/null 2>&1
   260 	c.InheritedFlags().VisitAll(func(f *pflag.Flag) {
   219         fi
   261 		if !f.Hidden {
   220         return $result
   262 			flags = append(flags, f)
   221     else
   263 		}
   222         __%[1]s_debug "Calling _describe"
   264 	})
   223         if eval _describe "completions" completions $flagPrefix $noSpace; then
   265 	return flags
   224             __%[1]s_debug "_describe found some completions"
   266 }
   225 
   267 
   226             # Return the success of having called _describe
   268 // zshCompGenFlagEntryForArguments returns an entry that matches _arguments
   227             return 0
   269 // zsh-completion parameters. It's too complicated to generate in a template.
   228         else
   270 func zshCompGenFlagEntryForArguments(f *pflag.Flag) string {
   229             __%[1]s_debug "_describe did not find completions."
   271 	if f.Name == "" || f.Shorthand == "" {
   230             __%[1]s_debug "Checking if we should do file completion."
   272 		return zshCompGenFlagEntryForSingleOptionFlag(f)
   231             if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
   273 	}
   232                 __%[1]s_debug "deactivating file completion"
   274 	return zshCompGenFlagEntryForMultiOptionFlag(f)
   233 
   275 }
   234                 # We must return an error code here to let zsh know that there were no
   276 
   235                 # completions found by _describe; this is what will trigger other
   277 func zshCompGenFlagEntryForSingleOptionFlag(f *pflag.Flag) string {
   236                 # matching algorithms to attempt to find completions.
   278 	var option, multiMark, extras string
   237                 # For example zsh can match letters in the middle of words.
   279 
   238                 return 1
   280 	if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) {
   239             else
   281 		multiMark = "*"
   240                 # Perform file completion
   282 	}
   241                 __%[1]s_debug "Activating file completion"
   283 
   242 
   284 	option = "--" + f.Name
   243                 # We must return the result of this command, so it must be the
   285 	if option == "--" {
   244                 # last command, or else we must store its result to return it.
   286 		option = "-" + f.Shorthand
   245                 _arguments '*:filename:_files'" ${flagPrefix}"
   287 	}
   246             fi
   288 	extras = zshCompGenFlagEntryExtras(f)
   247         fi
   289 
   248     fi
   290 	return fmt.Sprintf(`'%s%s[%s]%s'`, multiMark, option, zshCompQuoteFlagDescription(f.Usage), extras)
   249 }
   291 }
   250 
   292 
   251 # don't run the completion function when being source-ed or eval-ed
   293 func zshCompGenFlagEntryForMultiOptionFlag(f *pflag.Flag) string {
   252 if [ "$funcstack[1]" = "_%[1]s" ]; then
   294 	var options, parenMultiMark, curlyMultiMark, extras string
   253 	_%[1]s
   295 
   254 fi
   296 	if zshCompFlagCouldBeSpecifiedMoreThenOnce(f) {
   255 `, name, compCmd,
   297 		parenMultiMark = "*"
   256 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
   298 		curlyMultiMark = "\\*"
   257 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
   299 	}
   258 }
   300 
       
   301 	options = fmt.Sprintf(`'(%s-%s %s--%s)'{%s-%s,%s--%s}`,
       
   302 		parenMultiMark, f.Shorthand, parenMultiMark, f.Name, curlyMultiMark, f.Shorthand, curlyMultiMark, f.Name)
       
   303 	extras = zshCompGenFlagEntryExtras(f)
       
   304 
       
   305 	return fmt.Sprintf(`%s'[%s]%s'`, options, zshCompQuoteFlagDescription(f.Usage), extras)
       
   306 }
       
   307 
       
   308 func zshCompGenFlagEntryExtras(f *pflag.Flag) string {
       
   309 	if f.NoOptDefVal != "" {
       
   310 		return ""
       
   311 	}
       
   312 
       
   313 	extras := ":" // allow options for flag (even without assistance)
       
   314 	for key, values := range f.Annotations {
       
   315 		switch key {
       
   316 		case zshCompDirname:
       
   317 			extras = fmt.Sprintf(":filename:_files -g %q", values[0])
       
   318 		case BashCompFilenameExt:
       
   319 			extras = ":filename:_files"
       
   320 			for _, pattern := range values {
       
   321 				extras = extras + fmt.Sprintf(` -g "%s"`, pattern)
       
   322 			}
       
   323 		}
       
   324 	}
       
   325 
       
   326 	return extras
       
   327 }
       
   328 
       
   329 func zshCompFlagCouldBeSpecifiedMoreThenOnce(f *pflag.Flag) bool {
       
   330 	return strings.Contains(f.Value.Type(), "Slice") ||
       
   331 		strings.Contains(f.Value.Type(), "Array")
       
   332 }
       
   333 
       
   334 func zshCompQuoteFlagDescription(s string) string {
       
   335 	return strings.Replace(s, "'", `'\''`, -1)
       
   336 }