Skip to content

Commit

Permalink
options: Add kong.ShortUsageOnError() option
Browse files Browse the repository at this point in the history
Add kong.ShortUsageOnError() option similar to kong.UsageOnError().
Add tests for UsageOnError and ShortUsageOnError.

Remove the HelpOption summary reset at the beginning of DefaultHelpPrinter:

	 func DefaultHelpPrinter(options HelpOptions, ctx *Context) error {
	-       if ctx.Empty() {
	-               options.Summary = false
	-       }

⚠️ I'm not really sure what the implications of this are, tests still
seem to pass, but maybe this has unintended side effects.
  • Loading branch information
Julia Ogris authored and alecthomas committed Feb 16, 2021
1 parent f306ae1 commit 1300b2a
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 6 deletions.
15 changes: 15 additions & 0 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ func DefaultHelpValueFormatter(value *Value) string {
}
}

// DefaultShortHelpPrinter is the default HelpPrinter for short help on error.
func DefaultShortHelpPrinter(options HelpOptions, ctx *Context) error {
w := newHelpWriter(ctx, options)
cmd := ctx.Selected()
app := ctx.Model
if cmd == nil {
w.Printf("Usage: %s%s", app.Name, app.Summary())
w.Printf(`Run "%s --help" for more information.`, app.Name)
} else {
w.Printf("Usage: %s %s", app.Name, cmd.Summary())
w.Printf(`Run "%s --help" for more information.`, cmd.FullPath())
}
return w.Write(ctx.Stdout)
}

// DefaultHelpPrinter is the default HelpPrinter.
func DefaultHelpPrinter(options HelpOptions, ctx *Context) error {
if ctx.Empty() {
Expand Down
78 changes: 78 additions & 0 deletions help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kong_test

import (
"bytes"
"fmt"
"strings"
"testing"

Expand Down Expand Up @@ -512,3 +513,80 @@ Group 2
require.Equal(t, expected, w.String())
})
}

func TestUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.UsageOnError(),
)
_, err := p.Parse([]string{})
p.FatalIfErrorf(err)

expected := `Usage: test --flag=STRING
Some description.
Flags:
-h, --help Show context-sensitive help.
--flag=STRING A required flag.
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}

func TestShortUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.ShortUsageOnError(),
)
_, err := p.Parse([]string{})
require.Error(t, err)
p.FatalIfErrorf(err)

expected := `Usage: test --flag=STRING
Run "test --help" for more information.
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}

func TestCustomShortUsageOnError(t *testing.T) {
var cli struct {
Flag string `help:"A required flag." required`
}
w := &strings.Builder{}
shortHelp := func(_ kong.HelpOptions, ctx *kong.Context) error {
fmt.Fprintln(ctx.Stdout, "🤷 wish I could help")
return nil
}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Description("Some description."),
kong.Exit(func(int) {}),
kong.ShortHelp(shortHelp),
kong.ShortUsageOnError(),
)
_, err := p.Parse([]string{})
require.Error(t, err)
p.FatalIfErrorf(err)

expected := `🤷 wish I could help
test: error: missing flags: --flag=STRING
`
require.Equal(t, expected, w.String())
}
27 changes: 22 additions & 5 deletions kong.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ func Must(ast interface{}, options ...Option) *Kong {
return k
}

type usageOnError int

const (
shortUsage usageOnError = iota + 1
fullUsage
)

// Kong is the main parser type.
type Kong struct {
// Grammar model.
Expand All @@ -48,8 +55,9 @@ type Kong struct {
registry *Registry

noDefaultHelp bool
usageOnError bool
usageOnError usageOnError
help HelpPrinter
shortHelp HelpPrinter
helpFormatter HelpValueFormatter
helpOptions HelpOptions
helpFlag *Flag
Expand Down Expand Up @@ -86,6 +94,10 @@ func New(grammar interface{}, options ...Option) (*Kong, error) {
k.help = DefaultHelpPrinter
}

if k.shortHelp == nil {
k.shortHelp = DefaultShortHelpPrinter
}

model, err := build(k, grammar)
if err != nil {
return k, err
Expand Down Expand Up @@ -331,10 +343,15 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) {
msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error()
}
// Maybe display usage information.
if err, ok := err.(*ParseError); ok && k.usageOnError {
options := k.helpOptions
_ = k.help(options, err.Context)
fmt.Fprintln(k.Stdout)
if err, ok := err.(*ParseError); ok {
switch k.usageOnError {
case fullUsage:
_ = k.help(k.helpOptions, err.Context)
fmt.Fprintln(k.Stdout)
case shortUsage:
_ = k.shortHelp(k.helpOptions, err.Context)
fmt.Fprintln(k.Stdout)
}
}
k.Fatalf("%s", msg)
}
Expand Down
23 changes: 22 additions & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,17 @@ func Help(help HelpPrinter) Option {
})
}

// ShortHelp configures the short usage message.
//
// It should be used together with kong.ShortUsageOnError() to display a
// custom short usage message on errors.
func ShortHelp(shortHelp HelpPrinter) Option {
return OptionFunc(func(k *Kong) error {
k.shortHelp = shortHelp
return nil
})
}

// HelpFormatter configures how the help text is formatted.
func HelpFormatter(helpFormatter HelpValueFormatter) Option {
return OptionFunc(func(k *Kong) error {
Expand Down Expand Up @@ -251,7 +262,17 @@ func ExplicitGroups(groups []Group) Option {
// UsageOnError configures Kong to display context-sensitive usage if FatalIfErrorf is called with an error.
func UsageOnError() Option {
return OptionFunc(func(k *Kong) error {
k.usageOnError = true
k.usageOnError = fullUsage
return nil
})
}

// ShortUsageOnError configures Kong to display context-sensitive short
// usage if FatalIfErrorf is called with an error. The default short
// usage message can be overridden with kong.ShortHelp(...).
func ShortUsageOnError() Option {
return OptionFunc(func(k *Kong) error {
k.usageOnError = shortUsage
return nil
})
}
Expand Down

0 comments on commit 1300b2a

Please sign in to comment.