mirror of https://github.com/docker/cli.git
vendor: github.com/spf13/cobra v1.1.3
full diff: https://github.com/spf13/cobra/compare/v1.1.1...v1.1.3 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
d3c36a2a73
commit
25dc8034ff
|
@ -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 5f1f4a34f4cfa3066e44f652e895f4b24b96248e
|
github.com/theupdateframework/notary 5f1f4a34f4cfa3066e44f652e895f4b24b96248e
|
||||||
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
github.com/tonistiigi/fsutil 0834f99b7b85462efb69b4f571a4fa3ca7da5ac9
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(out, "\n '%s' {", cmdName)
|
filter __%[1]s_escapeStringWithSpecialChars {
|
||||||
|
`+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
|
Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||||
if nonCompletableFlag(flag) {
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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\" + ' `\"`\"' "+`
|
||||||
|
}
|
||||||
|
|
||||||
|
__%[1]s_debug "Calling $RequestComp"
|
||||||
|
#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"}
|
||||||
|
}
|
||||||
|
|
||||||
for _, subCmd := range cmd.Commands() {
|
|
||||||
generatePowerShellSubcommandCases(out, subCmd, cmdName)
|
$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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func escapeStringForPowerShell(s string) string {
|
if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or
|
||||||
return strings.Replace(s, "'", "''", -1)
|
(($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) {
|
||||||
|
__%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported"
|
||||||
|
|
||||||
|
# return here to prevent the completion of the extensions
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenPowerShellCompletion generates PowerShell completion file and writes to the passed writer.
|
$Values = $Values | Where-Object {
|
||||||
func (c *Command) GenPowerShellCompletion(w io.Writer) error {
|
# 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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, name, compCmd,
|
||||||
|
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||||
|
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 -*-
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue