Add groups for commands in help

Co-authored-by: Marc Khouzam <marc.khouzam@gmail.com>
This commit is contained in:
Alexander Weiss 2022-03-10 09:26:12 +01:00
parent 212ea40783
commit 3f96ff8c4a
4 changed files with 73 additions and 3 deletions

View file

@ -23,6 +23,7 @@ Cobra provides:
* Global, local and cascading flags * Global, local and cascading flags
* Intelligent suggestions (`app srver`... did you mean `app server`?) * Intelligent suggestions (`app srver`... did you mean `app server`?)
* Automatic help generation for commands and flags * Automatic help generation for commands and flags
* Grouping help for subcommands
* Automatic help flag recognition of `-h`, `--help`, etc. * Automatic help flag recognition of `-h`, `--help`, etc.
* Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell) * Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
* Automatically generated man pages for your application * Automatically generated man pages for your application

View file

@ -35,6 +35,12 @@ const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
// FParseErrWhitelist configures Flag parse errors to be ignored // FParseErrWhitelist configures Flag parse errors to be ignored
type FParseErrWhitelist flag.ParseErrorsWhitelist type FParseErrWhitelist flag.ParseErrorsWhitelist
// Structure to manage groups for commands
type Group struct {
ID string
Title string
}
// Command is just that, a command for your application. // Command is just that, a command for your application.
// E.g. 'go run ...' - 'run' is the command. Cobra requires // E.g. 'go run ...' - 'run' is the command. Cobra requires
// you to define the usage and description as part of your command // you to define the usage and description as part of your command
@ -61,6 +67,9 @@ type Command struct {
// Short is the short description shown in the 'help' output. // Short is the short description shown in the 'help' output.
Short string Short string
// The group id under which this subcommand is grouped in the 'help' output of its parent.
GroupID string
// Long is the long message shown in the 'help <this-command>' output. // Long is the long message shown in the 'help <this-command>' output.
Long string Long string
@ -128,6 +137,9 @@ 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
// groups for subcommands
commandgroups []*Group
// 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.
@ -160,6 +172,12 @@ type Command struct {
// helpCommand is command with usage 'help'. If it's not defined by user, // helpCommand is command with usage 'help'. If it's not defined by user,
// cobra uses default help command. // cobra uses default help command.
helpCommand *Command helpCommand *Command
// helpCommandGroupID is the group id for the helpCommand
helpCommandGroupID string
// completionCommandGroupID is the group id for the completion command
completionCommandGroupID string
// versionTemplate is the version template defined by user. // versionTemplate is the version template defined by user.
versionTemplate string versionTemplate string
@ -303,6 +321,21 @@ func (c *Command) SetHelpCommand(cmd *Command) {
c.helpCommand = cmd c.helpCommand = cmd
} }
// SetHelpCommandGroup sets the group id of the help command.
func (c *Command) SetHelpCommandGroupID(groupID string) {
if c.helpCommand != nil {
c.helpCommand.GroupID = groupID
}
// helpCommandGroupID is used if no helpCommand is defined by the user
c.helpCommandGroupID = groupID
}
// SetCompletionCommandGroup sets the group id of the completion command.
func (c *Command) SetCompletionCommandGroupID(groupID string) {
// completionCommandGroupID is used if no completion commmand is defined by the user
c.Root().completionCommandGroupID = groupID
}
// SetHelpTemplate sets help template to be used. Application can use it to set custom template. // SetHelpTemplate sets help template to be used. Application can use it to set custom template.
func (c *Command) SetHelpTemplate(s string) { func (c *Command) SetHelpTemplate(s string) {
c.helpTemplate = s c.helpTemplate = s
@ -511,10 +544,13 @@ Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}} {{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples: Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}} {{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} Available Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{range $group := .Groups}}
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags: Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
@ -1140,6 +1176,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
CheckErr(cmd.Help()) CheckErr(cmd.Help())
} }
}, },
GroupID: c.helpCommandGroupID,
} }
} }
c.RemoveCommand(c.helpCommand) c.RemoveCommand(c.helpCommand)
@ -1178,6 +1215,10 @@ func (c *Command) AddCommand(cmds ...*Command) {
panic("Command can't be a child of itself") panic("Command can't be a child of itself")
} }
cmds[i].parent = c cmds[i].parent = c
// if Group is not defined let the developer know right away
if x.GroupID != "" && !c.ContainsGroup(x.GroupID) {
panic(fmt.Sprintf("Group id %s is not defined", x.GroupID))
}
// update max lengths // update max lengths
usageLen := len(x.Use) usageLen := len(x.Use)
if usageLen > c.commandsMaxUseLen { if usageLen > c.commandsMaxUseLen {
@ -1200,6 +1241,26 @@ func (c *Command) AddCommand(cmds ...*Command) {
} }
} }
// Groups returns a slice of child command groups.
func (c *Command) Groups() []*Group {
return c.commandgroups
}
// ContainGroups return if groupID exists in the list of command groups.
func (c *Command) ContainsGroup(groupID string) bool {
for _, x := range c.commandgroups {
if x.ID == groupID {
return true
}
}
return false
}
// AddGroup adds one or more command groups to this parent command.
func (c *Command) AddGroup(groups ...*Group) {
c.commandgroups = append(c.commandgroups, groups...)
}
// RemoveCommand removes one or more commands from a parent command. // RemoveCommand removes one or more commands from a parent command.
func (c *Command) RemoveCommand(cmds ...*Command) { func (c *Command) RemoveCommand(cmds ...*Command) {
commands := []*Command{} commands := []*Command{}

View file

@ -673,6 +673,7 @@ See each sub-command's help for details on how to use the generated script.
Args: NoArgs, Args: NoArgs,
ValidArgsFunction: NoFileCompletions, ValidArgsFunction: NoFileCompletions,
Hidden: c.CompletionOptions.HiddenDefaultCmd, Hidden: c.CompletionOptions.HiddenDefaultCmd,
GroupID: c.completionCommandGroupID,
} }
c.AddCommand(completionCmd) c.AddCommand(completionCmd)

View file

@ -490,6 +490,13 @@ command and flag definitions are needed.
Help is just a command like any other. There is no special logic or behavior Help is just a command like any other. There is no special logic or behavior
around it. In fact, you can provide your own if you want. around it. In fact, you can provide your own if you want.
### Grouping commands in help
Cobra supports grouping of available commands. Groups must be explicitly defined by `AddGroup` and set by
the `GroupId` element of a subcommand. The groups will appear in the same order as they are defined.
If you use the generated `help` or `completion` commands, you can set the group ids by `SetHelpCommandGroupId`
and `SetCompletionCommandGroupId`, respectively.
### Defining your own help ### Defining your own help
You can provide your own Help command or your own template for the default command to use You can provide your own Help command or your own template for the default command to use