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
* Intelligent suggestions (`app srver`... did you mean `app server`?)
* Automatic help generation for commands and flags
* Grouping help for subcommands
* Automatic help flag recognition of `-h`, `--help`, etc.
* Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
* 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
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.
// E.g. 'go run ...' - 'run' is the command. Cobra requires
// 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 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 string
@ -128,6 +137,9 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error
// groups for subcommands
commandgroups []*Group
// args is actual args parsed from flags.
args []string
// 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,
// cobra uses default help 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 string
@ -303,6 +321,21 @@ func (c *Command) SetHelpCommand(cmd *Command) {
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.
func (c *Command) SetHelpTemplate(s string) {
c.helpTemplate = s
@ -511,10 +544,13 @@ Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Available Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
{{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:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
@ -1140,6 +1176,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
CheckErr(cmd.Help())
}
},
GroupID: c.helpCommandGroupID,
}
}
c.RemoveCommand(c.helpCommand)
@ -1178,6 +1215,10 @@ func (c *Command) AddCommand(cmds ...*Command) {
panic("Command can't be a child of itself")
}
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
usageLen := len(x.Use)
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.
func (c *Command) RemoveCommand(cmds ...*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,
ValidArgsFunction: NoFileCompletions,
Hidden: c.CompletionOptions.HiddenDefaultCmd,
GroupID: c.completionCommandGroupID,
}
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
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
You can provide your own Help command or your own template for the default command to use