Help Option to position flags in help output

When FlagsLast is set to true, flags are printed after commands.

- Added HelpOptions.FlagsLast bool
- Added test TestFlagsLast
This commit is contained in:
Jaco Coetzee
2021-02-09 10:21:40 +02:00
committed by Alec Thomas
parent abbc2dfc2c
commit 405b2f4fd9
2 changed files with 130 additions and 10 deletions
+21 -10
View File
@@ -41,6 +41,9 @@ type HelpOptions struct {
// Tree writes command chains in a tree structure instead of listing them separately.
Tree bool
// Place the flags after the commands listing.
FlagsLast bool
// Indenter modulates the given prefix for the next layer in the tree view.
// The following exported templates can be used: kong.SpaceIndenter, kong.LineIndenter, kong.TreeIndenter
// The kong.SpaceIndenter will be used by default.
@@ -143,20 +146,25 @@ func printNodeDetail(w *helpWriter, node *Node, hide bool) {
w.Print("Arguments:")
writePositionals(w.Indent(), node.Positional)
}
if flags := node.AllFlags(true); len(flags) > 0 {
groupedFlags := collectFlagGroups(flags)
for _, group := range groupedFlags {
w.Print("")
if group.Metadata.Title != "" {
w.Wrap(group.Metadata.Title)
}
if group.Metadata.Description != "" {
w.Indent().Wrap(group.Metadata.Description)
printFlags := func() {
if flags := node.AllFlags(true); len(flags) > 0 {
groupedFlags := collectFlagGroups(flags)
for _, group := range groupedFlags {
w.Print("")
if group.Metadata.Title != "" {
w.Wrap(group.Metadata.Title)
}
if group.Metadata.Description != "" {
w.Indent().Wrap(group.Metadata.Description)
w.Print("")
}
writeFlags(w.Indent(), group.Flags)
}
writeFlags(w.Indent(), group.Flags)
}
}
if !w.FlagsLast {
printFlags()
}
cmds := node.Leaves(hide)
if len(cmds) > 0 {
iw := w.Indent()
@@ -184,6 +192,9 @@ func printNodeDetail(w *helpWriter, node *Node, hide bool) {
}
}
}
if w.FlagsLast {
printFlags()
}
}
func writeCommandList(cmds []*Node, iw *helpWriter) {
+109
View File
@@ -126,6 +126,115 @@ Flags:
})
}
func TestFlagsLast(t *testing.T) {
// nolint: govet
var cli struct {
String string `help:"A string flag."`
Bool bool `help:"A bool flag with very long help that wraps a lot and is verbose and is really verbose."`
Slice []string `help:"A slice of strings." placeholder:"STR"`
Map map[string]int `help:"A map of strings to ints."`
Required bool `required help:"A required flag."`
One struct {
Flag string `help:"Nested flag."`
} `cmd help:"A subcommand."`
Two struct {
Flag string `help:"Nested flag under two."`
RequiredTwo bool `required`
Three threeArg `arg help:"Sub-sub-arg."`
Four struct {
} `cmd help:"Sub-sub-command."`
} `cmd help:"Another subcommand."`
}
w := bytes.NewBuffer(nil)
exited := false
app := mustNew(t, &cli,
kong.Name("test-app"),
kong.Description("A test app."),
kong.HelpOptions{
FlagsLast: true,
},
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.NoError(t, err)
})
require.True(t, exited)
expected := `Usage: test-app --required <command>
A test app.
Commands:
one --required
A subcommand.
two <three> --required --required-two --required-three
Sub-sub-arg.
two four --required --required-two
Sub-sub-command.
Flags:
-h, --help Show context-sensitive help.
--string=STRING A string flag.
--bool A bool flag with very long help that wraps a lot
and is verbose and is really verbose.
--slice=STR,... A slice of strings.
--map=KEY=VALUE;... A map of strings to ints.
--required A required flag.
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", "hello", "--help"})
require.NoError(t, err)
})
require.True(t, exited)
expected := `Usage: test-app two <three> --required --required-two --required-three
Sub-sub-arg.
Detailed help provided through the HelpProvider interface.
Flags:
-h, --help Show context-sensitive help.
--string=STRING A string flag.
--bool A bool flag with very long help that wraps a lot
and is verbose and is really verbose.
--slice=STR,... A slice of strings.
--map=KEY=VALUE;... A map of strings to ints.
--required A required flag.
--flag=STRING Nested flag under two.
--required-two
--required-three
`
t.Log(expected)
t.Log(w.String())
require.Equal(t, expected, w.String())
})
}
func TestHelpTree(t *testing.T) {
// nolint: govet
var cli struct {