Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remove group method for command #1997

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{}
Expand Down
104 changes: 104 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,110 @@ func TestAddGroup(t *testing.T) {
checkStringContains(t, output, "\nTest group\n cmd")
}

func TestRemoveSingleGroup(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding tests! Definitely makes it easier to validate!!

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}

Expand Down
2 changes: 1 addition & 1 deletion site/content/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down