Add support for groups in the default HelpPrinter (#135)
This commit is contained in:
@@ -589,6 +589,7 @@ The default help output is usually sufficient, but if not there are two solution
|
|||||||
1. Use `ConfigureHelp(HelpOptions)` to configure how help is formatted (see [HelpOptions](https://godoc.org/github.com/alecthomas/kong#HelpOptions) for details).
|
1. Use `ConfigureHelp(HelpOptions)` to configure how help is formatted (see [HelpOptions](https://godoc.org/github.com/alecthomas/kong#HelpOptions) for details).
|
||||||
2. Custom help can be wired into Kong via the `Help(HelpFunc)` option. The `HelpFunc` is passed a `Context`, which contains the parsed context for the current command-line. See the implementation of `PrintHelp` for an example.
|
2. Custom help can be wired into Kong via the `Help(HelpFunc)` option. The `HelpFunc` is passed a `Context`, which contains the parsed context for the current command-line. See the implementation of `PrintHelp` for an example.
|
||||||
3. Use `HelpFormatter(HelpValueFormatter)` if you want to just customize the help text that is accompanied by flags and arguments.
|
3. Use `HelpFormatter(HelpValueFormatter)` if you want to just customize the help text that is accompanied by flags and arguments.
|
||||||
|
4. Use `Groups([]Group)` if you want to customize group titles or add a header.
|
||||||
|
|
||||||
### `Bind(...)` - bind values for callback hooks and Run() methods
|
### `Bind(...)` - bind values for callback hooks and Run() methods
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ func buildChild(k *Kong, node *Node, typ NodeType, v reflect.Value, ft reflect.S
|
|||||||
child.Parent = node
|
child.Parent = node
|
||||||
child.Help = tag.Help
|
child.Help = tag.Help
|
||||||
child.Hidden = tag.Hidden
|
child.Hidden = tag.Hidden
|
||||||
child.Group = tag.Group
|
child.Group = buildGroupForKey(k, tag.Group)
|
||||||
child.Aliases = tag.Aliases
|
child.Aliases = tag.Aliases
|
||||||
|
|
||||||
if provider, ok := fv.Addr().Interface().(HelpProvider); ok {
|
if provider, ok := fv.Addr().Interface().(HelpProvider); ok {
|
||||||
@@ -213,7 +213,7 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
|
|||||||
Short: tag.Short,
|
Short: tag.Short,
|
||||||
PlaceHolder: tag.PlaceHolder,
|
PlaceHolder: tag.PlaceHolder,
|
||||||
Env: tag.Env,
|
Env: tag.Env,
|
||||||
Group: tag.Group,
|
Group: buildGroupForKey(k, tag.Group),
|
||||||
Xor: tag.Xor,
|
Xor: tag.Xor,
|
||||||
Hidden: tag.Hidden,
|
Hidden: tag.Hidden,
|
||||||
}
|
}
|
||||||
@@ -221,3 +221,20 @@ func buildField(k *Kong, node *Node, v reflect.Value, ft reflect.StructField, fv
|
|||||||
node.Flags = append(node.Flags, flag)
|
node.Flags = append(node.Flags, flag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildGroupForKey(k *Kong, key string) *Group {
|
||||||
|
if key == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, group := range k.groups {
|
||||||
|
if group.Key == key {
|
||||||
|
return &group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No group provided with kong.Groups. We create one ad-hoc for this key.
|
||||||
|
return &Group{
|
||||||
|
Key: key,
|
||||||
|
Title: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -144,22 +144,41 @@ func printNodeDetail(w *helpWriter, node *Node, hide bool) {
|
|||||||
writePositionals(w.Indent(), node.Positional)
|
writePositionals(w.Indent(), node.Positional)
|
||||||
}
|
}
|
||||||
if flags := node.AllFlags(true); len(flags) > 0 {
|
if flags := node.AllFlags(true); len(flags) > 0 {
|
||||||
w.Print("")
|
groupedFlags := collectFlagGroups(flags)
|
||||||
w.Print("Flags:")
|
for _, group := range groupedFlags {
|
||||||
writeFlags(w.Indent(), flags)
|
w.Print("")
|
||||||
|
if group.Metadata.Title != "" {
|
||||||
|
w.Print(group.Metadata.Title)
|
||||||
|
}
|
||||||
|
if group.Metadata.Header != "" {
|
||||||
|
w.Print(group.Metadata.Header)
|
||||||
|
}
|
||||||
|
writeFlags(w.Indent(), group.Flags)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cmds := node.Leaves(hide)
|
cmds := node.Leaves(hide)
|
||||||
if len(cmds) > 0 {
|
if len(cmds) > 0 {
|
||||||
w.Print("")
|
iw := w.Indent()
|
||||||
w.Print("Commands:")
|
|
||||||
if w.Tree {
|
if w.Tree {
|
||||||
writeCommandTree(w, node)
|
w.Print("")
|
||||||
|
w.Print("Commands:")
|
||||||
|
writeCommandTree(iw, node)
|
||||||
} else {
|
} else {
|
||||||
iw := w.Indent()
|
groupedCmds := collectCommandGroups(cmds)
|
||||||
if w.Compact {
|
for _, group := range groupedCmds {
|
||||||
writeCompactCommandList(cmds, iw)
|
w.Print("")
|
||||||
} else {
|
if group.Metadata.Title != "" {
|
||||||
writeCommandList(cmds, iw)
|
w.Print(group.Metadata.Title)
|
||||||
|
}
|
||||||
|
if group.Metadata.Header != "" {
|
||||||
|
w.Print(group.Metadata.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Compact {
|
||||||
|
writeCompactCommandList(group.Commands, iw)
|
||||||
|
} else {
|
||||||
|
writeCommandList(group.Commands, iw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +208,6 @@ func writeCompactCommandList(cmds []*Node, iw *helpWriter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeCommandTree(w *helpWriter, node *Node) {
|
func writeCommandTree(w *helpWriter, node *Node) {
|
||||||
iw := w.Indent()
|
|
||||||
rows := make([][2]string, 0, len(node.Children)*2)
|
rows := make([][2]string, 0, len(node.Children)*2)
|
||||||
for i, cmd := range node.Children {
|
for i, cmd := range node.Children {
|
||||||
if cmd.Hidden {
|
if cmd.Hidden {
|
||||||
@@ -200,27 +218,93 @@ func writeCommandTree(w *helpWriter, node *Node) {
|
|||||||
rows = append(rows, [2]string{"", ""})
|
rows = append(rows, [2]string{"", ""})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeTwoColumns(iw, rows)
|
writeTwoColumns(w, rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
type helpFlagGroup struct {
|
||||||
|
Metadata *Group
|
||||||
|
Flags [][]*Flag
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectFlagGroups(flags [][]*Flag) []helpFlagGroup {
|
||||||
|
// Group keys in order of appearance.
|
||||||
|
groups := []*Group{}
|
||||||
|
// Flags grouped by their group key.
|
||||||
|
flagsByGroup := map[string][][]*Flag{}
|
||||||
|
|
||||||
|
for _, levelFlags := range flags {
|
||||||
|
levelFlagsByGroup := map[string][]*Flag{}
|
||||||
|
|
||||||
|
for _, flag := range levelFlags {
|
||||||
|
key := ""
|
||||||
|
if flag.Group != nil {
|
||||||
|
key = flag.Group.Key
|
||||||
|
groupAlreadySeen := false
|
||||||
|
for _, group := range groups {
|
||||||
|
if key == group.Key {
|
||||||
|
groupAlreadySeen = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !groupAlreadySeen {
|
||||||
|
groups = append(groups, flag.Group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
levelFlagsByGroup[key] = append(levelFlagsByGroup[key], flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, flags := range levelFlagsByGroup {
|
||||||
|
flagsByGroup[key] = append(flagsByGroup[key], flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out := []helpFlagGroup{}
|
||||||
|
// Ungrouped flags are always displayed first.
|
||||||
|
if ungroupedFlags, ok := flagsByGroup[""]; ok {
|
||||||
|
out = append(out, helpFlagGroup{
|
||||||
|
Metadata: &Group{Title: "Flags:"},
|
||||||
|
Flags: ungroupedFlags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
out = append(out, helpFlagGroup{Metadata: group, Flags: flagsByGroup[group.Key]})
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: unused
|
|
||||||
type helpCommandGroup struct {
|
type helpCommandGroup struct {
|
||||||
Name string
|
Metadata *Group
|
||||||
Commands []*Node
|
Commands []*Node
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: unused, deadcode
|
|
||||||
func collectCommandGroups(nodes []*Node) []helpCommandGroup {
|
func collectCommandGroups(nodes []*Node) []helpCommandGroup {
|
||||||
groups := map[string][]*Node{}
|
// Groups in order of appearance.
|
||||||
|
groups := []*Group{}
|
||||||
|
// Nodes grouped by their group key.
|
||||||
|
nodesByGroup := map[string][]*Node{}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
groups[node.Group] = append(groups[node.Group], node)
|
key := ""
|
||||||
}
|
if group := node.ClosestGroup(); group != nil {
|
||||||
out := []helpCommandGroup{}
|
key = group.Key
|
||||||
for name, nodes := range groups {
|
if _, ok := nodesByGroup[key]; !ok {
|
||||||
if name == "" {
|
groups = append(groups, group)
|
||||||
name = "Commands"
|
}
|
||||||
}
|
}
|
||||||
out = append(out, helpCommandGroup{Name: name, Commands: nodes})
|
nodesByGroup[key] = append(nodesByGroup[key], node)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := []helpCommandGroup{}
|
||||||
|
// Ungrouped nodes are always displayed first.
|
||||||
|
if ungroupedNodes, ok := nodesByGroup[""]; ok {
|
||||||
|
out = append(out, helpCommandGroup{
|
||||||
|
Metadata: &Group{Title: "Commands:"},
|
||||||
|
Commands: ungroupedNodes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
out = append(out, helpCommandGroup{Metadata: group, Commands: nodesByGroup[group.Key]})
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
+162
-1
@@ -136,7 +136,7 @@ func TestHelpTree(t *testing.T) {
|
|||||||
Other struct {
|
Other struct {
|
||||||
Other string `arg help:"other arg"`
|
Other string `arg help:"other arg"`
|
||||||
} `arg help:"subcommand other"`
|
} `arg help:"subcommand other"`
|
||||||
} `cmd help:"subcommand one"`
|
} `cmd help:"subcommand one" group:"Group A"` // Groups are ignored in trees
|
||||||
|
|
||||||
Two struct {
|
Two struct {
|
||||||
Three threeArg `arg help:"Sub-sub-arg."`
|
Three threeArg `arg help:"Sub-sub-arg."`
|
||||||
@@ -248,3 +248,164 @@ func TestCustomHelpFormatter(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Contains(t, w.String(), "A flag.")
|
require.Contains(t, w.String(), "A flag.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHelpGrouping(t *testing.T) {
|
||||||
|
// nolint: govet
|
||||||
|
var cli struct {
|
||||||
|
GroupedAString string `help:"A string flag grouped in A." group:"Group A"`
|
||||||
|
FreeString string `help:"A non grouped string flag."`
|
||||||
|
GroupedBString string `help:"A string flag grouped in B." group:"Group B"`
|
||||||
|
FreeBool bool `help:"A non grouped bool flag."`
|
||||||
|
GroupedABool bool `help:"A bool flag grouped in A." group:"Group A"`
|
||||||
|
|
||||||
|
One struct {
|
||||||
|
Flag string `help:"Nested flag."`
|
||||||
|
// Group is inherited from the parent command
|
||||||
|
Thing struct {
|
||||||
|
Arg string `arg help:"argument"`
|
||||||
|
} `cmd help:"subcommand thing"`
|
||||||
|
Other struct {
|
||||||
|
Other string `arg help:"other arg"`
|
||||||
|
} `arg help:"subcommand other"`
|
||||||
|
// ... but a subcommand can override it
|
||||||
|
Stuff struct {
|
||||||
|
Stuff string `arg help:"argument"`
|
||||||
|
} `arg help:"subcommand stuff" group:"Group B"`
|
||||||
|
} `cmd help:"A subcommand grouped in A." group:"Group A"`
|
||||||
|
|
||||||
|
Two struct {
|
||||||
|
Grouped1String string `help:"A string flag grouped in 1." group:"Group 1"`
|
||||||
|
AFreeString string `help:"A non grouped string flag."`
|
||||||
|
Grouped2String string `help:"A string flag grouped in 2." group:"Group 2"`
|
||||||
|
AGroupedAString bool `help:"A string flag grouped in A." group:"Group A"`
|
||||||
|
Grouped1Bool bool `help:"A bool flag grouped in 1." group:"Group 1"`
|
||||||
|
} `cmd help:"A non grouped subcommand."`
|
||||||
|
|
||||||
|
Four struct {
|
||||||
|
Flag string `help:"Nested flag."`
|
||||||
|
} `cmd help:"Another subcommand grouped in B." group:"Group B"`
|
||||||
|
|
||||||
|
Three struct {
|
||||||
|
Flag string `help:"Nested flag."`
|
||||||
|
} `cmd help:"Another subcommand grouped in A." group:"Group A"`
|
||||||
|
}
|
||||||
|
|
||||||
|
w := bytes.NewBuffer(nil)
|
||||||
|
exited := false
|
||||||
|
app := mustNew(t, &cli,
|
||||||
|
kong.Name("test-app"),
|
||||||
|
kong.Description("A test app."),
|
||||||
|
kong.Groups([]kong.Group{
|
||||||
|
{
|
||||||
|
Key: "Group A",
|
||||||
|
Title: "Group title taken from the kong.Groups option",
|
||||||
|
Header: "A group header",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "Group 1",
|
||||||
|
Title: "Another group title, this time without header",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "Unknown key",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
kong.Writers(w, w),
|
||||||
|
kong.Exit(func(int) {
|
||||||
|
exited = true
|
||||||
|
panic(true) // Panic to fake "exit".
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("Full", func(t *testing.T) {
|
||||||
|
require.PanicsWithValue(t, true, func() {
|
||||||
|
_, err := app.Parse([]string{"--help"})
|
||||||
|
require.True(t, exited)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
expected := `Usage: test-app <command>
|
||||||
|
|
||||||
|
A test app.
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help Show context-sensitive help.
|
||||||
|
--free-string=STRING A non grouped string flag.
|
||||||
|
--free-bool A non grouped bool flag.
|
||||||
|
|
||||||
|
Group title taken from the kong.Groups option
|
||||||
|
A group header
|
||||||
|
--grouped-a-string=STRING A string flag grouped in A.
|
||||||
|
--grouped-a-bool A bool flag grouped in A.
|
||||||
|
|
||||||
|
Group B
|
||||||
|
--grouped-b-string=STRING A string flag grouped in B.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
two
|
||||||
|
A non grouped subcommand.
|
||||||
|
|
||||||
|
Group title taken from the kong.Groups option
|
||||||
|
A group header
|
||||||
|
one thing <arg>
|
||||||
|
subcommand thing
|
||||||
|
|
||||||
|
one <other>
|
||||||
|
subcommand other
|
||||||
|
|
||||||
|
three
|
||||||
|
Another subcommand grouped in A.
|
||||||
|
|
||||||
|
Group B
|
||||||
|
one <stuff>
|
||||||
|
subcommand stuff
|
||||||
|
|
||||||
|
four
|
||||||
|
Another subcommand grouped in B.
|
||||||
|
|
||||||
|
Run "test-app <command> --help" for more information on a command.
|
||||||
|
`
|
||||||
|
t.Log(w.String())
|
||||||
|
t.Log(expected)
|
||||||
|
require.Equal(t, expected, w.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Selected", func(t *testing.T) {
|
||||||
|
exited = false
|
||||||
|
w.Truncate(0)
|
||||||
|
require.PanicsWithValue(t, true, func() {
|
||||||
|
_, err := app.Parse([]string{"two", "--help"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, exited)
|
||||||
|
})
|
||||||
|
expected := `Usage: test-app two
|
||||||
|
|
||||||
|
A non grouped subcommand.
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help Show context-sensitive help.
|
||||||
|
--free-string=STRING A non grouped string flag.
|
||||||
|
--free-bool A non grouped bool flag.
|
||||||
|
|
||||||
|
--a-free-string=STRING A non grouped string flag.
|
||||||
|
|
||||||
|
Group title taken from the kong.Groups option
|
||||||
|
A group header
|
||||||
|
--grouped-a-string=STRING A string flag grouped in A.
|
||||||
|
--grouped-a-bool A bool flag grouped in A.
|
||||||
|
|
||||||
|
--a-grouped-a-string A string flag grouped in A.
|
||||||
|
|
||||||
|
Group B
|
||||||
|
--grouped-b-string=STRING A string flag grouped in B.
|
||||||
|
|
||||||
|
Another group title, this time without header
|
||||||
|
--grouped-1-string=STRING A string flag grouped in 1.
|
||||||
|
--grouped-1-bool A bool flag grouped in 1.
|
||||||
|
|
||||||
|
Group 2
|
||||||
|
--grouped-2-string=STRING A string flag grouped in 2.
|
||||||
|
`
|
||||||
|
t.Log(expected)
|
||||||
|
t.Log(w.String())
|
||||||
|
require.Equal(t, expected, w.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type Kong struct {
|
|||||||
helpFormatter HelpValueFormatter
|
helpFormatter HelpValueFormatter
|
||||||
helpOptions HelpOptions
|
helpOptions HelpOptions
|
||||||
helpFlag *Flag
|
helpFlag *Flag
|
||||||
|
groups []Group
|
||||||
vars Vars
|
vars Vars
|
||||||
|
|
||||||
// Set temporarily by Options. These are applied after build().
|
// Set temporarily by Options. These are applied after build().
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ type Node struct {
|
|||||||
Name string
|
Name string
|
||||||
Help string // Short help displayed in summaries.
|
Help string // Short help displayed in summaries.
|
||||||
Detail string // Detailed help displayed when describing command/arg alone.
|
Detail string // Detailed help displayed when describing command/arg alone.
|
||||||
Group string
|
Group *Group
|
||||||
Hidden bool
|
Hidden bool
|
||||||
Flags []*Flag
|
Flags []*Flag
|
||||||
Positional []*Positional
|
Positional []*Positional
|
||||||
@@ -203,6 +203,18 @@ func (n *Node) Path() (out string) {
|
|||||||
return strings.TrimSpace(out)
|
return strings.TrimSpace(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClosestGroup finds the first non-nil group in this node and its ancestors.
|
||||||
|
func (n *Node) ClosestGroup() *Group {
|
||||||
|
switch {
|
||||||
|
case n.Group != nil:
|
||||||
|
return n.Group
|
||||||
|
case n.Parent != nil:
|
||||||
|
return n.Parent.ClosestGroup()
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A Value is either a flag or a variable positional argument.
|
// A Value is either a flag or a variable positional argument.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
Flag *Flag // Nil if positional argument.
|
Flag *Flag // Nil if positional argument.
|
||||||
@@ -351,7 +363,7 @@ type Positional = Value
|
|||||||
// A Flag represents a command-line flag.
|
// A Flag represents a command-line flag.
|
||||||
type Flag struct {
|
type Flag struct {
|
||||||
*Value
|
*Value
|
||||||
Group string // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
|
Group *Group // Logical grouping when displaying. May also be used by configuration loaders to group options logically.
|
||||||
Xor string
|
Xor string
|
||||||
PlaceHolder string
|
PlaceHolder string
|
||||||
Env string
|
Env string
|
||||||
@@ -394,6 +406,17 @@ func (f *Flag) FormatPlaceHolder() string {
|
|||||||
return strings.ToUpper(f.Name) + tail
|
return strings.ToUpper(f.Name) + tail
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group holds metadata about a command or flag group used when printing help.
|
||||||
|
type Group struct {
|
||||||
|
// Key is the `group` field tag value used to identify this group.
|
||||||
|
Key string
|
||||||
|
// Title is displayed above the grouped items.
|
||||||
|
Title string
|
||||||
|
// Header is optional and displayed under the Title when non empty.
|
||||||
|
// It can be used to introduce the group's purpose to the user.
|
||||||
|
Header string
|
||||||
|
}
|
||||||
|
|
||||||
// This is directly from the Go 1.13 source code.
|
// This is directly from the Go 1.13 source code.
|
||||||
func reflectValueIsZero(v reflect.Value) bool {
|
func reflectValueIsZero(v reflect.Value) bool {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
|
|||||||
+10
@@ -208,6 +208,16 @@ func ConfigureHelp(options HelpOptions) Option {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Groups associates `group` field tags with their metadata.
|
||||||
|
//
|
||||||
|
// It can be used to provide a title or header to a command or flag group.
|
||||||
|
func Groups(groups []Group) Option {
|
||||||
|
return OptionFunc(func(k *Kong) error {
|
||||||
|
k.groups = groups
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
|
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
|
||||||
func UsageOnError() Option {
|
func UsageOnError() Option {
|
||||||
return OptionFunc(func(k *Kong) error {
|
return OptionFunc(func(k *Kong) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user