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
feat: add flag help groups #2117
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if groupID == "" { | ||
fs.AddFlag(f) | ||
} | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't return ""
better than returning nil
in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return
statement is from VisitAll()
closure. It's not actually returning any values, but working as a control flow statement, so we don't access an out of bound index at f.Annotations[FlagHelpGroupsAnnotation]
bellow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right, my mistake! Resolving it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi!
Thank you for this PR, this is really appreciated!
I tested it and it works fine:
$ cat go.mod
module github.com/eiffel-fl/group-flags-example
go 1.21.7
require github.com/spf13/cobra v1.8.0
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)
replace github.com/spf13/cobra => github.com/pedromotita/cobra v0.0.0-20240307182157-e9498ae28a51
$ cat main.go
package main
import (
"github.com/spf13/cobra"
)
func main() {
child := &cobra.Command{Use: "child", Run: func (*cobra.Command, []string) {}}
rootCmd := &cobra.Command{Use: "root", Run: func (*cobra.Command, []string) {}}
rootCmd.AddCommand(child)
b := "b"
s := "s"
i := "i"
g := "g"
child.Flags().Bool(b, false, "bool flag")
child.Flags().String(s, "", "string flag")
child.Flags().Int(i, 0, "int flag")
rootCmd.PersistentFlags().String(g, "", "global flag")
group := cobra.Group{ID: "groupId", Title: "GroupTitle"}
child.AddFlagHelpGroup(&group)
_ = child.AddFlagToHelpGroupID(b, group.ID)
_ = child.AddFlagToHelpGroupID(s, group.ID)
rootCmd.Execute()
}%
$ go run . child --help
Usage:
root child [flags]
Flags:
-h, --help help for child
--i int int flag
GroupTitle Flags:
--b bool flag
--s string string flag
Global Flags:
--g string global flag
I have some small comments.
Best regards.
if groupID == "" { | ||
fs.AddFlag(f) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure to understand why you are adding flags without group to this flag set, can you please shed some light?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the solution I've found for printing the usage of flags that have not been added to any flagHelpGroup
.
If you see the template on Command.UsageTemplate
, I use UsageByFlagHelpGroupID
to print flag usages when len(c.FlagHelpGroups) != 0
. But I still have to cover the case where there are flags not attached to any group, which is the case of the i
flag in your example. I guarantee that there wont be any empty groupID
by returning an error at AddFlagHelpGroup
The same goes for InheritedFlags
and the global
reserved group ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this code basically avoids having orphan flags without group.
This makes sense, thank you!
return nil | ||
} | ||
|
||
func (c *Command) hasFlagHelpGroup(groupID string) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of ID, should you use category
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. Do you mean renaming groupID
to category
?
I prefer groupID
to make it clear we are dealing with Group
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. Do you mean renaming groupID to category?
Yes.
I prefer groupID to make it clear we are dealing with Group here
Let's stick with groupID
.
|
||
// AddFlagHelpGroup adds one more flag help group do the command. Returns an error if the Group.ID is empty, | ||
// or if the "global" reserved ID is used | ||
func (c *Command) AddFlagHelpGroup(groups ...*Group) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that Group
is already used in cobra
, but I am wondering if we should not use Group
instead of HelpGroup
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a previous discussion on why to use cobra's Group
in this thread. Take a look 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was only referring to naming here, i.e. AddFlagGroup()
instead of AddFlagHelperGroup()
.
} | ||
|
||
// AddFlagToHelpGroupID adds associates a flag to a groupID. Returns an error if the flag or group is non-existent | ||
func (c *Command) AddFlagToHelpGroupID(flag, groupID string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be useful to have some option to add multiple flags in one call (like in AddFlagHelpGroup)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I second this point, having a variadic function would be welcomed!
The implementation could be something like this:
func (c *Command) AddFlagsToHelpGroupID(groupID string, flags ...string) error {
for _, flag := range flags {
err := AddFlagToHelpGroupID(flag, groupID)
if err != nil {
return err
}
}
return nil
}
Flags: | ||
{{$cmd.UsageByFlagHelpGroupID "" | trimTrailingWhitespaces}}{{end}} | ||
|
||
{{.Title}} Flags: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can cause problems if localization support is added.
{{.Title}}:
is probably better.
Description
Flags can now be split into groups in usage text:
Related Issues
Implementation Details
The API for this feature was based on this comment. Check it out :)