diff --git a/command.go b/command.go index 01f7c6f1c..00853c9b4 100644 --- a/command.go +++ b/command.go @@ -330,12 +330,25 @@ func (c *Command) SetHelpCommandGroupID(groupID string) { c.helpCommandGroupID = groupID } +// resetHelpCommandGroupID resets the group id of the help command. +func (c *Command) resetHelpCommandGroupID() { + if c.helpCommand != nil { + c.helpCommand.GroupID = "" + } + c.helpCommandGroupID = "" +} + // SetCompletionCommandGroupID sets the group id of the completion command. func (c *Command) SetCompletionCommandGroupID(groupID string) { // completionCommandGroupID is used if no completion command is defined by the user c.Root().completionCommandGroupID = groupID } +// resetCompletionCommandGroupID resets the group id of the completion command. +func (c *Command) resetCompletionCommandGroupID() { + c.Root().completionCommandGroupID = "" +} + // SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { c.helpTemplate = s @@ -1311,6 +1324,47 @@ func (c *Command) AddGroup(groups ...*Group) { c.commandgroups = append(c.commandgroups, groups...) } +// RemoveGroup removes one or more command groups to this parent command. +func (c *Command) RemoveGroup(groupIDs ...string) error { + // remove groups from commandgroups + groups := []*Group{} + hasRemoved := false +main: + for _, group := range c.commandgroups { + for _, groupID := range groupIDs { + if group.ID == groupID { + hasRemoved = true + continue main + } + } + groups = append(groups, group) + } + if !hasRemoved { + return fmt.Errorf("following group ID does not exist; %s", strings.Join(groupIDs, ", ")) + } + c.commandgroups = groups + // remove the groupID from the target commands + for _, command := range c.commands { + for _, groupID := range groupIDs { + if command.GroupID == groupID { + command.GroupID = "" + } + if command.helpCommandGroupID == groupID { + command.resetHelpCommandGroupID() + } + } + } + for _, groupID := range groupIDs { + if c.helpCommandGroupID == groupID { + c.resetHelpCommandGroupID() + } + if c.Root().completionCommandGroupID == groupID { + c.resetCompletionCommandGroupID() + } + } + return nil +} + // RemoveCommand removes one or more commands from a parent command. func (c *Command) RemoveCommand(cmds ...*Command) { commands := []*Command{} diff --git a/command_test.go b/command_test.go index b0f5e860e..624d738a3 100644 --- a/command_test.go +++ b/command_test.go @@ -1862,6 +1862,110 @@ func TestAddGroup(t *testing.T) { checkStringContains(t, output, "\nTest group\n cmd") } +func TestRemoveSingleGroup(t *testing.T) { + var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun} + + rootCmd.AddGroup( + &Group{ID: "group", Title: "Test group"}, + &Group{ID: "help", Title: "help"}, + &Group{ID: "comp", Title: "comp"}, + ) + + rootCmd.AddCommand(&Command{Use: "sub", GroupID: "group", Run: emptyRun}) + + rootCmd.SetHelpCommandGroupID("help") + rootCmd.SetCompletionCommandGroupID("comp") + + if err := rootCmd.RemoveGroup("group"); err != nil { + t.Errorf("Unexpected error: %v", err) + } + + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringOmits(t, output, "\nTest group:\n sub") + checkStringContains(t, output, "\nAdditional Commands:\n sub") +} + +func TestRemoveHelpCommandGroup(t *testing.T) { + rootCmd := &Command{Use: "root", Short: "test", Run: emptyRun} + rootCmd.CompletionOptions.DisableDefaultCmd = true + + rootCmd.AddGroup(&Group{ID: "group", Title: "group"}) + rootCmd.AddCommand(&Command{Use: "child", Short: "c", GroupID: "group", Run: emptyRun}) + rootCmd.SetHelpCommandGroupID("group") + + if err := rootCmd.RemoveGroup("group"); err != nil { + t.Errorf("Unexpected error: %v", err) + } + + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringOmits(t, output, "\ngroup\n child\n help") + checkStringContains(t, output, "\nAvailable Commands:\n child c\n help") +} + +func TestRemoveCompletionCommandGroup(t *testing.T) { + rootCmd := &Command{Use: "root", Short: "test", Run: emptyRun} + + rootCmd.AddGroup( + &Group{ID: "group", Title: "group"}, + &Group{ID: "help", Title: "help"}, + ) + rootCmd.AddCommand(&Command{Use: "child", Short: "c", GroupID: "group", Run: emptyRun}) + rootCmd.SetHelpCommandGroupID("help") + rootCmd.SetCompletionCommandGroupID("group") + + if err := rootCmd.RemoveGroup("group"); err != nil { + t.Errorf("Unexpected error: %v", err) + } + + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringOmits(t, output, "\ngroup\n child\n completion") + checkStringContains(t, output, "\nAdditional Commands:\n child c\n completion") +} + +func TestRemoveMultipleGroups(t *testing.T) { + var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun} + + rootCmd.AddGroup( + &Group{ID: "group1", Title: "Test group1"}, + &Group{ID: "group2", Title: "Test group2"}, + &Group{ID: "help", Title: "help"}, + &Group{ID: "comp", Title: "comp"}, + ) + + rootCmd.AddCommand( + &Command{Use: "sub1", Short: "sub1", GroupID: "group1", Run: emptyRun}, + &Command{Use: "sub2", Short: "sub2", GroupID: "group2", Run: emptyRun}, + ) + + rootCmd.SetHelpCommandGroupID("help") + rootCmd.SetCompletionCommandGroupID("comp") + + if err := rootCmd.RemoveGroup("group1", "group2"); err != nil { + t.Errorf("Unexpected error: %v", err) + } + + output, err := executeCommand(rootCmd, "--help") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + checkStringOmits(t, output, "\nTest group1:\n sub1") + checkStringOmits(t, output, "\nTest group2:\n sub2") + checkStringContains(t, output, "\nAdditional Commands:\n sub1 sub1\n sub2 sub2") +} + func TestWrongGroupFirstLevel(t *testing.T) { var rootCmd = &Command{Use: "root", Short: "test", Run: emptyRun} diff --git a/site/content/user_guide.md b/site/content/user_guide.md index 56e8e44a3..71ddddc42 100644 --- a/site/content/user_guide.md +++ b/site/content/user_guide.md @@ -534,7 +534,7 @@ around it. In fact, you can provide your own if you want. Cobra supports grouping of available commands in the help output. To group commands, each group must be explicitly defined using `AddGroup()` on the parent command. Then a subcommand can be added to a group using the `GroupID` element -of that subcommand. The groups will appear in the help output in the same order as they are defined using different +of that subcommand. `RemoveGroup()` can be used to remove groups and reset the GroupID of commands belonging to the removed group. The groups will appear in the help output in the same order as they are defined using different calls to `AddGroup()`. If you use the generated `help` or `completion` commands, you can set their group ids using `SetHelpCommandGroupId()` and `SetCompletionCommandGroupId()` on the root command, respectively.