Merge pull request #3013 from thaJeztah/bump_cobra

vendor: github.com/spf13/cobra v1.1.3
This commit is contained in:
Silvin Lubecki 2021-06-17 22:33:19 +02:00 committed by GitHub
commit 674c074dc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 430 additions and 231 deletions

View File

@ -65,7 +65,7 @@ github.com/prometheus/procfs 46159f73e74d1cb8dc223deef9b2
github.com/russross/blackfriday/v2 d3b5b032dc8e8927d31a5071b56e14c89f045135 # v2.0.1 github.com/russross/blackfriday/v2 d3b5b032dc8e8927d31a5071b56e14c89f045135 # v2.0.1
github.com/shurcooL/sanitized_anchor_name 7bfe4c7ecddb3666a94b053b422cdd8f5aaa3615 # v1.0.0 github.com/shurcooL/sanitized_anchor_name 7bfe4c7ecddb3666a94b053b422cdd8f5aaa3615 # v1.0.0
github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0 github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
github.com/spf13/cobra 86f8bfd7fef868a174e1b606783bd7f5c82ddf8f # v1.1.1 github.com/spf13/cobra 8380ddd3132bdf8fd77731725b550c181dda0aa8 # v1.1.3
github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5 github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5
github.com/theupdateframework/notary bf96a202a09a312ae005cd312fc06ff4d2c166ce # v0.7.0-21-gbf96a202 github.com/theupdateframework/notary bf96a202a09a312ae005cd312fc06ff4d2c166ce # v0.7.0-21-gbf96a202
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9 github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9

View File

