Add HelpValueFormatter as discussed in #60 (#61)

Fixes #60 .
This commit is contained in:
Rene Zbinden
2020-02-21 10:24:19 +01:00
committed by GitHub
parent 73ecfde9b2
commit 7b000bd775
7 changed files with 78 additions and 60 deletions
+2 -6
View File
@@ -27,7 +27,7 @@
2. [`Configuration(loader, paths...)` - load defaults from configuration files](#configurationloader-paths---load-defaults-from-configuration-files)
3. [`Resolver(...)` - support for default values from external sources](#resolver---support-for-default-values-from-external-sources)
4. [`*Mapper(...)` - customising how the command-line is mapped to Go values](#mapper---customising-how-the-command-line-is-mapped-to-go-values)
5. [`ConfigureHelp(HelpOptions)` and `Help(HelpFunc)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
5. [`ConfigureHelp(HelpOptions)`, `Help(HelpFunc)` and `HelpFormatter(HelpValueFormatter)` - customising help](#configurehelphelpoptions-and-helphelpfunc---customising-help)
6. [`Bind(...)` - bind values for callback hooks and Run() methods](#bind---bind-values-for-callback-hooks-and-run-methods)
7. [Other options](#other-options)
@@ -117,10 +117,6 @@ eg.
-f, --force Force removal.
-r, --recursive Recursively remove files.
For flags with associated environment variables, the variable `${env}` can be
interpolated into the help string. In the absence of this variable in the help,
<a id="markdown-command-handling" name="command-handling"></a>
## Command handling
@@ -463,7 +459,6 @@ are defined from the value itself:
${default}
${enum}
${env}
eg.
@@ -546,6 +541,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).
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.
<a id="markdown-bind---bind-values-for-callback-hooks-and-run-methods" name="bind---bind-values-for-callback-hooks-and-run-methods"></a>
### `Bind(...)` - bind values for callback hooks and Run() methods
+31 -10
View File
@@ -65,6 +65,25 @@ type HelpIndenter func(prefix string) string
// HelpPrinter is used to print context-sensitive help.
type HelpPrinter func(options HelpOptions, ctx *Context) error
// HelpValueFormatter is used to format the help text of flags and positional arguments.
type HelpValueFormatter func(value *Value) string
// DefaultHelpValueFormatter is the default HelpValueFormatter.
func DefaultHelpValueFormatter(value *Value) string {
if value.Tag.Env == "" {
return value.Help
}
suffix := "($" + value.Tag.Env + ")"
switch {
case strings.HasSuffix(value.Help, "."):
return value.Help[:len(value.Help)-1] + " " + suffix + "."
case value.Help == "":
return suffix
default:
return value.Help + " " + suffix
}
}
// DefaultHelpPrinter is the default HelpPrinter.
func DefaultHelpPrinter(options HelpOptions, ctx *Context) error {
if ctx.Empty() {
@@ -214,19 +233,21 @@ func printCommandSummary(w *helpWriter, cmd *Command) {
}
type helpWriter struct {
indent string
width int
lines *[]string
indent string
width int
lines *[]string
helpFormatter HelpValueFormatter
HelpOptions
}
func newHelpWriter(ctx *Context, options HelpOptions) *helpWriter {
lines := []string{}
w := &helpWriter{
indent: "",
width: guessWidth(ctx.Stdout),
lines: &lines,
HelpOptions: options,
indent: "",
width: guessWidth(ctx.Stdout),
lines: &lines,
helpFormatter: ctx.Kong.helpFormatter,
HelpOptions: options,
}
return w
}
@@ -240,7 +261,7 @@ func (h *helpWriter) Print(text string) {
}
func (h *helpWriter) Indent() *helpWriter {
return &helpWriter{indent: h.indent + " ", lines: h.lines, width: h.width - 2, HelpOptions: h.HelpOptions}
return &helpWriter{indent: h.indent + " ", lines: h.lines, width: h.width - 2, HelpOptions: h.HelpOptions, helpFormatter: h.helpFormatter}
}
func (h *helpWriter) String() string {
@@ -268,7 +289,7 @@ func (h *helpWriter) Wrap(text string) {
func writePositionals(w *helpWriter, args []*Positional) {
rows := [][2]string{}
for _, arg := range args {
rows = append(rows, [2]string{arg.Summary(), arg.Help})
rows = append(rows, [2]string{arg.Summary(), w.helpFormatter(arg)})
}
writeTwoColumns(w, rows)
}
@@ -290,7 +311,7 @@ func writeFlags(w *helpWriter, groups [][]*Flag) {
}
for _, flag := range group {
if !flag.Hidden {
rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help})
rows = append(rows, [2]string{formatFlag(haveShort, flag), w.helpFormatter(flag.Value)})
}
}
}
+29
View File
@@ -2,6 +2,7 @@ package kong_test
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/require"
@@ -219,3 +220,31 @@ Commands:
require.Equal(t, expected, w.String())
})
}
func TestEnvarAutoHelp(t *testing.T) {
var cli struct {
Flag string `env:"FLAG" help:"A flag."`
}
w := &strings.Builder{}
p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {}))
_, err := p.Parse([]string{"--help"})
require.NoError(t, err)
require.Contains(t, w.String(), "A flag ($FLAG).")
}
func TestCustomHelpFormatter(t *testing.T) {
var cli struct {
Flag string `env:"FLAG" help:"A flag."`
}
w := &strings.Builder{}
p := mustNew(t, &cli,
kong.Writers(w, w),
kong.Exit(func(int) {}),
kong.HelpFormatter(func(value *kong.Value) string {
return value.Help
}),
)
_, err := p.Parse([]string{"--help"})
require.NoError(t, err)
require.Contains(t, w.String(), "A flag.")
}
-11
View File
@@ -7,17 +7,6 @@ import (
var interpolationRegex = regexp.MustCompile(`((?:\${([[:alpha:]_][[:word:]]*))(?:=([^}]+))?})|(\$)|([^$]+)`)
// Returns true if the variable "v" is interpolated in "s".
func interpolationHasVar(s string, v string) bool {
matches := interpolationRegex.FindAllStringSubmatch(s, -1)
for _, match := range matches {
if name := match[2]; name == v {
return true
}
}
return false
}
// Interpolate variables from vars into s for substrings in the form ${var} or ${var=default}.
func interpolate(s string, vars map[string]string) (string, error) {
out := ""
+8 -22
View File
@@ -50,6 +50,7 @@ type Kong struct {
noDefaultHelp bool
usageOnError bool
help HelpPrinter
helpFormatter HelpValueFormatter
helpOptions HelpOptions
helpFlag *Flag
vars Vars
@@ -63,12 +64,13 @@ type Kong struct {
// See the README (https://github.com/alecthomas/kong) for usage instructions.
func New(grammar interface{}, options ...Option) (*Kong, error) {
k := &Kong{
Exit: os.Exit,
Stdout: os.Stdout,
Stderr: os.Stderr,
registry: NewRegistry().RegisterDefaults(),
vars: Vars{},
bindings: bindings{},
Exit: os.Exit,
Stdout: os.Stdout,
Stderr: os.Stderr,
registry: NewRegistry().RegisterDefaults(),
vars: Vars{},
bindings: bindings{},
helpFormatter: DefaultHelpValueFormatter,
}
options = append(options, Bind(k))
@@ -153,22 +155,6 @@ func (k *Kong) interpolateValue(value *Value, vars Vars) (err error) {
"default": value.Default,
"enum": value.Enum,
})
if value.Tag.Env != "" {
vars["env"] = value.Tag.Env
if !interpolationHasVar(value.Help, "env") {
suffix := "($" + value.Tag.Env + ")"
switch {
case strings.HasSuffix(value.Help, "."):
value.Help = value.Help[:len(value.Help)-1] + " " + suffix + "."
case value.Help == "":
value.Help += suffix
default:
value.Help += " " + suffix
}
}
}
if value.Help, err = interpolate(value.Help, vars); err != nil {
return fmt.Errorf("help for %s: %s", value.Summary(), err)
}
-11
View File
@@ -750,17 +750,6 @@ func TestEnvarEnumValidated(t *testing.T) {
require.EqualError(t, err, "--flag must be one of \"valid\" but got \"invalid\"")
}
func TestEnvarAutoHelp(t *testing.T) {
var cli struct {
Flag string `env:"FLAG" help:"A flag."`
}
w := &strings.Builder{}
p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {}))
_, err := p.Parse([]string{"--help"})
require.NoError(t, err)
require.Contains(t, w.String(), "A flag ($FLAG).")
}
func TestXor(t *testing.T) {
var cli struct {
Hello bool `xor:"another"`
+8
View File
@@ -158,6 +158,14 @@ func Help(help HelpPrinter) Option {
})
}
// HelpFormatter configures how the help text is formatted.
func HelpFormatter(helpFormatter HelpValueFormatter) Option {
return OptionFunc(func(k *Kong) error {
k.helpFormatter = helpFormatter
return nil
})
}
// ConfigureHelp sets the HelpOptions to use for printing help.
func ConfigureHelp(options HelpOptions) Option {
return OptionFunc(func(k *Kong) error {