Support detailed help.

Any command/arg implementing the HelpProvider interface will be used to
provide arbitrarily detailed help.
This commit is contained in:
Alec Thomas
2018-08-14 14:41:27 +10:00
parent 27d2a08e53
commit f0bd1294a7
4 changed files with 31 additions and 5 deletions
+6
View File
@@ -6,6 +6,8 @@ import (
"strings" "strings"
) )
var helpProviderType = reflect.TypeOf((*HelpProvider)(nil)).Elem()
func build(k *Kong, ast interface{}) (app *Application, err error) { func build(k *Kong, ast interface{}) (app *Application, err error) {
defer catch(&err) defer catch(&err)
v := reflect.ValueOf(ast) v := reflect.ValueOf(ast)
@@ -107,6 +109,10 @@ func buildChild(k *Kong, node *Node, typ NodeType, v reflect.Value, ft reflect.S
child.Help = tag.Help child.Help = tag.Help
child.Hidden = tag.Hidden child.Hidden = tag.Hidden
if fv.Type().Implements(helpProviderType) {
child.Detail = fv.Interface().(HelpProvider).Help()
}
// A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that // A branching argument. This is a bit hairy, as we let buildNode() do the parsing, then check that
// a positional argument is provided to the child, and move it to the branching argument field. // a positional argument is provided to the child, and move it to the branching argument field.
if tag.Arg { if tag.Arg {
+10
View File
@@ -25,6 +25,12 @@ type HelpOptions struct {
Compact bool Compact bool
} }
// HelpProvider can be implemented by commands/args to provide detailed help.
type HelpProvider interface {
// This string is formatted by go/doc and thus has the same formatting rules.
Help() string
}
// HelpPrinter is used to print context-sensitive help. // HelpPrinter is used to print context-sensitive help.
type HelpPrinter func(options HelpOptions, ctx *Context) error type HelpPrinter func(options HelpOptions, ctx *Context) error
@@ -77,6 +83,10 @@ func printNodeDetail(w *helpWriter, node *Node) {
if w.Summary { if w.Summary {
return return
} }
if node.Detail != "" {
w.Print("")
w.Wrap(node.Detail)
}
if len(node.Positional) > 0 { if len(node.Positional) > 0 {
w.Print("") w.Print("")
w.Print("Arguments:") w.Print("Arguments:")
+13 -4
View File
@@ -9,6 +9,16 @@ import (
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
) )
// nolint: govet
type threeArg struct {
RequiredThree bool `required`
Three string `arg`
}
func (threeArg) Help() string {
return `Detailed help provided through the HelpProvider interface.`
}
func TestHelp(t *testing.T) { func TestHelp(t *testing.T) {
// nolint: govet // nolint: govet
var cli struct { var cli struct {
@@ -26,10 +36,7 @@ func TestHelp(t *testing.T) {
Flag string `help:"Nested flag under two."` Flag string `help:"Nested flag under two."`
RequiredTwo bool `required` RequiredTwo bool `required`
Three struct { Three threeArg `arg help:"Sub-sub-arg."`
RequiredThree bool `required`
Three string `arg`
} `arg help:"Sub-sub-arg."`
Four struct { Four struct {
} `cmd help:"Sub-sub-command."` } `cmd help:"Sub-sub-command."`
@@ -95,6 +102,8 @@ Run "test-app <command> --help" for more information on a command.
Sub-sub-arg. Sub-sub-arg.
Detailed help provided through the HelpProvider interface.
Flags: Flags:
--help Show context-sensitive help. --help Show context-sensitive help.
--string=STRING A string flag. --string=STRING A string flag.
+2 -1
View File
@@ -35,7 +35,8 @@ type Node struct {
Type NodeType Type NodeType
Parent *Node Parent *Node
Name string Name string
Help string Help string // Short help displayed in summaries.
Detail string // Detailed help displayed when describing command/arg alone.
Hidden bool Hidden bool
Flags []*Flag Flags []*Flag
Positional []*Positional Positional []*Positional