@ -6,6 +6,7 @@ Cobra is used in many Go projects such as [Kubernetes](http://kubernetes.io/),
[Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to [Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to
name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra.
[![](https://img.shields.io/github/workflow/status/spf13/cobra/Test?longCache=tru&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
[![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra) [![Build Status](https://travis-ci.org/spf13/cobra.svg "Travis CI status")](https://travis-ci.org/spf13/cobra)
[![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra) [![GoDoc](https://godoc.org/github.com/spf13/cobra?status.svg)](https://godoc.org/github.com/spf13/cobra)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra)
@ -62,8 +63,8 @@ Cobra is built on a structure of commands, arguments & flags.
**Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions. **Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions.
The best applications will read like sentences when used. Users will know how The best applications read like sentences when used, and as a result, users
to use the application because they will natively understand how to use it. intuitively know how to interact with them.
The pattern to follow is The pattern to follow is
`APPNAME VERB NOUN --ADJECTIVE.` `APPNAME VERB NOUN --ADJECTIVE.`
@ -234,11 +235,6 @@ func init() {
rootCmd.AddCommand(initCmd) rootCmd.AddCommand(initCmd)
} }
func er(msg interface{}) {
fmt.Println("Error:", msg)
os.Exit(1)
}
func initConfig() { func initConfig() {
if cfgFile != "" { if cfgFile != "" {
// Use config file from the flag. // Use config file from the flag.
@ -246,9 +242,7 @@ func initConfig() {
} else { } else {
// Find home directory. // Find home directory.
home, err := homedir.Dir() home, err := homedir.Dir()
if err != nil { cobra.CheckErr(err)
er(err)
}
// Search config in home directory with name ".cobra" (without extension). // Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)
@ -268,7 +262,7 @@ func initConfig() {
With the root command you need to have your main function execute it. With the root command you need to have your main function execute it.
Execute should be run on the root for clarity, though it can be called on any command. Execute should be run on the root for clarity, though it can be called on any command.
In a Cobra app, typically the main.go file is very bare. It serves, one purpose, to initialize Cobra. In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra.
```go ```go
package main package main
@ -363,7 +357,7 @@ There are two different approaches to assign a flag.
### Persistent Flags ### Persistent Flags
A flag can be 'persistent' meaning that this flag will be available to the A flag can be 'persistent', meaning that this flag will be available to the
command it's assigned to as well as every command under that command. For command it's assigned to as well as every command under that command. For
global flags, assign a flag as a persistent flag on the root. global flags, assign a flag as a persistent flag on the root.
@ -373,7 +367,7 @@ rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose out
### Local Flags ### Local Flags
A flag can also be assigned locally which will only apply to that specific command. A flag can also be assigned locally, which will only apply to that specific command.
```go ```go
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
@ -381,8 +375,8 @@ localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to rea
### Local Flag on Parent Commands ### Local Flag on Parent Commands
By default Cobra only parses local flags on the target command, any local flags on By default, Cobra only parses local flags on the target command, and any local flags on
parent commands are ignored. By enabling `Command.TraverseChildren` Cobra will parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will
parse local flags on each command before executing the target command. parse local flags on each command before executing the target command.
```go ```go
@ -404,8 +398,8 @@ func init() {
} }
``` ```
In this example the persistent flag `author` is bound with `viper`. In this example, the persistent flag `author` is bound with `viper`.
**Note**, that the variable `author` will not be set to the value from config, **Note**: the variable `author` will not be set to the value from config,
when the `--author` flag is not provided by user. when the `--author` flag is not provided by user.
More in [viper documentation](https://github.com/spf13/viper#working-with-flags). More in [viper documentation](https://github.com/spf13/viper#working-with-flags).
@ -465,7 +459,7 @@ var cmd = &cobra.Command{
In the example below, we have defined three commands. Two are at the top level In the example below, we have defined three commands. Two are at the top level
and one (cmdTimes) is a child of one of the top commands. In this case the root and one (cmdTimes) is a child of one of the top commands. In this case the root
is not executable meaning that a subcommand is required. This is accomplished is not executable, meaning that a subcommand is required. This is accomplished
by not providing a 'Run' for the 'rootCmd'. by not providing a 'Run' for the 'rootCmd'.
We have only defined one flag for a single command. We have only defined one flag for a single command.
@ -759,7 +753,7 @@ Cobra can generate documentation based on subcommands, flags, etc. Read more abo
## Generating shell completions ## Generating shell completions
Cobra can generate a shell-completion file for the following shells: Bash, Zsh, Fish, Powershell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md). Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md).
# License # License

View File

@ -19,9 +19,9 @@ const (
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
) )
func writePreamble(buf *bytes.Buffer, name string) { func writePreamble(buf io.StringWriter, name string) {
buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
buf.WriteString(fmt.Sprintf(` WriteStringAndCheck(buf, fmt.Sprintf(`
__%[1]s_debug() __%[1]s_debug()
{ {
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
@ -380,10 +380,10 @@ __%[1]s_handle_word()
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs)) ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
} }
func writePostscript(buf *bytes.Buffer, name string) { func writePostscript(buf io.StringWriter, name string) {
name = strings.Replace(name, ":", "__", -1) name = strings.Replace(name, ":", "__", -1)
buf.WriteString(fmt.Sprintf("__start_%s()\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
buf.WriteString(fmt.Sprintf(`{ WriteStringAndCheck(buf, fmt.Sprintf(`{
local cur prev words cword local cur prev words cword
declare -A flaghash 2>/dev/null || : declare -A flaghash 2>/dev/null || :
declare -A aliashash 2>/dev/null || : declare -A aliashash 2>/dev/null || :
@ -410,33 +410,33 @@ func writePostscript(buf *bytes.Buffer, name string) {
} }
`, name)) `, name))
buf.WriteString(fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then
complete -o default -F __start_%s %s complete -o default -F __start_%s %s
else else
complete -o default -o nospace -F __start_%s %s complete -o default -o nospace -F __start_%s %s
fi fi
`, name, name, name, name)) `, name, name, name, name))
buf.WriteString("# ex: ts=4 sw=4 et filetype=sh\n") WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n")
} }
func writeCommands(buf *bytes.Buffer, cmd *Command) { func writeCommands(buf io.StringWriter, cmd *Command) {
buf.WriteString(" commands=()\n") WriteStringAndCheck(buf, " commands=()\n")
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand { if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue continue
} }
buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) WriteStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name()))
writeCmdAliases(buf, c) writeCmdAliases(buf, c)
} }
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) { func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) {
for key, value := range annotations { for key, value := range annotations {
switch key { switch key {
case BashCompFilenameExt: case BashCompFilenameExt:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string var ext string
if len(value) > 0 { if len(value) > 0 {
@ -444,17 +444,18 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else { } else {
ext = "_filedir" ext = "_filedir"
} }
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
case BashCompCustom: case BashCompCustom:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
if len(value) > 0 { if len(value) > 0 {
handlers := strings.Join(value, "; ") handlers := strings.Join(value, "; ")
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers))
} else { } else {
buf.WriteString(" flags_completion+=(:)\n") WriteStringAndCheck(buf, " flags_completion+=(:)\n")
} }
case BashCompSubdirsInDir: case BashCompSubdirsInDir:
buf.WriteString(fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name))
var ext string var ext string
if len(value) == 1 { if len(value) == 1 {
@ -462,46 +463,48 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
} else { } else {
ext = "_filedir -d" ext = "_filedir -d"
} }
buf.WriteString(fmt.Sprintf(" flags_completion+=(%q)\n", ext)) WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext))
} }
} }
} }
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { const cbn = "\")\n"
func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
name := flag.Shorthand name := flag.Shorthand
format := " " format := " "
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += "two_word_" format += "two_word_"
} }
format += "flags+=(\"-%s\")\n" format += "flags+=(\"-%s" + cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
} }
func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) { func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) {
name := flag.Name name := flag.Name
format := " flags+=(\"--%s" format := " flags+=(\"--%s"
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += "=" format += "="
} }
format += "\")\n" format += cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format = " two_word_flags+=(\"--%s\")\n" format = " two_word_flags+=(\"--%s" + cbn
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
} }
writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
} }
func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) { func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
name := flag.Name name := flag.Name
format := " local_nonpersistent_flags+=(\"--%[1]s\")\n" format := " local_nonpersistent_flags+=(\"--%[1]s" + cbn
if len(flag.NoOptDefVal) == 0 { if len(flag.NoOptDefVal) == 0 {
format += " local_nonpersistent_flags+=(\"--%[1]s=\")\n" format += " local_nonpersistent_flags+=(\"--%[1]s=" + cbn
} }
buf.WriteString(fmt.Sprintf(format, name)) WriteStringAndCheck(buf, fmt.Sprintf(format, name))
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
buf.WriteString(fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) WriteStringAndCheck(buf, fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand))
} }
} }
@ -519,9 +522,9 @@ func prepareCustomAnnotationsForFlags(cmd *Command) {
} }
} }
func writeFlags(buf *bytes.Buffer, cmd *Command) { func writeFlags(buf io.StringWriter, cmd *Command) {
prepareCustomAnnotationsForFlags(cmd) prepareCustomAnnotationsForFlags(cmd)
buf.WriteString(` flags=() WriteStringAndCheck(buf, ` flags=()
two_word_flags=() two_word_flags=()
local_nonpersistent_flags=() local_nonpersistent_flags=()
flags_with_completion=() flags_with_completion=()
@ -553,11 +556,11 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
} }
}) })
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { func writeRequiredFlag(buf io.StringWriter, cmd *Command) {
buf.WriteString(" must_have_one_flag=()\n") WriteStringAndCheck(buf, " must_have_one_flag=()\n")
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if nonCompletableFlag(flag) { if nonCompletableFlag(flag) {
@ -570,55 +573,55 @@ func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) {
if flag.Value.Type() != "bool" { if flag.Value.Type() != "bool" {
format += "=" format += "="
} }
format += "\")\n" format += cbn
buf.WriteString(fmt.Sprintf(format, flag.Name)) WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name))
if len(flag.Shorthand) > 0 { if len(flag.Shorthand) > 0 {
buf.WriteString(fmt.Sprintf(" must_have_one_flag+=(\"-%s\")\n", flag.Shorthand)) WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand))
} }
} }
} }
}) })
} }
func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { func writeRequiredNouns(buf io.StringWriter, cmd *Command) {
buf.WriteString(" must_have_one_noun=()\n") WriteStringAndCheck(buf, " must_have_one_noun=()\n")
sort.Sort(sort.StringSlice(cmd.ValidArgs)) sort.Strings(cmd.ValidArgs)
for _, value := range cmd.ValidArgs { for _, value := range cmd.ValidArgs {
// Remove any description that may be included following a tab character. // Remove any description that may be included following a tab character.
// Descriptions are not supported by bash completion. // Descriptions are not supported by bash completion.
value = strings.Split(value, "\t")[0] value = strings.Split(value, "\t")[0]
buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
} }
if cmd.ValidArgsFunction != nil { if cmd.ValidArgsFunction != nil {
buf.WriteString(" has_completion_function=1\n") WriteStringAndCheck(buf, " has_completion_function=1\n")
} }
} }
func writeCmdAliases(buf *bytes.Buffer, cmd *Command) { func writeCmdAliases(buf io.StringWriter, cmd *Command) {
if len(cmd.Aliases) == 0 { if len(cmd.Aliases) == 0 {
return return
} }
sort.Sort(sort.StringSlice(cmd.Aliases)) sort.Strings(cmd.Aliases)
buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n")) WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
for _, value := range cmd.Aliases { for _, value := range cmd.Aliases {
buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value))
buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))
} }
buf.WriteString(` fi`) WriteStringAndCheck(buf, ` fi`)
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
} }
func writeArgAliases(buf *bytes.Buffer, cmd *Command) { func writeArgAliases(buf io.StringWriter, cmd *Command) {
buf.WriteString(" noun_aliases=()\n") WriteStringAndCheck(buf, " noun_aliases=()\n")
sort.Sort(sort.StringSlice(cmd.ArgAliases)) sort.Strings(cmd.ArgAliases)
for _, value := range cmd.ArgAliases { for _, value := range cmd.ArgAliases {
buf.WriteString(fmt.Sprintf(" noun_aliases+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" noun_aliases+=(%q)\n", value))
} }
} }
func gen(buf *bytes.Buffer, cmd *Command) { func gen(buf io.StringWriter, cmd *Command) {
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() && c != cmd.helpCommand { if !c.IsAvailableCommand() && c != cmd.helpCommand {
continue continue
@ -630,22 +633,22 @@ func gen(buf *bytes.Buffer, cmd *Command) {
commandName = strings.Replace(commandName, ":", "__", -1) commandName = strings.Replace(commandName, ":", "__", -1)
if cmd.Root() == cmd { if cmd.Root() == cmd {
buf.WriteString(fmt.Sprintf("_%s_root_command()\n{\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
} else { } else {
buf.WriteString(fmt.Sprintf("_%s()\n{\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName))
} }
buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) WriteStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName))
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
buf.WriteString(" command_aliases=()\n") WriteStringAndCheck(buf, " command_aliases=()\n")
buf.WriteString("\n") WriteStringAndCheck(buf, "\n")
writeCommands(buf, cmd) writeCommands(buf, cmd)
writeFlags(buf, cmd) writeFlags(buf, cmd)
writeRequiredFlag(buf, cmd) writeRequiredFlag(buf, cmd)
writeRequiredNouns(buf, cmd) writeRequiredNouns(buf, cmd)
writeArgAliases(buf, cmd) writeArgAliases(buf, cmd)
buf.WriteString("}\n\n") WriteStringAndCheck(buf, "}\n\n")
} }
// GenBashCompletion generates bash completion file and writes to the passed writer. // GenBashCompletion generates bash completion file and writes to the passed writer.

View File

@ -19,6 +19,7 @@ package cobra
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -205,3 +206,17 @@ func stringInSlice(a string, list []string) bool {
} }
return false return false
} }
// CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing.
func CheckErr(msg interface{}) {
if msg != nil {
fmt.Fprintln(os.Stderr, "Error:", msg)
os.Exit(1)
}
}
// WriteStringAndCheck writes a string into a buffer, and checks if the error is not nil.
func WriteStringAndCheck(b io.StringWriter, s string) {
_, err := b.WriteString(s)
CheckErr(err)
}

View File

@ -84,9 +84,6 @@ type Command struct {
// Deprecated defines, if this command is deprecated and should print this string when used. // Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string Deprecated string
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
// Annotations are key/value pairs that can be used by applications to identify or // Annotations are key/value pairs that can be used by applications to identify or
// group commands. // group commands.
Annotations map[string]string Annotations map[string]string
@ -126,55 +123,6 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error. // PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error PersistentPostRunE func(cmd *Command, args []string) error
// SilenceErrors is an option to quiet errors down stream.
SilenceErrors bool
// SilenceUsage is an option to silence usage when an error occurs.
SilenceUsage bool
// DisableFlagParsing disables the flag parsing.
// If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
// will be printed by generating docs for this command.
DisableAutoGenTag bool
// DisableFlagsInUseLine will disable the addition of [flags] to the usage
// line of a command when printing help or generating docs
DisableFlagsInUseLine bool
// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
// FParseErrWhitelist flag parse errors to be ignored
FParseErrWhitelist FParseErrWhitelist
ctx context.Context
// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}
// args is actual args parsed from flags. // args is actual args parsed from flags.
args []string args []string
// flagErrorBuf contains all error messages from pflag. // flagErrorBuf contains all error messages from pflag.
@ -216,6 +164,60 @@ type Command struct {
outWriter io.Writer outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr // errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer errWriter io.Writer
//FParseErrWhitelist flag parse errors to be ignored
FParseErrWhitelist FParseErrWhitelist
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}
ctx context.Context
// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
// SilenceErrors is an option to quiet errors down stream.
SilenceErrors bool
// SilenceUsage is an option to silence usage when an error occurs.
SilenceUsage bool
// DisableFlagParsing disables the flag parsing.
// If this is true all flags will be passed to the command as arguments.
DisableFlagParsing bool
// DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...")
// will be printed by generating docs for this command.
DisableAutoGenTag bool
// DisableFlagsInUseLine will disable the addition of [flags] to the usage
// line of a command when printing help or generating docs
DisableFlagsInUseLine bool
// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
} }
// Context returns underlying command context. If command wasn't // Context returns underlying command context. If command wasn't
@ -418,7 +420,7 @@ func (c *Command) UsageString() string {
c.outWriter = bb c.outWriter = bb
c.errWriter = bb c.errWriter = bb
c.Usage() CheckErr(c.Usage())
// Setting things back to normal // Setting things back to normal
c.outWriter = tmpOutput c.outWriter = tmpOutput
@ -964,13 +966,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
return cmd, nil return cmd, nil
} }
// If root command has SilentErrors flagged, // If root command has SilenceErrors flagged,
// all subcommands should respect it // all subcommands should respect it
if !cmd.SilenceErrors && !c.SilenceErrors { if !cmd.SilenceErrors && !c.SilenceErrors {
c.PrintErrln("Error:", err.Error()) c.PrintErrln("Error:", err.Error())
} }
// If root command has SilentUsage flagged, // If root command has SilenceUsage flagged,
// all subcommands should respect it // all subcommands should respect it
if !cmd.SilenceUsage && !c.SilenceUsage { if !cmd.SilenceUsage && !c.SilenceUsage {
c.Println(cmd.UsageString()) c.Println(cmd.UsageString())
@ -1087,10 +1089,10 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
cmd, _, e := c.Root().Find(args) cmd, _, e := c.Root().Find(args)
if cmd == nil || e != nil { if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args) c.Printf("Unknown help topic %#q\n", args)
c.Root().Usage() CheckErr(c.Root().Usage())
} else { } else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
cmd.Help() CheckErr(cmd.Help())
} }
}, },
} }

