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:
committed by
Alec Thomas
parent
abbc2dfc2c
commit
405b2f4fd9
@@ -41,6 +41,9 @@ type HelpOptions struct {
|
|||||||
// Tree writes command chains in a tree structure instead of listing them separately.
|
// Tree writes command chains in a tree structure instead of listing them separately.
|
||||||
Tree bool
|
Tree bool
|
||||||
|
|
||||||
|
// Place the flags after the commands listing.
|
||||||
|
FlagsLast bool
|
||||||
|
|
||||||
// Indenter modulates the given prefix for the next layer in the tree view.
|
// 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 following exported templates can be used: kong.SpaceIndenter, kong.LineIndenter, kong.TreeIndenter
|
||||||
// The kong.SpaceIndenter will be used by default.
|
// The kong.SpaceIndenter will be used by default.
|
||||||
@@ -143,20 +146,25 @@ func printNodeDetail(w *helpWriter, node *Node, hide bool) {
|
|||||||
w.Print("Arguments:")
|
w.Print("Arguments:")
|
||||||
writePositionals(w.Indent(), node.Positional)
|
writePositionals(w.Indent(), node.Positional)
|
||||||
}
|
}
|
||||||
if flags := node.AllFlags(true); len(flags) > 0 {
|
printFlags := func() {
|
||||||
groupedFlags := collectFlagGroups(flags)
|
if flags := node.AllFlags(true); len(flags) > 0 {
|
||||||
for _, group := range groupedFlags {
|
groupedFlags := collectFlagGroups(flags)
|
||||||
w.Print("")
|
for _, group := range groupedFlags {
|
||||||
if group.Metadata.Title != "" {
|
|
||||||
w.Wrap(group.Metadata.Title)
|
|
||||||
}
|
|
||||||
if group.Metadata.Description != "" {
|
|
||||||
w.Indent().Wrap(group.Metadata.Description)
|
|
||||||
w.Print("")
|
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)
|
cmds := node.Leaves(hide)
|
||||||
if len(cmds) > 0 {
|
if len(cmds) > 0 {
|
||||||
iw := w.Indent()
|
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) {
|
func writeCommandList(cmds []*Node, iw *helpWriter) {
|
||||||
|
|||||||
+109
@@ -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) {
|
func TestHelpTree(t *testing.T) {
|
||||||
// nolint: govet
|
// nolint: govet
|
||||||
var cli struct {
|
var cli struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user