Update module spf13/cobra to v1 (#511)
Update module spf13/cobra to v1 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/511
This commit is contained in:
parent
91a3b7aba2
commit
4533ac6b28
11 changed files with 855 additions and 61 deletions
2
go.mod
2
go.mod
|
@ -60,7 +60,7 @@ require (
|
||||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||||
github.com/spf13/afero v1.2.2
|
github.com/spf13/afero v1.2.2
|
||||||
github.com/spf13/cobra v0.0.7
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/viper v1.7.0
|
github.com/spf13/viper v1.7.0
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -462,6 +462,8 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
|
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
|
||||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||||
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
|
|
2
vendor/github.com/spf13/cobra/README.md
generated
vendored
2
vendor/github.com/spf13/cobra/README.md
generated
vendored
|
@ -25,10 +25,10 @@ Many of the most widely used Go projects are built using Cobra, such as:
|
||||||
[mattermost-server](https://github.com/mattermost/mattermost-server),
|
[mattermost-server](https://github.com/mattermost/mattermost-server),
|
||||||
[Gardener](https://github.com/gardener/gardenctl),
|
[Gardener](https://github.com/gardener/gardenctl),
|
||||||
[Linkerd](https://linkerd.io/),
|
[Linkerd](https://linkerd.io/),
|
||||||
|
[Github CLI](https://github.com/cli/cli)
|
||||||
etc.
|
etc.
|
||||||
|
|
||||||
[![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)
|
||||||
[![CircleCI status](https://circleci.com/gh/spf13/cobra.png?circle-token=:circle-token "CircleCI status")](https://circleci.com/gh/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)
|
||||||
|
|
||||||
|
|
10
vendor/github.com/spf13/cobra/args.go
generated
vendored
10
vendor/github.com/spf13/cobra/args.go
generated
vendored
|
@ -2,6 +2,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PositionalArgs func(cmd *Command, args []string) error
|
type PositionalArgs func(cmd *Command, args []string) error
|
||||||
|
@ -34,8 +35,15 @@ func NoArgs(cmd *Command, args []string) error {
|
||||||
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
|
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
|
||||||
func OnlyValidArgs(cmd *Command, args []string) error {
|
func OnlyValidArgs(cmd *Command, args []string) error {
|
||||||
if len(cmd.ValidArgs) > 0 {
|
if len(cmd.ValidArgs) > 0 {
|
||||||
|
// Remove any description that may be included in ValidArgs.
|
||||||
|
// A description is following a tab character.
|
||||||
|
var validArgs []string
|
||||||
|
for _, v := range cmd.ValidArgs {
|
||||||
|
validArgs = append(validArgs, strings.Split(v, "\t")[0])
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range args {
|
for _, v := range args {
|
||||||
if !stringInSlice(v, cmd.ValidArgs) {
|
if !stringInSlice(v, validArgs) {
|
||||||
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
89
vendor/github.com/spf13/cobra/bash_completions.go
generated
vendored
89
vendor/github.com/spf13/cobra/bash_completions.go
generated
vendored
|
@ -58,6 +58,67 @@ __%[1]s_contains_word()
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__%[1]s_handle_go_custom_completion()
|
||||||
|
{
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}"
|
||||||
|
|
||||||
|
local out requestComp lastParam lastChar comp directive args
|
||||||
|
|
||||||
|
# Prepare the command to request completions for the program.
|
||||||
|
# Calling ${words[0]} instead of directly %[1]s allows to handle aliases
|
||||||
|
args=("${words[@]:1}")
|
||||||
|
requestComp="${words[0]} %[2]s ${args[*]}"
|
||||||
|
|
||||||
|
lastParam=${words[$((${#words[@]}-1))]}
|
||||||
|
lastChar=${lastParam:$((${#lastParam}-1)):1}
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}"
|
||||||
|
|
||||||
|
if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then
|
||||||
|
# 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 "${FUNCNAME[0]}: Adding extra empty parameter"
|
||||||
|
requestComp="${requestComp} \"\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}"
|
||||||
|
# Use eval to handle any environment variables and such
|
||||||
|
out=$(eval "${requestComp}" 2>/dev/null)
|
||||||
|
|
||||||
|
# Extract the directive integer at the very end of the output following a colon (:)
|
||||||
|
directive=${out##*:}
|
||||||
|
# Remove the directive
|
||||||
|
out=${out%%:*}
|
||||||
|
if [ "${directive}" = "${out}" ]; then
|
||||||
|
# There is not directive specified
|
||||||
|
directive=0
|
||||||
|
fi
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
|
||||||
|
|
||||||
|
if [ $((directive & %[3]d)) -ne 0 ]; then
|
||||||
|
# Error code. No completion.
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
if [ $((directive & %[4]d)) -ne 0 ]; then
|
||||||
|
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: activating no space"
|
||||||
|
compopt -o nospace
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ $((directive & %[5]d)) -ne 0 ]; then
|
||||||
|
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||||
|
__%[1]s_debug "${FUNCNAME[0]}: activating no file completion"
|
||||||
|
compopt +o default
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS='' read -r comp; do
|
||||||
|
COMPREPLY+=("$comp")
|
||||||
|
done < <(compgen -W "${out[*]}" -- "$cur")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
__%[1]s_handle_reply()
|
__%[1]s_handle_reply()
|
||||||
{
|
{
|
||||||
__%[1]s_debug "${FUNCNAME[0]}"
|
__%[1]s_debug "${FUNCNAME[0]}"
|
||||||
|
@ -121,6 +182,10 @@ __%[1]s_handle_reply()
|
||||||
completions=("${commands[@]}")
|
completions=("${commands[@]}")
|
||||||
if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
|
if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
|
||||||
completions=("${must_have_one_noun[@]}")
|
completions=("${must_have_one_noun[@]}")
|
||||||
|
elif [[ -n "${has_completion_function}" ]]; then
|
||||||
|
# if a go completion function is provided, defer to that function
|
||||||
|
completions=()
|
||||||
|
__%[1]s_handle_go_custom_completion
|
||||||
fi
|
fi
|
||||||
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
|
if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
|
||||||
completions+=("${must_have_one_flag[@]}")
|
completions+=("${must_have_one_flag[@]}")
|
||||||
|
@ -279,7 +344,7 @@ __%[1]s_handle_word()
|
||||||
__%[1]s_handle_word
|
__%[1]s_handle_word
|
||||||
}
|
}
|
||||||
|
|
||||||
`, name))
|
`, name, ShellCompNoDescRequestCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePostscript(buf *bytes.Buffer, name string) {
|
func writePostscript(buf *bytes.Buffer, name string) {
|
||||||
|
@ -304,6 +369,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
|
||||||
local commands=("%[1]s")
|
local commands=("%[1]s")
|
||||||
local must_have_one_flag=()
|
local must_have_one_flag=()
|
||||||
local must_have_one_noun=()
|
local must_have_one_noun=()
|
||||||
|
local has_completion_function
|
||||||
local last_command
|
local last_command
|
||||||
local nouns=()
|
local nouns=()
|
||||||
|
|
||||||
|
@ -404,7 +470,22 @@ func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||||
buf.WriteString(fmt.Sprintf(format, name))
|
buf.WriteString(fmt.Sprintf(format, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup annotations for go completions for registered flags
|
||||||
|
func prepareCustomAnnotationsForFlags(cmd *Command) {
|
||||||
|
for flag := range flagCompletionFunctions {
|
||||||
|
// Make sure the completion script calls the __*_go_custom_completion function for
|
||||||
|
// every registered flag. We need to do this here (and not when the flag was registered
|
||||||
|
// for completion) so that we can know the root command name for the prefix
|
||||||
|
// of __<prefix>_go_custom_completion
|
||||||
|
if flag.Annotations == nil {
|
||||||
|
flag.Annotations = map[string][]string{}
|
||||||
|
}
|
||||||
|
flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
||||||
|
prepareCustomAnnotationsForFlags(cmd)
|
||||||
buf.WriteString(` flags=()
|
buf.WriteString(` flags=()
|
||||||
two_word_flags=()
|
two_word_flags=()
|
||||||
local_nonpersistent_flags=()
|
local_nonpersistent_flags=()
|
||||||
|
@ -467,8 +548,14 @@ func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
|
||||||
buf.WriteString(" must_have_one_noun=()\n")
|
buf.WriteString(" must_have_one_noun=()\n")
|
||||||
sort.Sort(sort.StringSlice(cmd.ValidArgs))
|
sort.Sort(sort.StringSlice(cmd.ValidArgs))
|
||||||
for _, value := range cmd.ValidArgs {
|
for _, value := range cmd.ValidArgs {
|
||||||
|
// Remove any description that may be included following a tab character.
|
||||||
|
// Descriptions are not supported by bash completion.
|
||||||
|
value = strings.Split(value, "\t")[0]
|
||||||
buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
|
buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value))
|
||||||
}
|
}
|
||||||
|
if cmd.ValidArgsFunction != nil {
|
||||||
|
buf.WriteString(" has_completion_function=1\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
|
func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
|
||||||
|
|
239
vendor/github.com/spf13/cobra/bash_completions.md
generated
vendored
239
vendor/github.com/spf13/cobra/bash_completions.md
generated
vendored
|
@ -56,7 +56,149 @@ func main() {
|
||||||
|
|
||||||
`out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
|
`out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior.
|
||||||
|
|
||||||
## Creating your own custom functions
|
## Have the completions code complete your 'nouns'
|
||||||
|
|
||||||
|
### Static completion of nouns
|
||||||
|
|
||||||
|
This method allows you to provide a pre-defined list of completion choices for your nouns using the `validArgs` field.
|
||||||
|
For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
|
||||||
|
|
||||||
|
```go
|
||||||
|
validArgs []string = { "pod", "node", "service", "replicationcontroller" }
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
|
||||||
|
Short: "Display one or many resources",
|
||||||
|
Long: get_long,
|
||||||
|
Example: get_example,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := RunGet(f, out, cmd, args)
|
||||||
|
util.CheckErr(err)
|
||||||
|
},
|
||||||
|
ValidArgs: validArgs,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubectl get [tab][tab]
|
||||||
|
node pod replicationcontroller service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plural form and shortcuts for nouns
|
||||||
|
|
||||||
|
If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
...
|
||||||
|
ValidArgs: validArgs,
|
||||||
|
ArgAliases: argAliases
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
|
||||||
|
the completion algorithm if entered manually, e.g. in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# kubectl get rc [tab][tab]
|
||||||
|
backend frontend database
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns
|
||||||
|
in this example again instead of the replication controllers.
|
||||||
|
|
||||||
|
### Dynamic completion of nouns
|
||||||
|
|
||||||
|
In some cases it is not possible to provide a list of possible completions in advance. Instead, the list of completions must be determined at execution-time. Cobra provides two ways of defining such dynamic completion of nouns. Note that both these methods can be used along-side each other as long as they are not both used for the same command.
|
||||||
|
|
||||||
|
**Note**: *Custom Completions written in Go* will automatically work for other shell-completion scripts (e.g., Fish shell), while *Custom Completions written in Bash* will only work for Bash shell-completion. It is therefore recommended to use *Custom Completions written in Go*.
|
||||||
|
|
||||||
|
#### 1. Custom completions of nouns written in Go
|
||||||
|
|
||||||
|
In a similar fashion as for static completions, you can use the `ValidArgsFunction` field to provide a Go function that Cobra will execute when it needs the list of completion choices for the nouns of a command. Note that either `ValidArgs` or `ValidArgsFunction` can be used for a single cobra command, but not both.
|
||||||
|
Simplified code from `helm status` looks like:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "status RELEASE_NAME",
|
||||||
|
Short: "Display the status of the named release",
|
||||||
|
Long: status_long,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) {
|
||||||
|
RunGet(args[0])
|
||||||
|
},
|
||||||
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Where `getReleasesFromCluster()` is a Go function that obtains the list of current Helm releases running on the Kubernetes cluster.
|
||||||
|
Notice we put the `ValidArgsFunction` on the `status` subcommand. Let's assume the Helm releases on the cluster are: `harbor`, `notary`, `rook` and `thanos` then this dynamic completion will give results like
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# helm status [tab][tab]
|
||||||
|
harbor notary rook thanos
|
||||||
|
```
|
||||||
|
You may have noticed the use of `cobra.ShellCompDirective`. These directives are bit fields allowing to control some shell completion behaviors for your particular completion. You can combine them with the bit-or operator such as `cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp`
|
||||||
|
```go
|
||||||
|
// Indicates an error occurred and completions should be ignored.
|
||||||
|
ShellCompDirectiveError
|
||||||
|
// Indicates that the shell should not add a space after the completion,
|
||||||
|
// even if there is a single completion provided.
|
||||||
|
ShellCompDirectiveNoSpace
|
||||||
|
// Indicates that the shell should not provide file completion even when
|
||||||
|
// no completion is provided.
|
||||||
|
// This currently does not work for zsh or bash < 4
|
||||||
|
ShellCompDirectiveNoFileComp
|
||||||
|
// Indicates that the shell will perform its default behavior after completions
|
||||||
|
// have been provided (this implies !ShellCompDirectiveNoSpace && !ShellCompDirectiveNoFileComp).
|
||||||
|
ShellCompDirectiveDefault
|
||||||
|
```
|
||||||
|
|
||||||
|
When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function.
|
||||||
|
|
||||||
|
##### Debugging
|
||||||
|
|
||||||
|
Cobra achieves dynamic completions written in Go through the use of a hidden command called by the completion script. To debug your Go completion code, you can call this hidden command directly:
|
||||||
|
```bash
|
||||||
|
# helm __complete status har<ENTER>
|
||||||
|
harbor
|
||||||
|
:4
|
||||||
|
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||||
|
```
|
||||||
|
***Important:*** If the noun to complete is empty, you must pass an empty parameter to the `__complete` command:
|
||||||
|
```bash
|
||||||
|
# helm __complete status ""<ENTER>
|
||||||
|
harbor
|
||||||
|
notary
|
||||||
|
rook
|
||||||
|
thanos
|
||||||
|
:4
|
||||||
|
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||||
|
```
|
||||||
|
Calling the `__complete` command directly allows you to run the Go debugger to troubleshoot your code. You can also add printouts to your code; Cobra provides the following functions to use for printouts in Go completion code:
|
||||||
|
```go
|
||||||
|
// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
|
||||||
|
// is set to a file path) and optionally prints to stderr.
|
||||||
|
cobra.CompDebug(msg string, printToStdErr bool) {
|
||||||
|
cobra.CompDebugln(msg string, printToStdErr bool)
|
||||||
|
|
||||||
|
// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
|
||||||
|
// is set to a file path) and to stderr.
|
||||||
|
cobra.CompError(msg string)
|
||||||
|
cobra.CompErrorln(msg string)
|
||||||
|
```
|
||||||
|
***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned above.
|
||||||
|
|
||||||
|
#### 2. Custom completions of nouns written in Bash
|
||||||
|
|
||||||
|
This method allows you to inject bash functions into the completion script. Those bash functions are responsible for providing the completion choices for your own completions.
|
||||||
|
|
||||||
Some more actual code that works in kubernetes:
|
Some more actual code that works in kubernetes:
|
||||||
|
|
||||||
|
@ -111,58 +253,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||||
|
|
||||||
The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`__<command-use>_custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods!
|
The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`__<command-use>_custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods!
|
||||||
|
|
||||||
## Have the completions code complete your 'nouns'
|
|
||||||
|
|
||||||
In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like:
|
|
||||||
|
|
||||||
```go
|
|
||||||
validArgs []string = { "pod", "node", "service", "replicationcontroller" }
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
|
|
||||||
Short: "Display one or many resources",
|
|
||||||
Long: get_long,
|
|
||||||
Example: get_example,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
err := RunGet(f, out, cmd, args)
|
|
||||||
util.CheckErr(err)
|
|
||||||
},
|
|
||||||
ValidArgs: validArgs,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# kubectl get [tab][tab]
|
|
||||||
node pod replicationcontroller service
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plural form and shortcuts for nouns
|
|
||||||
|
|
||||||
If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
...
|
|
||||||
ValidArgs: validArgs,
|
|
||||||
ArgAliases: argAliases
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
|
|
||||||
the completion algorithm if entered manually, e.g. in:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# kubectl get rc [tab][tab]
|
|
||||||
backend frontend database
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns
|
|
||||||
in this example again instead of the replication controllers.
|
|
||||||
|
|
||||||
## Mark flags as required
|
## Mark flags as required
|
||||||
|
|
||||||
Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab]. Marking a flag as 'Required' is incredibly easy.
|
Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types [tab][tab]. Marking a flag as 'Required' is incredibly easy.
|
||||||
|
@ -211,8 +301,45 @@ So while there are many other files in the CWD it only shows me subdirs and thos
|
||||||
|
|
||||||
# Specify custom flag completion
|
# Specify custom flag completion
|
||||||
|
|
||||||
Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specify
|
As for nouns, Cobra provides two ways of defining dynamic completion of flags. Note that both these methods can be used along-side each other as long as they are not both used for the same flag.
|
||||||
a custom flag completion function with cobra.BashCompCustom:
|
|
||||||
|
**Note**: *Custom Completions written in Go* will automatically work for other shell-completion scripts (e.g., Fish shell), while *Custom Completions written in Bash* will only work for Bash shell-completion. It is therefore recommended to use *Custom Completions written in Go*.
|
||||||
|
|
||||||
|
## 1. Custom completions of flags written in Go
|
||||||
|
|
||||||
|
To provide a Go function that Cobra will execute when it needs the list of completion choices for a flag, you must register the function in the following manner:
|
||||||
|
|
||||||
|
```go
|
||||||
|
flagName := "output"
|
||||||
|
cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
return []string{"json", "table", "yaml"}, cobra.ShellCompDirectiveDefault
|
||||||
|
})
|
||||||
|
```
|
||||||
|
Notice that calling `RegisterFlagCompletionFunc()` is done through the `command` with which the flag is associated. In our example this dynamic completion will give results like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# helm status --output [tab][tab]
|
||||||
|
json table yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
You can also easily debug your Go completion code for flags:
|
||||||
|
```bash
|
||||||
|
# helm __complete status --output ""
|
||||||
|
json
|
||||||
|
table
|
||||||
|
yaml
|
||||||
|
:4
|
||||||
|
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||||
|
```
|
||||||
|
***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned in the above section.
|
||||||
|
|
||||||
|
## 2. Custom completions of flags written in Bash
|
||||||
|
|
||||||
|
Alternatively, you can use bash code for flag custom completion. Similar to the filename
|
||||||
|
completion and filtering using `cobra.BashCompFilenameExt`, you can specify
|
||||||
|
a custom flag completion bash function with `cobra.BashCompCustom`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
annotation := make(map[string][]string)
|
annotation := make(map[string][]string)
|
||||||
|
@ -226,7 +353,7 @@ a custom flag completion function with cobra.BashCompCustom:
|
||||||
cmd.Flags().AddFlag(flag)
|
cmd.Flags().AddFlag(flag)
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition add the `__handle_namespace_flag` implementation in the `BashCompletionFunction`
|
In addition add the `__kubectl_get_namespaces` implementation in the `BashCompletionFunction`
|
||||||
value, e.g.:
|
value, e.g.:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
7
vendor/github.com/spf13/cobra/command.go
generated
vendored
7
vendor/github.com/spf13/cobra/command.go
generated
vendored
|
@ -57,6 +57,10 @@ type Command struct {
|
||||||
|
|
||||||
// ValidArgs is list of all valid non-flag arguments that are accepted in bash completions
|
// ValidArgs is list of all valid non-flag arguments that are accepted in bash completions
|
||||||
ValidArgs []string
|
ValidArgs []string
|
||||||
|
// ValidArgsFunction is an optional function that provides valid non-flag arguments for bash completion.
|
||||||
|
// It is a dynamic version of using ValidArgs.
|
||||||
|
// Only one of ValidArgs and ValidArgsFunction can be used for a command.
|
||||||
|
ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
||||||
|
|
||||||
// Expected arguments
|
// Expected arguments
|
||||||
Args PositionalArgs
|
Args PositionalArgs
|
||||||
|
@ -911,6 +915,9 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||||
args = os.Args[1:]
|
args = os.Args[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize the hidden command to be used for bash completion
|
||||||
|
c.initCompleteCmd(args)
|
||||||
|
|
||||||
var flags []string
|
var flags []string
|
||||||
if c.TraverseChildren {
|
if c.TraverseChildren {
|
||||||
cmd, flags, err = c.Traverse(args)
|
cmd, flags, err = c.Traverse(args)
|
||||||
|
|
384
vendor/github.com/spf13/cobra/custom_completions.go
generated
vendored
Normal file
384
vendor/github.com/spf13/cobra/custom_completions.go
generated
vendored
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ShellCompRequestCmd is the name of the hidden command that is used to request
|
||||||
|
// completion results from the program. It is used by the shell completion scripts.
|
||||||
|
ShellCompRequestCmd = "__complete"
|
||||||
|
// ShellCompNoDescRequestCmd is the name of the hidden command that is used to request
|
||||||
|
// completion results without their description. It is used by the shell completion scripts.
|
||||||
|
ShellCompNoDescRequestCmd = "__completeNoDesc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global map of flag completion functions.
|
||||||
|
var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){}
|
||||||
|
|
||||||
|
// ShellCompDirective is a bit map representing the different behaviors the shell
|
||||||
|
// can be instructed to have once completions have been provided.
|
||||||
|
type ShellCompDirective int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ShellCompDirectiveError indicates an error occurred and completions should be ignored.
|
||||||
|
ShellCompDirectiveError ShellCompDirective = 1 << iota
|
||||||
|
|
||||||
|
// ShellCompDirectiveNoSpace indicates that the shell should not add a space
|
||||||
|
// after the completion even if there is a single completion provided.
|
||||||
|
ShellCompDirectiveNoSpace
|
||||||
|
|
||||||
|
// ShellCompDirectiveNoFileComp indicates that the shell should not provide
|
||||||
|
// file completion even when no completion is provided.
|
||||||
|
// This currently does not work for zsh or bash < 4
|
||||||
|
ShellCompDirectiveNoFileComp
|
||||||
|
|
||||||
|
// ShellCompDirectiveDefault indicates to let the shell perform its default
|
||||||
|
// behavior after completions have been provided.
|
||||||
|
ShellCompDirectiveDefault ShellCompDirective = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
|
||||||
|
func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
|
||||||
|
flag := c.Flag(flagName)
|
||||||
|
if flag == nil {
|
||||||
|
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName)
|
||||||
|
}
|
||||||
|
if _, exists := flagCompletionFunctions[flag]; exists {
|
||||||
|
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
|
||||||
|
}
|
||||||
|
flagCompletionFunctions[flag] = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a string listing the different directive enabled in the specified parameter
|
||||||
|
func (d ShellCompDirective) string() string {
|
||||||
|
var directives []string
|
||||||
|
if d&ShellCompDirectiveError != 0 {
|
||||||
|
directives = append(directives, "ShellCompDirectiveError")
|
||||||
|
}
|
||||||
|
if d&ShellCompDirectiveNoSpace != 0 {
|
||||||
|
directives = append(directives, "ShellCompDirectiveNoSpace")
|
||||||
|
}
|
||||||
|
if d&ShellCompDirectiveNoFileComp != 0 {
|
||||||
|
directives = append(directives, "ShellCompDirectiveNoFileComp")
|
||||||
|
}
|
||||||
|
if len(directives) == 0 {
|
||||||
|
directives = append(directives, "ShellCompDirectiveDefault")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d > ShellCompDirectiveError+ShellCompDirectiveNoSpace+ShellCompDirectiveNoFileComp {
|
||||||
|
return fmt.Sprintf("ERROR: unexpected ShellCompDirective value: %d", d)
|
||||||
|
}
|
||||||
|
return strings.Join(directives, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a special hidden command that can be used to request custom completions.
|
||||||
|
func (c *Command) initCompleteCmd(args []string) {
|
||||||
|
completeCmd := &Command{
|
||||||
|
Use: fmt.Sprintf("%s [command-line]", ShellCompRequestCmd),
|
||||||
|
Aliases: []string{ShellCompNoDescRequestCmd},
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
Hidden: true,
|
||||||
|
DisableFlagParsing: true,
|
||||||
|
Args: MinimumNArgs(1),
|
||||||
|
Short: "Request shell completion choices for the specified command-line",
|
||||||
|
Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s",
|
||||||
|
"to request completion choices for the specified command-line.", ShellCompRequestCmd),
|
||||||
|
Run: func(cmd *Command, args []string) {
|
||||||
|
finalCmd, completions, directive, err := cmd.getCompletions(args)
|
||||||
|
if err != nil {
|
||||||
|
CompErrorln(err.Error())
|
||||||
|
// Keep going for multiple reasons:
|
||||||
|
// 1- There could be some valid completions even though there was an error
|
||||||
|
// 2- Even without completions, we need to print the directive
|
||||||
|
}
|
||||||
|
|
||||||
|
noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd)
|
||||||
|
for _, comp := range completions {
|
||||||
|
if noDescriptions {
|
||||||
|
// Remove any description that may be included following a tab character.
|
||||||
|
comp = strings.Split(comp, "\t")[0]
|
||||||
|
}
|
||||||
|
// Print each possible completion to stdout for the completion script to consume.
|
||||||
|
fmt.Fprintln(finalCmd.OutOrStdout(), comp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if directive > ShellCompDirectiveError+ShellCompDirectiveNoSpace+ShellCompDirectiveNoFileComp {
|
||||||
|
directive = ShellCompDirectiveDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// As the last printout, print the completion directive for the completion script to parse.
|
||||||
|
// The directive integer must be that last character following a single colon (:).
|
||||||
|
// The completion script expects :<directive>
|
||||||
|
fmt.Fprintf(finalCmd.OutOrStdout(), ":%d\n", directive)
|
||||||
|
|
||||||
|
// Print some helpful info to stderr for the user to understand.
|
||||||
|
// Output from stderr must be ignored by the completion script.
|
||||||
|
fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c.AddCommand(completeCmd)
|
||||||
|
subCmd, _, err := c.Find(args)
|
||||||
|
if err != nil || subCmd.Name() != ShellCompRequestCmd {
|
||||||
|
// Only create this special command if it is actually being called.
|
||||||
|
// This reduces possible side-effects of creating such a command;
|
||||||
|
// for example, having this command would cause problems to a
|
||||||
|
// cobra program that only consists of the root command, since this
|
||||||
|
// command would cause the root command to suddenly have a subcommand.
|
||||||
|
c.RemoveCommand(completeCmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) {
|
||||||
|
var completions []string
|
||||||
|
|
||||||
|
// The last argument, which is not completely typed by the user,
|
||||||
|
// should not be part of the list of arguments
|
||||||
|
toComplete := args[len(args)-1]
|
||||||
|
trimmedArgs := args[:len(args)-1]
|
||||||
|
|
||||||
|
// Find the real command for which completion must be performed
|
||||||
|
finalCmd, finalArgs, err := c.Root().Find(trimmedArgs)
|
||||||
|
if err != nil {
|
||||||
|
// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
|
||||||
|
return c, completions, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When doing completion of a flag name, as soon as an argument starts with
|
||||||
|
// a '-' we know it is a flag. We cannot use isFlagArg() here as it requires
|
||||||
|
// the flag to be complete
|
||||||
|
if len(toComplete) > 0 && toComplete[0] == '-' && !strings.Contains(toComplete, "=") {
|
||||||
|
// We are completing a flag name
|
||||||
|
finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||||
|
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
|
||||||
|
})
|
||||||
|
finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||||
|
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
|
||||||
|
})
|
||||||
|
|
||||||
|
directive := ShellCompDirectiveDefault
|
||||||
|
if len(completions) > 0 {
|
||||||
|
if strings.HasSuffix(completions[0], "=") {
|
||||||
|
directive = ShellCompDirectiveNoSpace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return finalCmd, completions, directive, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var flag *pflag.Flag
|
||||||
|
if !finalCmd.DisableFlagParsing {
|
||||||
|
// We only do flag completion if we are allowed to parse flags
|
||||||
|
// This is important for commands which have requested to do their own flag completion.
|
||||||
|
flag, finalArgs, toComplete, err = checkIfFlagCompletion(finalCmd, finalArgs, toComplete)
|
||||||
|
if err != nil {
|
||||||
|
// Error while attempting to parse flags
|
||||||
|
return finalCmd, completions, ShellCompDirectiveDefault, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag == nil {
|
||||||
|
// Complete subcommand names
|
||||||
|
for _, subCmd := range finalCmd.Commands() {
|
||||||
|
if subCmd.IsAvailableCommand() && strings.HasPrefix(subCmd.Name(), toComplete) {
|
||||||
|
completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(finalCmd.ValidArgs) > 0 {
|
||||||
|
// Always complete ValidArgs, even if we are completing a subcommand name.
|
||||||
|
// This is for commands that have both subcommands and ValidArgs.
|
||||||
|
for _, validArg := range finalCmd.ValidArgs {
|
||||||
|
if strings.HasPrefix(validArg, toComplete) {
|
||||||
|
completions = append(completions, validArg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are ValidArgs specified (even if they don't match), we stop completion.
|
||||||
|
// Only one of ValidArgs or ValidArgsFunction can be used for a single command.
|
||||||
|
return finalCmd, completions, ShellCompDirectiveNoFileComp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always let the logic continue so as to add any ValidArgsFunction completions,
|
||||||
|
// even if we already found sub-commands.
|
||||||
|
// This is for commands that have subcommands but also specify a ValidArgsFunction.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the flags and extract the arguments to prepare for calling the completion function
|
||||||
|
if err = finalCmd.ParseFlags(finalArgs); err != nil {
|
||||||
|
return finalCmd, completions, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only remove the flags from the arguments if DisableFlagParsing is not set.
|
||||||
|
// This is important for commands which have requested to do their own flag completion.
|
||||||
|
if !finalCmd.DisableFlagParsing {
|
||||||
|
finalArgs = finalCmd.Flags().Args()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the completion function for the flag or command
|
||||||
|
var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
||||||
|
if flag != nil {
|
||||||
|
completionFn = flagCompletionFunctions[flag]
|
||||||
|
} else {
|
||||||
|
completionFn = finalCmd.ValidArgsFunction
|
||||||
|
}
|
||||||
|
if completionFn == nil {
|
||||||
|
// Go custom completion not supported/needed for this flag or command
|
||||||
|
return finalCmd, completions, ShellCompDirectiveDefault, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the registered completion function to get the completions
|
||||||
|
comps, directive := completionFn(finalCmd, finalArgs, toComplete)
|
||||||
|
completions = append(completions, comps...)
|
||||||
|
return finalCmd, completions, directive, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string {
|
||||||
|
if nonCompletableFlag(flag) {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var completions []string
|
||||||
|
flagName := "--" + flag.Name
|
||||||
|
if strings.HasPrefix(flagName, toComplete) {
|
||||||
|
// Flag without the =
|
||||||
|
completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
|
||||||
|
|
||||||
|
if len(flag.NoOptDefVal) == 0 {
|
||||||
|
// Flag requires a value, so it can be suffixed with =
|
||||||
|
flagName += "="
|
||||||
|
completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flagName = "-" + flag.Shorthand
|
||||||
|
if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) {
|
||||||
|
completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
return completions
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) {
|
||||||
|
var flagName string
|
||||||
|
trimmedArgs := args
|
||||||
|
flagWithEqual := false
|
||||||
|
if isFlagArg(lastArg) {
|
||||||
|
if index := strings.Index(lastArg, "="); index >= 0 {
|
||||||
|
flagName = strings.TrimLeft(lastArg[:index], "-")
|
||||||
|
lastArg = lastArg[index+1:]
|
||||||
|
flagWithEqual = true
|
||||||
|
} else {
|
||||||
|
return nil, nil, "", errors.New("Unexpected completion request for flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(flagName) == 0 {
|
||||||
|
if len(args) > 0 {
|
||||||
|
prevArg := args[len(args)-1]
|
||||||
|
if isFlagArg(prevArg) {
|
||||||
|
// Only consider the case where the flag does not contain an =.
|
||||||
|
// If the flag contains an = it means it has already been fully processed,
|
||||||
|
// so we don't need to deal with it here.
|
||||||
|
if index := strings.Index(prevArg, "="); index < 0 {
|
||||||
|
flagName = strings.TrimLeft(prevArg, "-")
|
||||||
|
|
||||||
|
// Remove the uncompleted flag or else there could be an error created
|
||||||
|
// for an invalid value for that flag
|
||||||
|
trimmedArgs = args[:len(args)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(flagName) == 0 {
|
||||||
|
// Not doing flag completion
|
||||||
|
return nil, trimmedArgs, lastArg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
flag := findFlag(finalCmd, flagName)
|
||||||
|
if flag == nil {
|
||||||
|
// Flag not supported by this command, nothing to complete
|
||||||
|
err := fmt.Errorf("Subcommand '%s' does not support flag '%s'", finalCmd.Name(), flagName)
|
||||||
|
return nil, nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !flagWithEqual {
|
||||||
|
if len(flag.NoOptDefVal) != 0 {
|
||||||
|
// We had assumed dealing with a two-word flag but the flag is a boolean flag.
|
||||||
|
// In that case, there is no value following it, so we are not really doing flag completion.
|
||||||
|
// Reset everything to do noun completion.
|
||||||
|
trimmedArgs = args
|
||||||
|
flag = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag, trimmedArgs, lastArg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findFlag(cmd *Command, name string) *pflag.Flag {
|
||||||
|
flagSet := cmd.Flags()
|
||||||
|
if len(name) == 1 {
|
||||||
|
// First convert the short flag into a long flag
|
||||||
|
// as the cmd.Flag() search only accepts long flags
|
||||||
|
if short := flagSet.ShorthandLookup(name); short != nil {
|
||||||
|
name = short.Name
|
||||||
|
} else {
|
||||||
|
set := cmd.InheritedFlags()
|
||||||
|
if short = set.ShorthandLookup(name); short != nil {
|
||||||
|
name = short.Name
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmd.Flag(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompDebug prints the specified string to the same file as where the
|
||||||
|
// completion script prints its logs.
|
||||||
|
// Note that completion printouts should never be on stdout as they would
|
||||||
|
// be wrongly interpreted as actual completion choices by the completion script.
|
||||||
|
func CompDebug(msg string, printToStdErr bool) {
|
||||||
|
msg = fmt.Sprintf("[Debug] %s", msg)
|
||||||
|
|
||||||
|
// Such logs are only printed when the user has set the environment
|
||||||
|
// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
|
||||||
|
if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" {
|
||||||
|
f, err := os.OpenFile(path,
|
||||||
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err == nil {
|
||||||
|
defer f.Close()
|
||||||
|
f.WriteString(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if printToStdErr {
|
||||||
|
// Must print to stderr for this not to be read by the completion script.
|
||||||
|
fmt.Fprintf(os.Stderr, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompDebugln prints the specified string with a newline at the end
|
||||||
|
// to the same file as where the completion script prints its logs.
|
||||||
|
// Such logs are only printed when the user has set the environment
|
||||||
|
// variable BASH_COMP_DEBUG_FILE to the path of some file to be used.
|
||||||
|
func CompDebugln(msg string, printToStdErr bool) {
|
||||||
|
CompDebug(fmt.Sprintf("%s\n", msg), printToStdErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompError prints the specified completion message to stderr.
|
||||||
|
func CompError(msg string) {
|
||||||
|
msg = fmt.Sprintf("[Error] %s", msg)
|
||||||
|
CompDebug(msg, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompErrorln prints the specified completion message to stderr with a newline at the end.
|
||||||
|
func CompErrorln(msg string) {
|
||||||
|
CompError(fmt.Sprintf("%s\n", msg))
|
||||||
|
}
|
172
vendor/github.com/spf13/cobra/fish_completions.go
generated
vendored
Normal file
172
vendor/github.com/spf13/cobra/fish_completions.go
generated
vendored
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package cobra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
|
||||||
|
compCmd := ShellCompRequestCmd
|
||||||
|
if !includeDesc {
|
||||||
|
compCmd = ShellCompNoDescRequestCmd
|
||||||
|
}
|
||||||
|
buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
|
||||||
|
buf.WriteString(fmt.Sprintf(`
|
||||||
|
function __%[1]s_debug
|
||||||
|
set file "$BASH_COMP_DEBUG_FILE"
|
||||||
|
if test -n "$file"
|
||||||
|
echo "$argv" >> $file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function __%[1]s_perform_completion
|
||||||
|
__%[1]s_debug "Starting __%[1]s_perform_completion with: $argv"
|
||||||
|
|
||||||
|
set args (string split -- " " "$argv")
|
||||||
|
set lastArg "$args[-1]"
|
||||||
|
|
||||||
|
__%[1]s_debug "args: $args"
|
||||||
|
__%[1]s_debug "last arg: $lastArg"
|
||||||
|
|
||||||
|
set emptyArg ""
|
||||||
|
if test -z "$lastArg"
|
||||||
|
__%[1]s_debug "Setting emptyArg"
|
||||||
|
set emptyArg \"\"
|
||||||
|
end
|
||||||
|
__%[1]s_debug "emptyArg: $emptyArg"
|
||||||
|
|
||||||
|
set requestComp "$args[1] %[2]s $args[2..-1] $emptyArg"
|
||||||
|
__%[1]s_debug "Calling $requestComp"
|
||||||
|
|
||||||
|
set results (eval $requestComp 2> /dev/null)
|
||||||
|
set comps $results[1..-2]
|
||||||
|
set directiveLine $results[-1]
|
||||||
|
|
||||||
|
# For Fish, when completing a flag with an = (e.g., <program> -n=<TAB>)
|
||||||
|
# completions must be prefixed with the flag
|
||||||
|
set flagPrefix (string match -r -- '-.*=' "$lastArg")
|
||||||
|
|
||||||
|
__%[1]s_debug "Comps: $comps"
|
||||||
|
__%[1]s_debug "DirectiveLine: $directiveLine"
|
||||||
|
__%[1]s_debug "flagPrefix: $flagPrefix"
|
||||||
|
|
||||||
|
for comp in $comps
|
||||||
|
printf "%%s%%s\n" "$flagPrefix" "$comp"
|
||||||
|
end
|
||||||
|
|
||||||
|
printf "%%s\n" "$directiveLine"
|
||||||
|
end
|
||||||
|
|
||||||
|
# This function does three things:
|
||||||
|
# 1- Obtain the completions and store them in the global __%[1]s_comp_results
|
||||||
|
# 2- Set the __%[1]s_comp_do_file_comp flag if file completion should be performed
|
||||||
|
# and unset it otherwise
|
||||||
|
# 3- Return true if the completion results are not empty
|
||||||
|
function __%[1]s_prepare_completions
|
||||||
|
# Start fresh
|
||||||
|
set --erase __%[1]s_comp_do_file_comp
|
||||||
|
set --erase __%[1]s_comp_results
|
||||||
|
|
||||||
|
# Check if the command-line is already provided. This is useful for testing.
|
||||||
|
if not set --query __%[1]s_comp_commandLine
|
||||||
|
set __%[1]s_comp_commandLine (commandline)
|
||||||
|
end
|
||||||
|
__%[1]s_debug "commandLine is: $__%[1]s_comp_commandLine"
|
||||||
|
|
||||||
|
set results (__%[1]s_perform_completion "$__%[1]s_comp_commandLine")
|
||||||
|
set --erase __%[1]s_comp_commandLine
|
||||||
|
__%[1]s_debug "Completion results: $results"
|
||||||
|
|
||||||
|
if test -z "$results"
|
||||||
|
__%[1]s_debug "No completion, probably due to a failure"
|
||||||
|
# Might as well do file completion, in case it helps
|
||||||
|
set --global __%[1]s_comp_do_file_comp 1
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
set directive (string sub --start 2 $results[-1])
|
||||||
|
set --global __%[1]s_comp_results $results[1..-2]
|
||||||
|
|
||||||
|
__%[1]s_debug "Completions are: $__%[1]s_comp_results"
|
||||||
|
__%[1]s_debug "Directive is: $directive"
|
||||||
|
|
||||||
|
if test -z "$directive"
|
||||||
|
set directive 0
|
||||||
|
end
|
||||||
|
|
||||||
|
set compErr (math (math --scale 0 $directive / %[3]d) %% 2)
|
||||||
|
if test $compErr -eq 1
|
||||||
|
__%[1]s_debug "Received error directive: aborting."
|
||||||
|
# Might as well do file completion, in case it helps
|
||||||
|
set --global __%[1]s_comp_do_file_comp 1
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
set nospace (math (math --scale 0 $directive / %[4]d) %% 2)
|
||||||
|
set nofiles (math (math --scale 0 $directive / %[5]d) %% 2)
|
||||||
|
|
||||||
|
__%[1]s_debug "nospace: $nospace, nofiles: $nofiles"
|
||||||
|
|
||||||
|
# Important not to quote the variable for count to work
|
||||||
|
set numComps (count $__%[1]s_comp_results)
|
||||||
|
__%[1]s_debug "numComps: $numComps"
|
||||||
|
|
||||||
|
if test $numComps -eq 1; and test $nospace -ne 0
|
||||||
|
# To support the "nospace" directive we trick the shell
|
||||||
|
# by outputting an extra, longer completion.
|
||||||
|
__%[1]s_debug "Adding second completion to perform nospace directive"
|
||||||
|
set --append __%[1]s_comp_results $__%[1]s_comp_results[1].
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $numComps -eq 0; and test $nofiles -eq 0
|
||||||
|
__%[1]s_debug "Requesting file completion"
|
||||||
|
set --global __%[1]s_comp_do_file_comp 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# If we don't want file completion, we must return true even if there
|
||||||
|
# are no completions found. This is because fish will perform the last
|
||||||
|
# completion command, even if its condition is false, if no other
|
||||||
|
# completion command was triggered
|
||||||
|
return (not set --query __%[1]s_comp_do_file_comp)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove any pre-existing completions for the program since we will be handling all of them
|
||||||
|
# TODO this cleanup is not sufficient. Fish completions are only loaded once the user triggers
|
||||||
|
# them, so the below deletion will not work as it is run too early. What else can we do?
|
||||||
|
complete -c %[1]s -e
|
||||||
|
|
||||||
|
# The order in which the below two lines are defined is very important so that __%[1]s_prepare_completions
|
||||||
|
# is called first. It is __%[1]s_prepare_completions that sets up the __%[1]s_comp_do_file_comp variable.
|
||||||
|
#
|
||||||
|
# This completion will be run second as complete commands are added FILO.
|
||||||
|
# It triggers file completion choices when __%[1]s_comp_do_file_comp is set.
|
||||||
|
complete -c %[1]s -n 'set --query __%[1]s_comp_do_file_comp'
|
||||||
|
|
||||||
|
# This completion will be run first as complete commands are added FILO.
|
||||||
|
# The call to __%[1]s_prepare_completions will setup both __%[1]s_comp_results abd __%[1]s_comp_do_file_comp.
|
||||||
|
# It provides the program's completion choices.
|
||||||
|
complete -c %[1]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
|
||||||
|
|
||||||
|
`, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenFishCompletion generates fish completion file and writes to the passed writer.
|
||||||
|
func (c *Command) GenFishCompletion(w io.Writer, includeDesc bool) error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
genFishComp(buf, c.Name(), includeDesc)
|
||||||
|
_, err := buf.WriteTo(w)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenFishCompletionFile generates fish completion file.
|
||||||
|
func (c *Command) GenFishCompletionFile(filename string, includeDesc bool) error {
|
||||||
|
outFile, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
return c.GenFishCompletion(outFile, includeDesc)
|
||||||
|
}
|
7
vendor/github.com/spf13/cobra/fish_completions.md
generated
vendored
Normal file
7
vendor/github.com/spf13/cobra/fish_completions.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
## Generating Fish Completions for your own cobra.Command
|
||||||
|
|
||||||
|
Cobra supports native Fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users.
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
* Custom completions implemented using the `ValidArgsFunction` and `RegisterFlagCompletionFunc()` are supported automatically but the ones implemented in Bash scripting are not.
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
@ -193,7 +193,7 @@ github.com/spf13/afero
|
||||||
github.com/spf13/afero/mem
|
github.com/spf13/afero/mem
|
||||||
# github.com/spf13/cast v1.3.0
|
# github.com/spf13/cast v1.3.0
|
||||||
github.com/spf13/cast
|
github.com/spf13/cast
|
||||||
# github.com/spf13/cobra v0.0.7
|
# github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/cobra
|
github.com/spf13/cobra
|
||||||
# github.com/spf13/jwalterweatherman v1.1.0
|
# github.com/spf13/jwalterweatherman v1.1.0
|
||||||
github.com/spf13/jwalterweatherman
|
github.com/spf13/jwalterweatherman
|
||||||
|
|
Loading…
Reference in a new issue