View File

@ -527,13 +527,13 @@ func CompDebug(msg string, printToStdErr bool) {
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err == nil { if err == nil {
defer f.Close() defer f.Close()
f.WriteString(msg) WriteStringAndCheck(f, msg)
} }
} }
if printToStdErr { if printToStdErr {
// Must print to stderr for this not to be read by the completion script. // Must print to stderr for this not to be read by the completion script.
fmt.Fprintf(os.Stderr, msg) fmt.Fprint(os.Stderr, msg)
} }
} }

View File

@ -139,23 +139,23 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
return nil return nil
} }
func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, dashedName string) { func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) {
description := cmd.Long description := cmd.Long
if len(description) == 0 { if len(description) == 0 {
description = cmd.Short description = cmd.Short
} }
buf.WriteString(fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s" cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
# NAME # NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual)) `, header.Title, header.Section, header.date, header.Source, header.Manual))
buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short)) cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
buf.WriteString("# SYNOPSIS\n") cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
buf.WriteString(fmt.Sprintf("**%s**\n\n", cmd.UseLine())) cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
buf.WriteString("# DESCRIPTION\n") cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
buf.WriteString(description + "\n\n") cobra.WriteStringAndCheck(buf, description+"\n\n")
} }
func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) { func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) { flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden { if len(flag.Deprecated) > 0 || flag.Hidden {
return return
@ -179,22 +179,22 @@ func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
format += "]" format += "]"
} }
format += "\n\t%s\n\n" format += "\n\t%s\n\n"
buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage)) cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
}) })
} }
func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) { func manPrintOptions(buf io.StringWriter, command *cobra.Command) {
flags := command.NonInheritedFlags() flags := command.NonInheritedFlags()
if flags.HasAvailableFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS\n") cobra.WriteStringAndCheck(buf, "# OPTIONS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") cobra.WriteStringAndCheck(buf, "\n")
} }
flags = command.InheritedFlags() flags = command.InheritedFlags()
if flags.HasAvailableFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n") cobra.WriteStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") cobra.WriteStringAndCheck(buf, "\n")
} }
} }

