vendor/github.com/spf13/cobra/bash_completions.go
changeset 256 6d9efbef00a9
parent 251 1c52a0eeb952
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
    17 	BashCompCustom          = "cobra_annotation_bash_completion_custom"
    17 	BashCompCustom          = "cobra_annotation_bash_completion_custom"
    18 	BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
    18 	BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
    19 	BashCompSubdirsInDir    = "cobra_annotation_bash_completion_subdirs_in_dir"
    19 	BashCompSubdirsInDir    = "cobra_annotation_bash_completion_subdirs_in_dir"
    20 )
    20 )
    21 
    21 
    22 func writePreamble(buf *bytes.Buffer, name string) {
    22 func writePreamble(buf io.StringWriter, name string) {
    23 	buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
    23 	WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
    24 	buf.WriteString(fmt.Sprintf(`
    24 	WriteStringAndCheck(buf, fmt.Sprintf(`
    25 __%[1]s_debug()
    25 __%[1]s_debug()
    26 {
    26 {
    27     if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
    27     if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
    28         echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
    28         echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
    29     fi
    29     fi
    56         [[ $w = "$word" ]] && return
    56         [[ $w = "$word" ]] && return
    57     done
    57     done
    58     return 1
    58     return 1
    59 }
    59 }
    60 
    60 
       
    61 __%[1]s_handle_go_custom_completion()
       
    62 {
       
    63     __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}"
       
    64 
       
    65     local shellCompDirectiveError=%[3]d
       
    66     local shellCompDirectiveNoSpace=%[4]d
       
    67     local shellCompDirectiveNoFileComp=%[5]d
       
    68     local shellCompDirectiveFilterFileExt=%[6]d
       
    69     local shellCompDirectiveFilterDirs=%[7]d
       
    70 
       
    71     local out requestComp lastParam lastChar comp directive args
       
    72 
       
    73     # Prepare the command to request completions for the program.
       
    74     # Calling ${words[0]} instead of directly %[1]s allows to handle aliases
       
    75     args=("${words[@]:1}")
       
    76     requestComp="${words[0]} %[2]s ${args[*]}"
       
    77 
       
    78     lastParam=${words[$((${#words[@]}-1))]}
       
    79     lastChar=${lastParam:$((${#lastParam}-1)):1}
       
    80     __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}"
       
    81 
       
    82     if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then
       
    83         # If the last parameter is complete (there is a space following it)
       
    84         # We add an extra empty parameter so we can indicate this to the go method.
       
    85         __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter"
       
    86         requestComp="${requestComp} \"\""
       
    87     fi
       
    88 
       
    89     __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}"
       
    90     # Use eval to handle any environment variables and such
       
    91     out=$(eval "${requestComp}" 2>/dev/null)
       
    92 
       
    93     # Extract the directive integer at the very end of the output following a colon (:)
       
    94     directive=${out##*:}
       
    95     # Remove the directive
       
    96     out=${out%%:*}
       
    97     if [ "${directive}" = "${out}" ]; then
       
    98         # There is not directive specified
       
    99         directive=0
       
   100     fi
       
   101     __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
       
   102     __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
       
   103 
       
   104     if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
       
   105         # Error code.  No completion.
       
   106         __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code"
       
   107         return
       
   108     else
       
   109         if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
       
   110             if [[ $(type -t compopt) = "builtin" ]]; then
       
   111                 __%[1]s_debug "${FUNCNAME[0]}: activating no space"
       
   112                 compopt -o nospace
       
   113             fi
       
   114         fi
       
   115         if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
       
   116             if [[ $(type -t compopt) = "builtin" ]]; then
       
   117                 __%[1]s_debug "${FUNCNAME[0]}: activating no file completion"
       
   118                 compopt +o default
       
   119             fi
       
   120         fi
       
   121     fi
       
   122 
       
   123     if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
       
   124         # File extension filtering
       
   125         local fullFilter filter filteringCmd
       
   126         # Do not use quotes around the $out variable or else newline
       
   127         # characters will be kept.
       
   128         for filter in ${out[*]}; do
       
   129             fullFilter+="$filter|"
       
   130         done
       
   131 
       
   132         filteringCmd="_filedir $fullFilter"
       
   133         __%[1]s_debug "File filtering command: $filteringCmd"
       
   134         $filteringCmd
       
   135     elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
       
   136         # File completion for directories only
       
   137         local subDir
       
   138         # Use printf to strip any trailing newline
       
   139         subdir=$(printf "%%s" "${out[0]}")
       
   140         if [ -n "$subdir" ]; then
       
   141             __%[1]s_debug "Listing directories in $subdir"
       
   142             __%[1]s_handle_subdirs_in_dir_flag "$subdir"
       
   143         else
       
   144             __%[1]s_debug "Listing directories in ."
       
   145             _filedir -d
       
   146         fi
       
   147     else
       
   148         while IFS='' read -r comp; do
       
   149             COMPREPLY+=("$comp")
       
   150         done < <(compgen -W "${out[*]}" -- "$cur")
       
   151     fi
       
   152 }
       
   153 
    61 __%[1]s_handle_reply()
   154 __%[1]s_handle_reply()
    62 {
   155 {
    63     __%[1]s_debug "${FUNCNAME[0]}"
   156     __%[1]s_debug "${FUNCNAME[0]}"
       
   157     local comp
    64     case $cur in
   158     case $cur in
    65         -*)
   159         -*)
    66             if [[ $(type -t compopt) = "builtin" ]]; then
   160             if [[ $(type -t compopt) = "builtin" ]]; then
    67                 compopt -o nospace
   161                 compopt -o nospace
    68             fi
   162             fi
    70             if [ ${#must_have_one_flag[@]} -ne 0 ]; then
   164             if [ ${#must_have_one_flag[@]} -ne 0 ]; then
    71                 allflags=("${must_have_one_flag[@]}")
   165                 allflags=("${must_have_one_flag[@]}")
    72             else
   166             else
    73                 allflags=("${flags[*]} ${two_word_flags[*]}")
   167                 allflags=("${flags[*]} ${two_word_flags[*]}")
    74             fi
   168             fi
    75             COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") )
   169             while IFS='' read -r comp; do
       
   170                 COMPREPLY+=("$comp")
       
   171             done < <(compgen -W "${allflags[*]}" -- "$cur")
    76             if [[ $(type -t compopt) = "builtin" ]]; then
   172             if [[ $(type -t compopt) = "builtin" ]]; then
    77                 [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace
   173                 [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace
    78             fi
   174             fi
    79 
   175 
    80             # complete after --flag=abc
   176             # complete after --flag=abc
   115     fi
   211     fi
   116 
   212 
   117     local completions
   213     local completions
   118     completions=("${commands[@]}")
   214     completions=("${commands[@]}")
   119     if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
   215     if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
   120         completions=("${must_have_one_noun[@]}")
   216         completions+=("${must_have_one_noun[@]}")
       
   217     elif [[ -n "${has_completion_function}" ]]; then
       
   218         # if a go completion function is provided, defer to that function
       
   219         __%[1]s_handle_go_custom_completion
   121     fi
   220     fi
   122     if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
   221     if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
   123         completions+=("${must_have_one_flag[@]}")
   222         completions+=("${must_have_one_flag[@]}")
   124     fi
   223     fi
   125     COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
   224     while IFS='' read -r comp; do
       
   225         COMPREPLY+=("$comp")
       
   226     done < <(compgen -W "${completions[*]}" -- "$cur")
   126 
   227 
   127     if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then
   228     if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then
   128         COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") )
   229         while IFS='' read -r comp; do
       
   230             COMPREPLY+=("$comp")
       
   231         done < <(compgen -W "${noun_aliases[*]}" -- "$cur")
   129     fi
   232     fi
   130 
   233 
   131     if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
   234     if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
   132 		if declare -F __%[1]s_custom_func >/dev/null; then
   235 		if declare -F __%[1]s_custom_func >/dev/null; then
   133 			# try command name qualified custom func
   236 			# try command name qualified custom func
   158 }
   261 }
   159 
   262 
   160 __%[1]s_handle_subdirs_in_dir_flag()
   263 __%[1]s_handle_subdirs_in_dir_flag()
   161 {
   264 {
   162     local dir="$1"
   265     local dir="$1"
   163     pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
   266     pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
   164 }
   267 }
   165 
   268 
   166 __%[1]s_handle_flag()
   269 __%[1]s_handle_flag()
   167 {
   270 {
   168     __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
   271     __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
   270         __%[1]s_handle_noun
   373         __%[1]s_handle_noun
   271     fi
   374     fi
   272     __%[1]s_handle_word
   375     __%[1]s_handle_word
   273 }
   376 }
   274 
   377 
   275 `, name))
   378 `, name, ShellCompNoDescRequestCmd,
   276 }
   379 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
   277 
   380 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
   278 func writePostscript(buf *bytes.Buffer, name string) {
   381 }
       
   382 
       
   383 func writePostscript(buf io.StringWriter, name string) {
   279 	name = strings.Replace(name, ":", "__", -1)
   384 	name = strings.Replace(name, ":", "__", -1)
   280 	buf.WriteString(fmt.Sprintf("__start_%s()\n", name))
   385 	WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
   281 	buf.WriteString(fmt.Sprintf(`{
   386 	WriteStringAndCheck(buf, fmt.Sprintf(`{
   282     local cur prev words cword
   387     local cur prev words cword split
   283     declare -A flaghash 2>/dev/null || :
   388     declare -A flaghash 2>/dev/null || :
   284     declare -A aliashash 2>/dev/null || :
   389     declare -A aliashash 2>/dev/null || :
   285     if declare -F _init_completion >/dev/null 2>&1; then
   390     if declare -F _init_completion >/dev/null 2>&1; then
   286         _init_completion -s || return
   391         _init_completion -s || return
   287     else
   392     else
   293     local two_word_flags=()
   398     local two_word_flags=()
   294     local local_nonpersistent_flags=()
   399     local local_nonpersistent_flags=()
   295     local flags_with_completion=()
   400     local flags_with_completion=()
   296     local flags_completion=()
   401     local flags_completion=()
   297     local commands=("%[1]s")
   402     local commands=("%[1]s")
       
   403     local command_aliases=()
   298     local must_have_one_flag=()
   404     local must_have_one_flag=()
   299     local must_have_one_noun=()
   405     local must_have_one_noun=()
       
   406     local has_completion_function
   300     local last_command
   407     local last_command
   301     local nouns=()
   408     local nouns=()
       
   409     local noun_aliases=()
   302 
   410 
   303     __%[1]s_handle_word
   411     __%[1]s_handle_word
   304 }
   412 }
   305 
   413 
   306 `, name))
   414 `, name))
   307 	buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
   415 	WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
   308     complete -o default -F __start_%s %s
   416     complete -o default -F __start_%s %s
   309 else
   417 else
   310     complete -o default -o nospace -F __start_%s %s
   418     complete -o default -o nospace -F __start_%s %s
   311 fi
   419 fi
   312 
   420 
   313 `, name, name, name, name))
   421 `, name, name, name, name))
   314 	buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n")
   422 	WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")
   315 }
   423 }
   316 
   424 
   317 func writeCommands(buf *bytes.Buffer, cmd *Command) {
   425 func writeCommands(buf io.StringWriter, cmd *Command) {
   318 	buf.WriteString("    commands=()\n")
   426 	WriteStringAndCheck(buf, "    commands=()\n")
   319 	for _, c := range cmd.Commands() {
   427 	for _, c := range cmd.Commands() {
   320 		if !c.IsAvailableCommand() || c == cmd.helpCommand {
   428 		if !c.IsAvailableCommand() && c != cmd.helpCommand {
   321 			continue
   429 			continue
   322 		}
   430 		}
   323 		buf.WriteString(fmt.Sprintf("    commands+=(%q)\n", c.Name()))
   431 		WriteStringAndCheck(buf, fmt.Sprintf("    commands+=(%q)\n", c.Name()))
   324 		writeCmdAliases(buf, c)
   432 		writeCmdAliases(buf, c)
   325 	}
   433 	}
   326 	buf.WriteString("\n")
   434 	WriteStringAndCheck(buf, "\n")
   327 }
   435 }
   328 
   436 
   329 func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
   437 func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {
   330 	for key, value := range annotations {
   438 	for key, value := range annotations {
   331 		switch key {
   439 		switch key {
   332 		case BashCompFilenameExt:
   440 		case BashCompFilenameExt:
   333 			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
   441 			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
   334 
   442 
   335 			var ext string
   443 			var ext string
   336 			if len(value) > 0 {
   444 			if len(value) > 0 {
   337 				ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|")
   445 				ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|")
   338 			} else {
   446 			} else {
   339 				ext = "_filedir"
   447 				ext = "_filedir"
   340 			}
   448 			}
   341 			buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
   449 			WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", ext))
   342 		case BashCompCustom:
   450 		case BashCompCustom:
   343 			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
   451 			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
       
   452 
   344 			if len(value) > 0 {
   453 			if len(value) > 0 {
   345 				handlers := strings.Join(value, "; ")
   454 				handlers := strings.Join(value, "; ")
   346 				buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", handlers))
   455 				WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", handlers))
   347 			} else {
   456 			} else {
   348 				buf.WriteString("    flags_completion+=(:)\n")
   457 				WriteStringAndCheck(buf, "    flags_completion+=(:)\n")
   349 			}
   458 			}
   350 		case BashCompSubdirsInDir:
   459 		case BashCompSubdirsInDir:
   351 			buf.WriteString(fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
   460 			WriteStringAndCheck(buf, fmt.Sprintf("    flags_with_completion+=(%q)\n", name))
   352 
   461 
   353 			var ext string
   462 			var ext string
   354 			if len(value) == 1 {
   463 			if len(value) == 1 {
   355 				ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0]
   464 				ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0]
   356 			} else {
   465 			} else {
   357 				ext = "_filedir -d"
   466 				ext = "_filedir -d"
   358 			}
   467 			}
   359 			buf.WriteString(fmt.Sprintf("    flags_completion+=(%q)\n", ext))
   468 			WriteStringAndCheck(buf, fmt.Sprintf("    flags_completion+=(%q)\n", ext))
   360 		}
   469 		}
   361 	}
   470 	}
   362 }
   471 }
   363 
   472 
   364 func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
   473 const cbn = "\")\n"
       
   474 
       
   475 func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
   365 	name := flag.Shorthand
   476 	name := flag.Shorthand
   366 	format := "    "
   477 	format := "    "
   367 	if len(flag.NoOptDefVal) == 0 {
   478 	if len(flag.NoOptDefVal) == 0 {
   368 		format += "two_word_"
   479 		format += "two_word_"
   369 	}
   480 	}
   370 	format += "flags+=(\"-%s\")\n"
   481 	format += "flags+=(\"-%s" + cbn
   371 	buf.WriteString(fmt.Sprintf(format, name))
   482 	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
   372 	writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
   483 	writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
   373 }
   484 }
   374 
   485 
   375 func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
   486 func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
   376 	name := flag.Name
   487 	name := flag.Name
   377 	format := "    flags+=(\"--%s"
   488 	format := "    flags+=(\"--%s"
   378 	if len(flag.NoOptDefVal) == 0 {
   489 	if len(flag.NoOptDefVal) == 0 {
   379 		format += "="
   490 		format += "="
   380 	}
   491 	}
   381 	format += "\")\n"
   492 	format += cbn
   382 	buf.WriteString(fmt.Sprintf(format, name))
   493 	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
   383 	if len(flag.NoOptDefVal) == 0 {
   494 	if len(flag.NoOptDefVal) == 0 {
   384 		format = "    two_word_flags+=(\"--%s\")\n"
   495 		format = "    two_word_flags+=(\"--%s" + cbn
   385 		buf.WriteString(fmt.Sprintf(format, name))
   496 		WriteStringAndCheck(buf, fmt.Sprintf(format, name))
   386 	}
   497 	}
   387 	writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
   498 	writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
   388 }
   499 }
   389 
   500 
   390 func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
   501 func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
   391 	name := flag.Name
   502 	name := flag.Name
   392 	format := "    local_nonpersistent_flags+=(\"--%s"
   503 	format := "    local_nonpersistent_flags+=(\"--%[1]s" + cbn
   393 	if len(flag.NoOptDefVal) == 0 {
   504 	if len(flag.NoOptDefVal) == 0 {
   394 		format += "="
   505 		format += "    local_nonpersistent_flags+=(\"--%[1]s=" + cbn
   395 	}
   506 	}
   396 	format += "\")\n"
   507 	WriteStringAndCheck(buf, fmt.Sprintf(format, name))
   397 	buf.WriteString(fmt.Sprintf(format, name))
   508 	if len(flag.Shorthand) > 0 {
   398 }
   509 		WriteStringAndCheck(buf, fmt.Sprintf("    local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
   399 
   510 	}
   400 func writeFlags(buf *bytes.Buffer, cmd *Command) {
   511 }
   401 	buf.WriteString(`    flags=()
   512 
       
   513 // Setup annotations for go completions for registered flags
       
   514 func prepareCustomAnnotationsForFlags(cmd *Command) {
       
   515 	flagCompletionMutex.RLock()
       
   516 	defer flagCompletionMutex.RUnlock()
       
   517 	for flag := range flagCompletionFunctions {
       
   518 		// Make sure the completion script calls the __*_go_custom_completion function for
       
   519 		// every registered flag.  We need to do this here (and not when the flag was registered
       
   520 		// for completion) so that we can know the root command name for the prefix
       
   521 		// of __<prefix>_go_custom_completion
       
   522 		if flag.Annotations == nil {
       
   523 			flag.Annotations = map[string][]string{}
       
   524 		}
       
   525 		flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())}
       
   526 	}
       
   527 }
       
   528 
       
   529 func writeFlags(buf io.StringWriter, cmd *Command) {
       
   530 	prepareCustomAnnotationsForFlags(cmd)
       
   531 	WriteStringAndCheck(buf, `    flags=()
   402     two_word_flags=()
   532     two_word_flags=()
   403     local_nonpersistent_flags=()
   533     local_nonpersistent_flags=()
   404     flags_with_completion=()
   534     flags_with_completion=()
   405     flags_completion=()
   535     flags_completion=()
   406 
   536 
   412 		}
   542 		}
   413 		writeFlag(buf, flag, cmd)
   543 		writeFlag(buf, flag, cmd)
   414 		if len(flag.Shorthand) > 0 {
   544 		if len(flag.Shorthand) > 0 {
   415 			writeShortFlag(buf, flag, cmd)
   545 			writeShortFlag(buf, flag, cmd)
   416 		}
   546 		}
   417 		if localNonPersistentFlags.Lookup(flag.Name) != nil {
   547 		// localNonPersistentFlags are used to stop the completion of subcommands when one is set
       
   548 		// if TraverseChildren is true we should allow to complete subcommands
       
   549 		if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren {
   418 			writeLocalNonPersistentFlag(buf, flag)
   550 			writeLocalNonPersistentFlag(buf, flag)
   419 		}
   551 		}
   420 	})
   552 	})
   421 	cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
   553 	cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
   422 		if nonCompletableFlag(flag) {
   554 		if nonCompletableFlag(flag) {
   426 		if len(flag.Shorthand) > 0 {
   558 		if len(flag.Shorthand) > 0 {
   427 			writeShortFlag(buf, flag, cmd)
   559 			writeShortFlag(buf, flag, cmd)
   428 		}
   560 		}
   429 	})
   561 	})
   430 
   562 
   431 	buf.WriteString("\n")
   563 	WriteStringAndCheck(buf, "\n")
   432 }
   564 }
   433 
   565 
   434 func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
   566 func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
   435 	buf.WriteString("    must_have_one_flag=()\n")
   567 	WriteStringAndCheck(buf, "    must_have_one_flag=()\n")
   436 	flags := cmd.NonInheritedFlags()
   568 	flags := cmd.NonInheritedFlags()
   437 	flags.VisitAll(func(flag *pflag.Flag) {
   569 	flags.VisitAll(func(flag *pflag.Flag) {
   438 		if nonCompletableFlag(flag) {
   570 		if nonCompletableFlag(flag) {
   439 			return
   571 			return
   440 		}
   572 		}
   443 			case BashCompOneRequiredFlag:
   575 			case BashCompOneRequiredFlag:
   444 				format := "    must_have_one_flag+=(\"--%s"
   576 				format := "    must_have_one_flag+=(\"--%s"
   445 				if flag.Value.Type() != "bool" {
   577 				if flag.Value.Type() != "bool" {
   446 					format += "="
   578 					format += "="
   447 				}
   579 				}
   448 				format += "\")\n"
   580 				format += cbn
   449 				buf.WriteString(fmt.Sprintf(format, flag.Name))
   581 				WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
   450 
   582 
   451 				if len(flag.Shorthand) > 0 {
   583 				if len(flag.Shorthand) > 0 {
   452 					buf.WriteString(fmt.Sprintf("    must_have_one_flag+=(\"-%s\")\n", flag.Shorthand))
   584 					WriteStringAndCheck(buf, fmt.Sprintf("    must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
   453 				}
   585 				}
   454 			}
   586 			}
   455 		}
   587 		}
   456 	})
   588 	})
   457 }
   589 }
   458 
   590 
   459 func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
   591 func writeRequiredNouns(buf io.StringWriter, cmd *Command) {
   460 	buf.WriteString("    must_have_one_noun=()\n")
   592 	WriteStringAndCheck(buf, "    must_have_one_noun=()\n")
   461 	sort.Sort(sort.StringSlice(cmd.ValidArgs))
   593 	sort.Strings(cmd.ValidArgs)
   462 	for _, value := range cmd.ValidArgs {
   594 	for _, value := range cmd.ValidArgs {
   463 		buf.WriteString(fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
   595 		// Remove any description that may be included following a tab character.
   464 	}
   596 		// Descriptions are not supported by bash completion.
   465 }
   597 		value = strings.Split(value, "\t")[0]
   466 
   598 		WriteStringAndCheck(buf, fmt.Sprintf("    must_have_one_noun+=(%q)\n", value))
   467 func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
   599 	}
       
   600 	if cmd.ValidArgsFunction != nil {
       
   601 		WriteStringAndCheck(buf, "    has_completion_function=1\n")
       
   602 	}
       
   603 }
       
   604 
       
   605 func writeCmdAliases(buf io.StringWriter, cmd *Command) {
   468 	if len(cmd.Aliases) == 0 {
   606 	if len(cmd.Aliases) == 0 {
   469 		return
   607 		return
   470 	}
   608 	}
   471 
   609 
   472 	sort.Sort(sort.StringSlice(cmd.Aliases))
   610 	sort.Strings(cmd.Aliases)
   473 
   611 
   474 	buf.WriteString(fmt.Sprint(`    if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
   612 	WriteStringAndCheck(buf, fmt.Sprint(`    if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
   475 	for _, value := range cmd.Aliases {
   613 	for _, value := range cmd.Aliases {
   476 		buf.WriteString(fmt.Sprintf("        command_aliases+=(%q)\n", value))
   614 		WriteStringAndCheck(buf, fmt.Sprintf("        command_aliases+=(%q)\n", value))
   477 		buf.WriteString(fmt.Sprintf("        aliashash[%q]=%q\n", value, cmd.Name()))
   615 		WriteStringAndCheck(buf, fmt.Sprintf("        aliashash[%q]=%q\n", value, cmd.Name()))
   478 	}
   616 	}
   479 	buf.WriteString(`    fi`)
   617 	WriteStringAndCheck(buf, `    fi`)
   480 	buf.WriteString("\n")
   618 	WriteStringAndCheck(buf, "\n")
   481 }
   619 }
   482 func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
   620 func writeArgAliases(buf io.StringWriter, cmd *Command) {
   483 	buf.WriteString("    noun_aliases=()\n")
   621 	WriteStringAndCheck(buf, "    noun_aliases=()\n")
   484 	sort.Sort(sort.StringSlice(cmd.ArgAliases))
   622 	sort.Strings(cmd.ArgAliases)
   485 	for _, value := range cmd.ArgAliases {
   623 	for _, value := range cmd.ArgAliases {
   486 		buf.WriteString(fmt.Sprintf("    noun_aliases+=(%q)\n", value))
   624 		WriteStringAndCheck(buf, fmt.Sprintf("    noun_aliases+=(%q)\n", value))
   487 	}
   625 	}
   488 }
   626 }
   489 
   627 
   490 func gen(buf *bytes.Buffer, cmd *Command) {
   628 func gen(buf io.StringWriter, cmd *Command) {
   491 	for _, c := range cmd.Commands() {
   629 	for _, c := range cmd.Commands() {
   492 		if !c.IsAvailableCommand() || c == cmd.helpCommand {
   630 		if !c.IsAvailableCommand() && c != cmd.helpCommand {
   493 			continue
   631 			continue
   494 		}
   632 		}
   495 		gen(buf, c)
   633 		gen(buf, c)
   496 	}
   634 	}
   497 	commandName := cmd.CommandPath()
   635 	commandName := cmd.CommandPath()
   498 	commandName = strings.Replace(commandName, " ", "_", -1)
   636 	commandName = strings.Replace(commandName, " ", "_", -1)
   499 	commandName = strings.Replace(commandName, ":", "__", -1)
   637 	commandName = strings.Replace(commandName, ":", "__", -1)
   500 
   638 
   501 	if cmd.Root() == cmd {
   639 	if cmd.Root() == cmd {
   502 		buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName))
   640 		WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
   503 	} else {
   641 	} else {
   504 		buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName))
   642 		WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))
   505 	}
   643 	}
   506 
   644 
   507 	buf.WriteString(fmt.Sprintf("    last_command=%q\n", commandName))
   645 	WriteStringAndCheck(buf, fmt.Sprintf("    last_command=%q\n", commandName))
   508 	buf.WriteString("\n")
   646 	WriteStringAndCheck(buf, "\n")
   509 	buf.WriteString("    command_aliases=()\n")
   647 	WriteStringAndCheck(buf, "    command_aliases=()\n")
   510 	buf.WriteString("\n")
   648 	WriteStringAndCheck(buf, "\n")
   511 
   649 
   512 	writeCommands(buf, cmd)
   650 	writeCommands(buf, cmd)
   513 	writeFlags(buf, cmd)
   651 	writeFlags(buf, cmd)
   514 	writeRequiredFlag(buf, cmd)
   652 	writeRequiredFlag(buf, cmd)
   515 	writeRequiredNouns(buf, cmd)
   653 	writeRequiredNouns(buf, cmd)
   516 	writeArgAliases(buf, cmd)
   654 	writeArgAliases(buf, cmd)
   517 	buf.WriteString("}\n\n")
   655 	WriteStringAndCheck(buf, "}\n\n")
   518 }
   656 }
   519 
   657 
   520 // GenBashCompletion generates bash completion file and writes to the passed writer.
   658 // GenBashCompletion generates bash completion file and writes to the passed writer.
   521 func (c *Command) GenBashCompletion(w io.Writer) error {
   659 func (c *Command) GenBashCompletion(w io.Writer) error {
   522 	buf := new(bytes.Buffer)
   660 	buf := new(bytes.Buffer)