Merge remote-tracking branch 'spf13/master' into zsh-completion

This commit is contained in:
Haim Ashkenazi 2018-06-02 06:39:42 +03:00
commit 13b42c0440
9 changed files with 155 additions and 8 deletions

View file

@ -13,7 +13,7 @@ base: &base
name: "All Commands" name: "All Commands"
command: | command: |
mkdir -p bin mkdir -p bin
curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.3/shellcheck curl -Lso bin/shellcheck https://github.com/caarlos0/shellcheck-docker/releases/download/v0.4.6/shellcheck
chmod +x bin/shellcheck chmod +x bin/shellcheck
go get -t -v ./... go get -t -v ./...
PATH=$PATH:$PWD/bin go test -v ./... PATH=$PATH:$PWD/bin go test -v ./...

View file

@ -251,6 +251,14 @@ __%[1]s_handle_word()
__%[1]s_handle_command __%[1]s_handle_command
elif [[ $c -eq 0 ]]; then elif [[ $c -eq 0 ]]; then
__%[1]s_handle_command __%[1]s_handle_command
elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then
# aliashash variable is an associative array which is only supported in bash > 3.
if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then
words[c]=${aliashash[${words[c]}]}
__%[1]s_handle_command
else
__%[1]s_handle_noun
fi
else else
__%[1]s_handle_noun __%[1]s_handle_noun
fi fi
@ -266,6 +274,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
buf.WriteString(fmt.Sprintf(`{ buf.WriteString(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 || :
if declare -F _init_completion >/dev/null 2>&1; then if declare -F _init_completion >/dev/null 2>&1; then
_init_completion -s || return _init_completion -s || return
else else
@ -305,6 +314,7 @@ func writeCommands(buf *bytes.Buffer, cmd *Command) {
continue continue
} }
buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name())) buf.WriteString(fmt.Sprintf(" commands+=(%q)\n", c.Name()))
writeCmdAliases(buf, c)
} }
buf.WriteString("\n") buf.WriteString("\n")
} }
@ -443,6 +453,21 @@ func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) {
} }
} }
func writeCmdAliases(buf *bytes.Buffer, cmd *Command) {
if len(cmd.Aliases) == 0 {
return
}
sort.Sort(sort.StringSlice(cmd.Aliases))
buf.WriteString(fmt.Sprint(` if [[ -z "${BASH_VERSION}" || "${BASH_VERSINFO[0]}" -gt 3 ]]; then`, "\n"))
for _, value := range cmd.Aliases {
buf.WriteString(fmt.Sprintf(" command_aliases+=(%q)\n", value))
buf.WriteString(fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name()))
}
buf.WriteString(` fi`)
buf.WriteString("\n")
}
func writeArgAliases(buf *bytes.Buffer, cmd *Command) { func writeArgAliases(buf *bytes.Buffer, cmd *Command) {
buf.WriteString(" noun_aliases=()\n") buf.WriteString(" noun_aliases=()\n")
sort.Sort(sort.StringSlice(cmd.ArgAliases)) sort.Sort(sort.StringSlice(cmd.ArgAliases))
@ -469,6 +494,10 @@ func gen(buf *bytes.Buffer, cmd *Command) {
} }
buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName)) buf.WriteString(fmt.Sprintf(" last_command=%q\n", commandName))
buf.WriteString("\n")
buf.WriteString(" command_aliases=()\n")
buf.WriteString("\n")
writeCommands(buf, cmd) writeCommands(buf, cmd)
writeFlags(buf, cmd) writeFlags(buf, cmd)
writeRequiredFlag(buf, cmd) writeRequiredFlag(buf, cmd)

View file

@ -1,5 +1,40 @@
# Generating Bash Completions For Your Own cobra.Command # Generating Bash Completions For Your Own cobra.Command
If you are using the generator you can create a completion command by running
```bash
cobra add completion
```
Update the help text show how to install the bash_completion Linux show here [Kubectl docs show mac options](https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion)
Writing the shell script to stdout allows the most flexible use.
```go
// completionCmd represents the completion command
var completionCmd = &cobra.Command{
Use: "completion",
Short: "Generates bash completion scripts",
Long: `To load completion run
. <(bitbucket completion)
To configure your bash shell to load completions for each session add to your bashrc
# ~/.bashrc or ~/.profile
. <(bitbucket completion)
`,
Run: func(cmd *cobra.Command, args []string) {
rootCmd.GenBashCompletion(os.Stdout);
},
}
```
**Note:** The cobra generator may include messages printed to stdout for example if the config file is loaded, this will break the auto complete script
## Example from kubectl
Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows: Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows:
```go ```go
@ -181,7 +216,7 @@ a custom flag completion function with cobra.BashCompCustom:
```go ```go
annotation := make(map[string][]string) annotation := make(map[string][]string)
annotation[cobra.BashCompFilenameExt] = []string{"__kubectl_get_namespaces"} annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"}
flag := &pflag.Flag{ flag := &pflag.Flag{
Name: "namespace", Name: "namespace",

View file

@ -176,13 +176,13 @@ func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) { func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) {
flags := command.NonInheritedFlags() flags := command.NonInheritedFlags()
if flags.HasFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS\n") buf.WriteString("# OPTIONS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") buf.WriteString("\n")
} }
flags = command.InheritedFlags() flags = command.InheritedFlags()
if flags.HasFlags() { if flags.HasAvailableFlags() {
buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n") buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(buf, flags) manPrintFlags(buf, flags)
buf.WriteString("\n") buf.WriteString("\n")

View file

@ -47,6 +47,42 @@ func TestGenManDoc(t *testing.T) {
checkStringContains(t, output, translate("Auto generated")) checkStringContains(t, output, translate("Auto generated"))
} }
func TestGenManNoHiddenParents(t *testing.T) {
header := &GenManHeader{
Title: "Project",
Section: "2",
}
// We generate on a subcommand so we have both subcommands and parents
for _, name := range []string{"rootflag", "strtwo"} {
f := rootCmd.PersistentFlags().Lookup(name)
f.Hidden = true
defer func() { f.Hidden = false }()
}
buf := new(bytes.Buffer)
if err := GenMan(echoCmd, header, buf); err != nil {
t.Fatal(err)
}
output := buf.String()
// Make sure parent has - in CommandPath() in SEE ALSO:
parentPath := echoCmd.Parent().CommandPath()
dashParentPath := strings.Replace(parentPath, " ", "-", -1)
expected := translate(dashParentPath)
expected = expected + "(" + header.Section + ")"
checkStringContains(t, output, expected)
checkStringContains(t, output, translate(echoCmd.Name()))
checkStringContains(t, output, translate(echoCmd.Name()))
checkStringContains(t, output, "boolone")
checkStringOmits(t, output, "rootflag")
checkStringContains(t, output, translate(rootCmd.Name()))
checkStringContains(t, output, translate(echoSubCmd.Name()))
checkStringOmits(t, output, translate(deprecatedCmd.Name()))
checkStringContains(t, output, translate("Auto generated"))
checkStringOmits(t, output, "OPTIONS INHERITED FROM PARENT COMMANDS")
}
func TestGenManNoGenTag(t *testing.T) { func TestGenManNoGenTag(t *testing.T) {
echoCmd.DisableAutoGenTag = true echoCmd.DisableAutoGenTag = true
defer func() { echoCmd.DisableAutoGenTag = false }() defer func() { echoCmd.DisableAutoGenTag = false }()

View file

@ -29,7 +29,7 @@ import (
func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error { func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.SetOutput(buf) flags.SetOutput(buf)
if flags.HasFlags() { if flags.HasAvailableFlags() {
buf.WriteString("### Options\n\n```\n") buf.WriteString("### Options\n\n```\n")
flags.PrintDefaults() flags.PrintDefaults()
buf.WriteString("```\n\n") buf.WriteString("```\n\n")
@ -37,7 +37,7 @@ func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
parentFlags := cmd.InheritedFlags() parentFlags := cmd.InheritedFlags()
parentFlags.SetOutput(buf) parentFlags.SetOutput(buf)
if parentFlags.HasFlags() { if parentFlags.HasAvailableFlags() {
buf.WriteString("### Options inherited from parent commands\n\n```\n") buf.WriteString("### Options inherited from parent commands\n\n```\n")
parentFlags.PrintDefaults() parentFlags.PrintDefaults()
buf.WriteString("```\n\n") buf.WriteString("```\n\n")

View file

@ -25,6 +25,30 @@ func TestGenMdDoc(t *testing.T) {
checkStringContains(t, output, rootCmd.Short) checkStringContains(t, output, rootCmd.Short)
checkStringContains(t, output, echoSubCmd.Short) checkStringContains(t, output, echoSubCmd.Short)
checkStringOmits(t, output, deprecatedCmd.Short) checkStringOmits(t, output, deprecatedCmd.Short)
checkStringContains(t, output, "Options inherited from parent commands")
}
func TestGenMdNoHiddenParents(t *testing.T) {
// We generate on subcommand so we have both subcommands and parents.
for _, name := range []string{"rootflag", "strtwo"} {
f := rootCmd.PersistentFlags().Lookup(name)
f.Hidden = true
defer func() { f.Hidden = false }()
}
buf := new(bytes.Buffer)
if err := GenMarkdown(echoCmd, buf); err != nil {
t.Fatal(err)
}
output := buf.String()
checkStringContains(t, output, echoCmd.Long)
checkStringContains(t, output, echoCmd.Example)
checkStringContains(t, output, "boolone")
checkStringOmits(t, output, "rootflag")
checkStringContains(t, output, rootCmd.Short)
checkStringContains(t, output, echoSubCmd.Short)
checkStringOmits(t, output, deprecatedCmd.Short)
checkStringOmits(t, output, "Options inherited from parent commands")
} }
func TestGenMdNoTag(t *testing.T) { func TestGenMdNoTag(t *testing.T) {

View file

@ -29,7 +29,7 @@ import (
func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error { func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.SetOutput(buf) flags.SetOutput(buf)
if flags.HasFlags() { if flags.HasAvailableFlags() {
buf.WriteString("Options\n") buf.WriteString("Options\n")
buf.WriteString("~~~~~~~\n\n::\n\n") buf.WriteString("~~~~~~~\n\n::\n\n")
flags.PrintDefaults() flags.PrintDefaults()
@ -38,7 +38,7 @@ func printOptionsReST(buf *bytes.Buffer, cmd *cobra.Command, name string) error
parentFlags := cmd.InheritedFlags() parentFlags := cmd.InheritedFlags()
parentFlags.SetOutput(buf) parentFlags.SetOutput(buf)
if parentFlags.HasFlags() { if parentFlags.HasAvailableFlags() {
buf.WriteString("Options inherited from parent commands\n") buf.WriteString("Options inherited from parent commands\n")
buf.WriteString("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n") buf.WriteString("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n")
parentFlags.PrintDefaults() parentFlags.PrintDefaults()

View file

@ -27,6 +27,29 @@ func TestGenRSTDoc(t *testing.T) {
checkStringOmits(t, output, deprecatedCmd.Short) checkStringOmits(t, output, deprecatedCmd.Short)
} }
func TestGenRSTNoHiddenParents(t *testing.T) {
// We generate on a subcommand so we have both subcommands and parents
for _, name := range []string{"rootflag", "strtwo"} {
f := rootCmd.PersistentFlags().Lookup(name)
f.Hidden = true
defer func() { f.Hidden = false }()
}
buf := new(bytes.Buffer)
if err := GenReST(echoCmd, buf); err != nil {
t.Fatal(err)
}
output := buf.String()
checkStringContains(t, output, echoCmd.Long)
checkStringContains(t, output, echoCmd.Example)
checkStringContains(t, output, "boolone")
checkStringOmits(t, output, "rootflag")
checkStringContains(t, output, rootCmd.Short)
checkStringContains(t, output, echoSubCmd.Short)
checkStringOmits(t, output, deprecatedCmd.Short)
checkStringOmits(t, output, "Options inherited from parent commands")
}
func TestGenRSTNoTag(t *testing.T) { func TestGenRSTNoTag(t *testing.T) {
rootCmd.DisableAutoGenTag = true rootCmd.DisableAutoGenTag = true
defer func() { rootCmd.DisableAutoGenTag = false }() defer func() { rootCmd.DisableAutoGenTag = false }()