View File

@ -8,7 +8,7 @@ import (
"strings" "strings"
) )
func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) { func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
// Variables should not contain a '-' or ':' character // Variables should not contain a '-' or ':' character
nameForVar := name nameForVar := name
nameForVar = strings.Replace(nameForVar, "-", "_", -1) nameForVar = strings.Replace(nameForVar, "-", "_", -1)
@ -18,8 +18,8 @@ func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
if !includeDesc { if !includeDesc {
compCmd = ShellCompNoDescRequestCmd compCmd = ShellCompNoDescRequestCmd
} }
buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
buf.WriteString(fmt.Sprintf(` WriteStringAndCheck(buf, fmt.Sprintf(`
function __%[1]s_debug function __%[1]s_debug
set file "$BASH_COMP_DEBUG_FILE" set file "$BASH_COMP_DEBUG_FILE"
if test -n "$file" if test -n "$file"

View File

@ -8,5 +8,5 @@ require (
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.0
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.4.0
) )

View File

@ -1,6 +1,3 @@
// PowerShell completions are based on the amazing work from clap:
// https://github.com/clap-rs/clap/blob/3294d18efe5f264d12c9035f404c7d189d4824e1/src/completions/powershell.rs
//
// The generated scripts require PowerShell v5.0+ (which comes Windows 10, but // The generated scripts require PowerShell v5.0+ (which comes Windows 10, but
// can be downloaded separately for windows 7 or 8.1). // can be downloaded separately for windows 7 or 8.1).
@ -11,90 +8,278 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"strings"
"github.com/spf13/pflag"
) )
var powerShellCompletionTemplate = `using namespace System.Management.Automation func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
using namespace System.Management.Automation.Language compCmd := ShellCompRequestCmd
Register-ArgumentCompleter -Native -CommandName '%s' -ScriptBlock { if !includeDesc {
param($wordToComplete, $commandAst, $cursorPosition) compCmd = ShellCompNoDescRequestCmd
$commandElements = $commandAst.CommandElements
$command = @(
'%s'
for ($i = 1; $i -lt $commandElements.Count; $i++) {
$element = $commandElements[$i]
if ($element -isnot [StringConstantExpressionAst] -or
$element.StringConstantType -ne [StringConstantType]::BareWord -or
$element.Value.StartsWith('-')) {
break
} }
$element.Value WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*-
}
) -join ';'
$completions = @(switch ($command) {%s
})
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
Sort-Object -Property ListItemText
}`
func generatePowerShellSubcommandCases(out io.Writer, cmd *Command, previousCommandName string) { function __%[1]s_debug {
var cmdName string if ($env:BASH_COMP_DEBUG_FILE) {
if previousCommandName == "" { "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE"
cmdName = cmd.Name() }
} else { }
cmdName = fmt.Sprintf("%s;%s", previousCommandName, cmd.Name())
filter __%[1]s_escapeStringWithSpecialChars {
`+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
}
Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
param(
$WordToComplete,
$CommandAst,
$CursorPosition
)
# Get the current command line and convert into a string
$Command = $CommandAst.CommandElements
$Command = "$Command"
__%[1]s_debug ""
__%[1]s_debug "========= starting completion logic =========="
__%[1]s_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition"
# The user could have moved the cursor backwards on the command-line.
# We need to trigger completion from the $CursorPosition location, so we need
# to truncate the command-line ($Command) up to the $CursorPosition location.
# Make sure the $Command is longer then the $CursorPosition before we truncate.
# This happens because the $Command does not include the last space.
if ($Command.Length -gt $CursorPosition) {
$Command=$Command.Substring(0,$CursorPosition)
}
__%[1]s_debug "Truncated command: $Command"
$ShellCompDirectiveError=%[3]d
$ShellCompDirectiveNoSpace=%[4]d
$ShellCompDirectiveNoFileComp=%[5]d
$ShellCompDirectiveFilterFileExt=%[6]d
$ShellCompDirectiveFilterDirs=%[7]d
# Prepare the command to request completions for the program.
# Split the command at the first space to separate the program and arguments.
$Program,$Arguments = $Command.Split(" ",2)
$RequestComp="$Program %[2]s $Arguments"
__%[1]s_debug "RequestComp: $RequestComp"
# we cannot use $WordToComplete because it
# has the wrong values if the cursor was moved
# so use the last argument
if ($WordToComplete -ne "" ) {
$WordToComplete = $Arguments.Split(" ")[-1]
}
__%[1]s_debug "New WordToComplete: $WordToComplete"
# Check for flag with equal sign
$IsEqualFlag = ($WordToComplete -Like "--*=*" )
if ( $IsEqualFlag ) {
__%[1]s_debug "Completing equal sign flag"
# Remove the flag part
$Flag,$WordToComplete = $WordToComplete.Split("=",2)
} }
fmt.Fprintf(out, "\n '%s' {", cmdName) if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) {
# If the last parameter is complete (there is a space following it)
# We add an extra empty parameter so we can indicate this to the go method.
__%[1]s_debug "Adding extra empty parameter"
`+" # We need to use `\"`\" to pass an empty argument a \"\" or '' does not work!!!"+`
`+" $RequestComp=\"$RequestComp\" + ' `\"`\"' "+`
}
cmd.Flags().VisitAll(func(flag *pflag.Flag) { __%[1]s_debug "Calling $RequestComp"
if nonCompletableFlag(flag) { #call the command store the output in $out and redirect stderr and stdout to null
# $Out is an array contains each line per element
Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
# get directive from last line
[int]$Directive = $Out[-1].TrimStart(':')
if ($Directive -eq "") {
# There is no directive specified
$Directive = 0
}
__%[1]s_debug "The completion directive is: $Directive"
# remove directive (last element) from out
$Out = $Out | Where-Object { $_ -ne $Out[-1] }
__%[1]s_debug "The completions are: $Out"
if (($Directive -band $ShellCompDirectiveError) -ne 0 ) {
# Error code. No completion.
__%[1]s_debug "Received error from custom completion go code"
return return
} }
usage := escapeStringForPowerShell(flag.Usage)
if len(flag.Shorthand) > 0 {
fmt.Fprintf(out, "\n [CompletionResult]::new('-%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Shorthand, flag.Shorthand, usage)
}
fmt.Fprintf(out, "\n [CompletionResult]::new('--%s', '%s', [CompletionResultType]::ParameterName, '%s')", flag.Name, flag.Name, usage)
})
for _, subCmd := range cmd.Commands() { $Longest = 0
usage := escapeStringForPowerShell(subCmd.Short) $Values = $Out | ForEach-Object {
fmt.Fprintf(out, "\n [CompletionResult]::new('%s', '%s', [CompletionResultType]::ParameterValue, '%s')", subCmd.Name(), subCmd.Name(), usage) #Split the output in name and description
`+" $Name, $Description = $_.Split(\"`t\",2)"+`
__%[1]s_debug "Name: $Name Description: $Description"
# Look for the longest completion so that we can format things nicely
if ($Longest -lt $Name.Length) {
$Longest = $Name.Length
} }
fmt.Fprint(out, "\n break\n }") # Set the description to a one space string if there is none set.
# This is needed because the CompletionResult does not accept an empty string as argument
if (-Not $Description) {
$Description = " "
}
@{Name="$Name";Description="$Description"}
}
$Space = " "
if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) {
# remove the space here
__%[1]s_debug "ShellCompDirectiveNoSpace is called"
$Space = ""
}
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
__%[1]s_debug "ShellCompDirectiveNoFileComp is called"
if ($Values.Length -eq 0) {
# Just print an empty string here so the
# shell does not start to complete paths.
# We cannot use CompletionResult here because
# it does not accept an empty string as argument.
""
return
}
}
if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
(($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) {
__%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
# return here to prevent the completion of the extensions
return
}
$Values = $Values | Where-Object {
# filter the result
$_.Name -like "$WordToComplete*"
# Join the flag back if we have a equal sign flag
if ( $IsEqualFlag ) {
__%[1]s_debug "Join the equal sign flag back to the completion value"
$_.Name = $Flag + "=" + $_.Name
}
}
# Get the current mode
$Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function
__%[1]s_debug "Mode: $Mode"
$Values | ForEach-Object {
# store temporay because switch will overwrite $_
$comp = $_
# PowerShell supports three different completion modes
# - TabCompleteNext (default windows style - on each key press the next option is displayed)
# - Complete (works like bash)
# - MenuComplete (works like zsh)
# You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function <mode>
# CompletionResult Arguments:
# 1) CompletionText text to be used as the auto completion result
# 2) ListItemText text to be displayed in the suggestion list
# 3) ResultType type of completion result
# 4) ToolTip text for the tooltip with details about the object
switch ($Mode) {
# bash like
"Complete" {
if ($Values.Length -eq 1) {
__%[1]s_debug "Only one completion left"
# insert space after value
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
} else {
# Add the proper number of spaces to align the descriptions
while($comp.Name.Length -lt $Longest) {
$comp.Name = $comp.Name + " "
}
# Check for empty description and only add parentheses if needed
if ($($comp.Description) -eq " " ) {
$Description = ""
} else {
$Description = " ($($comp.Description))"
}
[System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)")
}
}
# zsh like
"MenuComplete" {
# insert space after value
# MenuComplete will automatically show the ToolTip of
# the highlighted value at the bottom of the suggestions.
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
}
# TabCompleteNext and in case we get something unknown
Default {
# Like MenuComplete but we don't want to add a space here because
# the user need to press space anyway to get the completion.
# Description will not be shown because thats not possible with TabCompleteNext
[System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)")
}
}
for _, subCmd := range cmd.Commands() {
generatePowerShellSubcommandCases(out, subCmd, cmdName)
} }
} }
`, name, compCmd,
func escapeStringForPowerShell(s string) string { ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
return strings.Replace(s, "'", "''", -1) ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
} }
// GenPowerShellCompletion generates PowerShell completion file and writes to the passed writer. func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
func (c *Command) GenPowerShellCompletion(w io.Writer) error {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
genPowerShellComp(buf, c.Name(), includeDesc)
var subCommandCases bytes.Buffer
generatePowerShellSubcommandCases(&subCommandCases, c, "")
fmt.Fprintf(buf, powerShellCompletionTemplate, c.Name(), c.Name(), subCommandCases.String())
_, err := buf.WriteTo(w) _, err := buf.WriteTo(w)
return err return err
} }
// GenPowerShellCompletionFile generates PowerShell completion file. func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error {
func (c *Command) GenPowerShellCompletionFile(filename string) error {
outFile, err := os.Create(filename) outFile, err := os.Create(filename)
if err != nil { if err != nil {
return err return err
} }
defer outFile.Close() defer outFile.Close()
return c.GenPowerShellCompletion(outFile) return c.genPowerShellCompletion(outFile, includeDesc)
}
// GenPowerShellCompletionFile generates powershell completion file without descriptions.
func (c *Command) GenPowerShellCompletionFile(filename string) error {
return c.genPowerShellCompletionFile(filename, false)
}
// GenPowerShellCompletion generates powershell completion file without descriptions
// and writes it to the passed writer.
func (c *Command) GenPowerShellCompletion(w io.Writer) error {
return c.genPowerShellCompletion(w, false)
}
// GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions.
func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error {
return c.genPowerShellCompletionFile(filename, true)
}
// GenPowerShellCompletionWithDesc generates powershell completion file with descriptions
// and writes it to the passed writer.
func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error {
return c.genPowerShellCompletion(w, true)
} }

View File

@ -70,12 +70,12 @@ func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error {
return err return err
} }
func genZshComp(buf *bytes.Buffer, name string, includeDesc bool) { func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
compCmd := ShellCompRequestCmd compCmd := ShellCompRequestCmd
if !includeDesc { if !includeDesc {
compCmd = ShellCompNoDescRequestCmd compCmd = ShellCompNoDescRequestCmd
} }
buf.WriteString(fmt.Sprintf(`#compdef _%[1]s %[1]s WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
# zsh completion for %-36[1]s -*- shell-script -*- # zsh completion for %-36[1]s -*- shell-script -*-