mirror of
https://github.com/spf13/cobra
synced 2025-05-05 04:47:22 +00:00
Make the default completion accessible
Fixes #1507 This allows a program to set whatever field it wants on the default completion command before calling rootCmd.Execute(). For example, cobra.CompletionCmd.Hidden = true // To make the command hidden or/and cobra.CompletionCmd.Use = "shellcomplete" // To rename the command Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>
This commit is contained in:
parent
f09e947637
commit
3ae090bcba
2 changed files with 204 additions and 170 deletions
204
completionCmd.go
Normal file
204
completionCmd.go
Normal file
|
@ -0,0 +1,204 @@
|
|||
package cobra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// Constants for the completion command
|
||||
compCmdName = "completion"
|
||||
compCmdNoDescFlagName = "no-descriptions"
|
||||
compCmdNoDescFlagDesc = "disable completion descriptions"
|
||||
compCmdNoDescFlagDefault = false
|
||||
shortDesc = "Generate the autocompletion script for %s"
|
||||
)
|
||||
|
||||
// CompletionOptions are the options to control shell completion
|
||||
type CompletionOptions struct {
|
||||
// DisableDefaultCmd prevents Cobra from creating a default 'completion' command
|
||||
DisableDefaultCmd bool
|
||||
// DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag
|
||||
// for shells that support completion descriptions
|
||||
DisableNoDescFlag bool
|
||||
// DisableDescriptions turns off all completion descriptions for shells
|
||||
// that support them
|
||||
DisableDescriptions bool
|
||||
}
|
||||
|
||||
var (
|
||||
CompletionCmd = &Command{
|
||||
Use: compCmdName,
|
||||
Short: "Generate the autocompletion script for the specified shell",
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
BashCompletionCmd = &Command{
|
||||
Use: "bash",
|
||||
Short: fmt.Sprintf(shortDesc, "bash"),
|
||||
Args: NoArgs,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
ZshCompletionCmd = &Command{
|
||||
Use: "zsh",
|
||||
Short: fmt.Sprintf(shortDesc, "zsh"),
|
||||
Args: NoArgs,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
FishCompletionCmd = &Command{
|
||||
Use: "fish",
|
||||
Short: fmt.Sprintf(shortDesc, "fish"),
|
||||
Args: NoArgs,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
PwshCompletionCmd = &Command{
|
||||
Use: "powershell",
|
||||
Short: fmt.Sprintf(shortDesc, "powershell"),
|
||||
Args: NoArgs,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
)
|
||||
|
||||
// initDefaultCompletionCmd adds a default 'completion' command to c.
|
||||
// This function will do nothing if any of the following is true:
|
||||
// 1- the feature has been explicitly disabled by the program,
|
||||
// 2- c has no subcommands (to avoid creating one),
|
||||
// 3- c already has a 'completion' command provided by the program.
|
||||
func (c *Command) initDefaultCompletionCmd() {
|
||||
if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, cmd := range c.commands {
|
||||
if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) {
|
||||
// A completion command is already available
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if CompletionCmd.Long == "" {
|
||||
CompletionCmd.Long = fmt.Sprintf(
|
||||
`Generate the autocompletion script for %[1]s for the specified shell.
|
||||
See each sub-command's help for details on how to use the generated script.`, c.Root().Name())
|
||||
}
|
||||
|
||||
c.RemoveCommand(CompletionCmd) // Tests can call this function multiple times in a row, so we must reset
|
||||
c.AddCommand(CompletionCmd)
|
||||
|
||||
out := c.OutOrStdout()
|
||||
noDesc := c.CompletionOptions.DisableDescriptions
|
||||
haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
|
||||
|
||||
bash := BashCompletionCmd
|
||||
if bash.Long == "" {
|
||||
bash.Long = fmt.Sprintf(
|
||||
`Generate the autocompletion script for the bash shell.
|
||||
|
||||
This script depends on the 'bash-completion' package.
|
||||
If it is not installed already, you can install it via your OS's package manager.
|
||||
|
||||
To load completions in your current shell session:
|
||||
$ source <(%[1]s %[2]s %[3]s)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
Linux:
|
||||
$ %[1]s %[2]s %[3]s > /etc/bash_completion.d/%[1]s
|
||||
MacOS:
|
||||
$ %[1]s %[2]s %[3]s > /usr/local/etc/bash_completion.d/%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.`,
|
||||
c.Root().Name(), CompletionCmd.Name(), BashCompletionCmd.Name())
|
||||
}
|
||||
bash.RunE = func(cmd *Command, args []string) error {
|
||||
return cmd.Root().GenBashCompletionV2(out, !noDesc)
|
||||
}
|
||||
|
||||
bash.ResetFlags() // Tests can call this function multiple times in a row, so we must reset
|
||||
if haveNoDescFlag {
|
||||
bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
zsh := ZshCompletionCmd
|
||||
if zsh.Long == "" {
|
||||
zsh.Long = fmt.Sprintf(
|
||||
`Generate the autocompletion script for the zsh shell.
|
||||
|
||||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
# Linux:
|
||||
$ %[1]s %[2]s %[3]s > "${fpath[1]}/_%[1]s"
|
||||
# macOS:
|
||||
$ %[1]s %[2]s %[3]s > /usr/local/share/zsh/site-functions/_%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.`,
|
||||
c.Root().Name(), CompletionCmd.Name(), ZshCompletionCmd.Name())
|
||||
}
|
||||
zsh.RunE = func(cmd *Command, args []string) error {
|
||||
if noDesc {
|
||||
return cmd.Root().GenZshCompletionNoDesc(out)
|
||||
}
|
||||
return cmd.Root().GenZshCompletion(out)
|
||||
}
|
||||
|
||||
zsh.ResetFlags() // Tests can call this function multiple times in a row, so we must reset
|
||||
if haveNoDescFlag {
|
||||
zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
fish := FishCompletionCmd
|
||||
if fish.Long == "" {
|
||||
fish.Long = fmt.Sprintf(
|
||||
`Generate the autocompletion script for the fish shell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
$ %[1]s %[2]s %[3]s | source
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
$ %[1]s %[2]s %[3]s > ~/.config/fish/completions/%[1]s.fish
|
||||
|
||||
You will need to start a new shell for this setup to take effect.`,
|
||||
c.Root().Name(), CompletionCmd.Name(), FishCompletionCmd.Name())
|
||||
}
|
||||
fish.RunE = func(cmd *Command, args []string) error {
|
||||
return cmd.Root().GenFishCompletion(out, !noDesc)
|
||||
}
|
||||
|
||||
fish.ResetFlags() // Tests can call this function multiple times in a row, so we must reset
|
||||
if haveNoDescFlag {
|
||||
fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
pwsh := PwshCompletionCmd
|
||||
if pwsh.Long == "" {
|
||||
pwsh.Long = fmt.Sprintf(
|
||||
`Generate the autocompletion script for powershell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
PS C:\> %[1]s %[2]s %[3]s | Out-String | Invoke-Expression
|
||||
|
||||
To load completions for every new session, add the output of the above command
|
||||
to your powershell profile.`,
|
||||
c.Root().Name(), CompletionCmd.Name(), PwshCompletionCmd.Name())
|
||||
}
|
||||
pwsh.RunE = func(cmd *Command, args []string) error {
|
||||
if noDesc {
|
||||
return cmd.Root().GenPowerShellCompletion(out)
|
||||
}
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
||||
}
|
||||
|
||||
pwsh.ResetFlags() // Tests can call this function multiple times in a row, so we must reset
|
||||
if haveNoDescFlag {
|
||||
pwsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
CompletionCmd.RemoveCommand(bash, zsh, fish, pwsh) // Tests can call this function multiple times in a row, so we must reset
|
||||
CompletionCmd.AddCommand(bash, zsh, fish, pwsh)
|
||||
}
|
170
completions.go
170
completions.go
|
@ -75,26 +75,6 @@ const (
|
|||
ShellCompDirectiveDefault ShellCompDirective = 0
|
||||
)
|
||||
|
||||
const (
|
||||
// Constants for the completion command
|
||||
compCmdName = "completion"
|
||||
compCmdNoDescFlagName = "no-descriptions"
|
||||
compCmdNoDescFlagDesc = "disable completion descriptions"
|
||||
compCmdNoDescFlagDefault = false
|
||||
)
|
||||
|
||||
// CompletionOptions are the options to control shell completion
|
||||
type CompletionOptions struct {
|
||||
// DisableDefaultCmd prevents Cobra from creating a default 'completion' command
|
||||
DisableDefaultCmd bool
|
||||
// DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag
|
||||
// for shells that support completion descriptions
|
||||
DisableNoDescFlag bool
|
||||
// DisableDescriptions turns off all completion descriptions for shells
|
||||
// that support them
|
||||
DisableDescriptions bool
|
||||
}
|
||||
|
||||
// NoFileCompletions can be used to disable file completion for commands that should
|
||||
// not trigger file completions.
|
||||
func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||
|
@ -578,156 +558,6 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
|
|||
return flag, trimmedArgs, lastArg, nil
|
||||
}
|
||||
|
||||
// initDefaultCompletionCmd adds a default 'completion' command to c.
|
||||
// This function will do nothing if any of the following is true:
|
||||
// 1- the feature has been explicitly disabled by the program,
|
||||
// 2- c has no subcommands (to avoid creating one),
|
||||
// 3- c already has a 'completion' command provided by the program.
|
||||
func (c *Command) initDefaultCompletionCmd() {
|
||||
if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, cmd := range c.commands {
|
||||
if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) {
|
||||
// A completion command is already available
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions
|
||||
|
||||
completionCmd := &Command{
|
||||
Use: compCmdName,
|
||||
Short: "generate the autocompletion script for the specified shell",
|
||||
Long: fmt.Sprintf(`
|
||||
Generate the autocompletion script for %[1]s for the specified shell.
|
||||
See each sub-command's help for details on how to use the generated script.
|
||||
`, c.Root().Name()),
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
}
|
||||
c.AddCommand(completionCmd)
|
||||
|
||||
out := c.OutOrStdout()
|
||||
noDesc := c.CompletionOptions.DisableDescriptions
|
||||
shortDesc := "generate the autocompletion script for %s"
|
||||
bash := &Command{
|
||||
Use: "bash",
|
||||
Short: fmt.Sprintf(shortDesc, "bash"),
|
||||
Long: fmt.Sprintf(`
|
||||
Generate the autocompletion script for the bash shell.
|
||||
|
||||
This script depends on the 'bash-completion' package.
|
||||
If it is not installed already, you can install it via your OS's package manager.
|
||||
|
||||
To load completions in your current shell session:
|
||||
$ source <(%[1]s completion bash)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
Linux:
|
||||
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
|
||||
MacOS:
|
||||
$ %[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`, c.Root().Name()),
|
||||
Args: NoArgs,
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
RunE: func(cmd *Command, args []string) error {
|
||||
return cmd.Root().GenBashCompletionV2(out, !noDesc)
|
||||
},
|
||||
}
|
||||
if haveNoDescFlag {
|
||||
bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
zsh := &Command{
|
||||
Use: "zsh",
|
||||
Short: fmt.Sprintf(shortDesc, "zsh"),
|
||||
Long: fmt.Sprintf(`
|
||||
Generate the autocompletion script for the zsh shell.
|
||||
|
||||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
# Linux:
|
||||
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
|
||||
# macOS:
|
||||
$ %[1]s completion zsh > /usr/local/share/zsh/site-functions/_%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`, c.Root().Name()),
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
RunE: func(cmd *Command, args []string) error {
|
||||
if noDesc {
|
||||
return cmd.Root().GenZshCompletionNoDesc(out)
|
||||
}
|
||||
return cmd.Root().GenZshCompletion(out)
|
||||
},
|
||||
}
|
||||
if haveNoDescFlag {
|
||||
zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
fish := &Command{
|
||||
Use: "fish",
|
||||
Short: fmt.Sprintf(shortDesc, "fish"),
|
||||
Long: fmt.Sprintf(`
|
||||
Generate the autocompletion script for the fish shell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
$ %[1]s completion fish | source
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`, c.Root().Name()),
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
RunE: func(cmd *Command, args []string) error {
|
||||
return cmd.Root().GenFishCompletion(out, !noDesc)
|
||||
},
|
||||
}
|
||||
if haveNoDescFlag {
|
||||
fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
powershell := &Command{
|
||||
Use: "powershell",
|
||||
Short: fmt.Sprintf(shortDesc, "powershell"),
|
||||
Long: fmt.Sprintf(`
|
||||
Generate the autocompletion script for powershell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
PS C:\> %[1]s completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
To load completions for every new session, add the output of the above command
|
||||
to your powershell profile.
|
||||
`, c.Root().Name()),
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
RunE: func(cmd *Command, args []string) error {
|
||||
if noDesc {
|
||||
return cmd.Root().GenPowerShellCompletion(out)
|
||||
}
|
||||
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
||||
|
||||
},
|
||||
}
|
||||
if haveNoDescFlag {
|
||||
powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc)
|
||||
}
|
||||
|
||||
completionCmd.AddCommand(bash, zsh, fish, powershell)
|
||||
}
|
||||
|
||||
func findFlag(cmd *Command, name string) *pflag.Flag {
|
||||
flagSet := cmd.Flags()
|
||||
if len(name) == 1 {
|
||||
|
|
Loading…
Add table
Reference in a new issue