⬆️ Update dependencies
This commit is contained in:
+231
-60
@@ -14,12 +14,14 @@ const (
|
||||
)
|
||||
|
||||
// Help flag.
|
||||
type helpValue bool
|
||||
type helpFlag bool
|
||||
|
||||
func (h helpValue) BeforeApply(ctx *Context) error {
|
||||
func (h helpFlag) IgnoreDefault() {}
|
||||
|
||||
func (h helpFlag) BeforeReset(ctx *Context) error {
|
||||
options := ctx.Kong.helpOptions
|
||||
options.Summary = false
|
||||
err := ctx.Kong.help(options, ctx)
|
||||
err := ctx.printHelp(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -41,10 +43,28 @@ 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.
|
||||
Indenter HelpIndenter
|
||||
|
||||
// Don't show the help associated with subcommands
|
||||
NoExpandSubcommands bool
|
||||
|
||||
// Clamp the help wrap width to a value smaller than the terminal width.
|
||||
// If this is set to a non-positive number, the terminal width is used; otherwise,
|
||||
// the min of this value or the terminal width is used.
|
||||
WrapUpperBound int
|
||||
|
||||
// ValueFormatter is used to format the help text of flags and positional arguments.
|
||||
ValueFormatter HelpValueFormatter
|
||||
|
||||
// NoAppDescFormat skips all formatting of app description text (wrapping, paragraph
|
||||
// reflow, newline merging, etc).
|
||||
NoAppDescFormat bool
|
||||
}
|
||||
|
||||
// Apply options to Kong as a configuration option.
|
||||
@@ -59,6 +79,11 @@ type HelpProvider interface {
|
||||
Help() string
|
||||
}
|
||||
|
||||
// PlaceHolderProvider can be implemented by mappers to provide custom placeholder text.
|
||||
type PlaceHolderProvider interface {
|
||||
PlaceHolder(flag *Flag) string
|
||||
}
|
||||
|
||||
// HelpIndenter is used to indent new layers in the help tree.
|
||||
type HelpIndenter func(prefix string) string
|
||||
|
||||
@@ -70,10 +95,10 @@ type HelpValueFormatter func(value *Value) string
|
||||
|
||||
// DefaultHelpValueFormatter is the default HelpValueFormatter.
|
||||
func DefaultHelpValueFormatter(value *Value) string {
|
||||
if value.Tag.Env == "" {
|
||||
if len(value.Tag.Envs) == 0 || HasInterpolatedVar(value.OrigHelp, "env") {
|
||||
return value.Help
|
||||
}
|
||||
suffix := "($" + value.Tag.Env + ")"
|
||||
suffix := "(" + formatEnvs(value.Tag.Envs) + ")"
|
||||
switch {
|
||||
case strings.HasSuffix(value.Help, "."):
|
||||
return value.Help[:len(value.Help)-1] + " " + suffix + "."
|
||||
@@ -84,6 +109,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() {
|
||||
@@ -126,10 +166,16 @@ func printCommand(w *helpWriter, app *Application, cmd *Command) {
|
||||
}
|
||||
}
|
||||
|
||||
func printNodeDetail(w *helpWriter, node *Node, hide bool) {
|
||||
func printNodeDetail(w *helpWriter, node *Node, hide bool) { //nolint:gocyclo
|
||||
if node.Help != "" {
|
||||
w.Print("")
|
||||
w.Wrap(node.Help)
|
||||
if w.NoAppDescFormat && node.Parent == nil {
|
||||
for _, line := range strings.Split(node.Help, "\n") {
|
||||
w.Print(line)
|
||||
}
|
||||
} else {
|
||||
w.Wrap(node.Help)
|
||||
}
|
||||
}
|
||||
if w.Summary {
|
||||
return
|
||||
@@ -143,26 +189,60 @@ func printNodeDetail(w *helpWriter, node *Node, hide bool) {
|
||||
w.Print("Arguments:")
|
||||
writePositionals(w.Indent(), node.Positional)
|
||||
}
|
||||
if flags := node.AllFlags(true); len(flags) > 0 {
|
||||
w.Print("")
|
||||
w.Print("Flags:")
|
||||
writeFlags(w.Indent(), flags)
|
||||
}
|
||||
cmds := node.Leaves(hide)
|
||||
if len(cmds) > 0 {
|
||||
w.Print("")
|
||||
w.Print("Commands:")
|
||||
if w.Tree {
|
||||
writeCommandTree(w, node)
|
||||
} else {
|
||||
iw := w.Indent()
|
||||
if w.Compact {
|
||||
writeCompactCommandList(cmds, iw)
|
||||
} else {
|
||||
writeCommandList(cmds, iw)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !w.FlagsLast {
|
||||
printFlags()
|
||||
}
|
||||
var cmds []*Node
|
||||
if w.NoExpandSubcommands {
|
||||
cmds = node.Children
|
||||
} else {
|
||||
cmds = node.Leaves(hide)
|
||||
}
|
||||
if len(cmds) > 0 {
|
||||
iw := w.Indent()
|
||||
if w.Tree {
|
||||
w.Print("")
|
||||
w.Print("Commands:")
|
||||
writeCommandTree(iw, node)
|
||||
} else {
|
||||
groupedCmds := collectCommandGroups(cmds)
|
||||
for _, group := range groupedCmds {
|
||||
w.Print("")
|
||||
if group.Metadata.Title != "" {
|
||||
w.Wrap(group.Metadata.Title)
|
||||
}
|
||||
if group.Metadata.Description != "" {
|
||||
w.Indent().Wrap(group.Metadata.Description)
|
||||
w.Print("")
|
||||
}
|
||||
|
||||
if w.Compact {
|
||||
writeCompactCommandList(group.Commands, iw)
|
||||
} else {
|
||||
writeCommandList(group.Commands, iw)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if w.FlagsLast {
|
||||
printFlags()
|
||||
}
|
||||
}
|
||||
|
||||
func writeCommandList(cmds []*Node, iw *helpWriter) {
|
||||
@@ -189,7 +269,6 @@ func writeCompactCommandList(cmds []*Node, iw *helpWriter) {
|
||||
}
|
||||
|
||||
func writeCommandTree(w *helpWriter, node *Node) {
|
||||
iw := w.Indent()
|
||||
rows := make([][2]string, 0, len(node.Children)*2)
|
||||
for i, cmd := range node.Children {
|
||||
if cmd.Hidden {
|
||||
@@ -200,27 +279,93 @@ func writeCommandTree(w *helpWriter, node *Node) {
|
||||
rows = append(rows, [2]string{"", ""})
|
||||
}
|
||||
}
|
||||
writeTwoColumns(iw, rows)
|
||||
writeTwoColumns(w, rows)
|
||||
}
|
||||
|
||||
type helpFlagGroup struct {
|
||||
Metadata *Group
|
||||
Flags [][]*Flag
|
||||
}
|
||||
|
||||
func collectFlagGroups(flags [][]*Flag) []helpFlagGroup {
|
||||
// Group keys in order of appearance.
|
||||
groups := []*Group{}
|
||||
// Flags grouped by their group key.
|
||||
flagsByGroup := map[string][][]*Flag{}
|
||||
|
||||
for _, levelFlags := range flags {
|
||||
levelFlagsByGroup := map[string][]*Flag{}
|
||||
|
||||
for _, flag := range levelFlags {
|
||||
key := ""
|
||||
if flag.Group != nil {
|
||||
key = flag.Group.Key
|
||||
groupAlreadySeen := false
|
||||
for _, group := range groups {
|
||||
if key == group.Key {
|
||||
groupAlreadySeen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !groupAlreadySeen {
|
||||
groups = append(groups, flag.Group)
|
||||
}
|
||||
}
|
||||
|
||||
levelFlagsByGroup[key] = append(levelFlagsByGroup[key], flag)
|
||||
}
|
||||
|
||||
for key, flags := range levelFlagsByGroup {
|
||||
flagsByGroup[key] = append(flagsByGroup[key], flags)
|
||||
}
|
||||
}
|
||||
|
||||
out := []helpFlagGroup{}
|
||||
// Ungrouped flags are always displayed first.
|
||||
if ungroupedFlags, ok := flagsByGroup[""]; ok {
|
||||
out = append(out, helpFlagGroup{
|
||||
Metadata: &Group{Title: "Flags:"},
|
||||
Flags: ungroupedFlags,
|
||||
})
|
||||
}
|
||||
for _, group := range groups {
|
||||
out = append(out, helpFlagGroup{Metadata: group, Flags: flagsByGroup[group.Key]})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// nolint: unused
|
||||
type helpCommandGroup struct {
|
||||
Name string
|
||||
Metadata *Group
|
||||
Commands []*Node
|
||||
}
|
||||
|
||||
// nolint: unused, deadcode
|
||||
func collectCommandGroups(nodes []*Node) []helpCommandGroup {
|
||||
groups := map[string][]*Node{}
|
||||
// Groups in order of appearance.
|
||||
groups := []*Group{}
|
||||
// Nodes grouped by their group key.
|
||||
nodesByGroup := map[string][]*Node{}
|
||||
|
||||
for _, node := range nodes {
|
||||
groups[node.Group] = append(groups[node.Group], node)
|
||||
}
|
||||
out := []helpCommandGroup{}
|
||||
for name, nodes := range groups {
|
||||
if name == "" {
|
||||
name = "Commands"
|
||||
key := ""
|
||||
if group := node.ClosestGroup(); group != nil {
|
||||
key = group.Key
|
||||
if _, ok := nodesByGroup[key]; !ok {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
out = append(out, helpCommandGroup{Name: name, Commands: nodes})
|
||||
nodesByGroup[key] = append(nodesByGroup[key], node)
|
||||
}
|
||||
|
||||
out := []helpCommandGroup{}
|
||||
// Ungrouped nodes are always displayed first.
|
||||
if ungroupedNodes, ok := nodesByGroup[""]; ok {
|
||||
out = append(out, helpCommandGroup{
|
||||
Metadata: &Group{Title: "Commands:"},
|
||||
Commands: ungroupedNodes,
|
||||
})
|
||||
}
|
||||
for _, group := range groups {
|
||||
out = append(out, helpCommandGroup{Metadata: group, Commands: nodesByGroup[group.Key]})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -233,26 +378,28 @@ func printCommandSummary(w *helpWriter, cmd *Command) {
|
||||
}
|
||||
|
||||
type helpWriter struct {
|
||||
indent string
|
||||
width int
|
||||
lines *[]string
|
||||
helpFormatter HelpValueFormatter
|
||||
indent string
|
||||
width int
|
||||
lines *[]string
|
||||
HelpOptions
|
||||
}
|
||||
|
||||
func newHelpWriter(ctx *Context, options HelpOptions) *helpWriter {
|
||||
lines := []string{}
|
||||
wrapWidth := guessWidth(ctx.Stdout)
|
||||
if options.WrapUpperBound > 0 && wrapWidth > options.WrapUpperBound {
|
||||
wrapWidth = options.WrapUpperBound
|
||||
}
|
||||
w := &helpWriter{
|
||||
indent: "",
|
||||
width: guessWidth(ctx.Stdout),
|
||||
lines: &lines,
|
||||
helpFormatter: ctx.Kong.helpFormatter,
|
||||
HelpOptions: options,
|
||||
indent: "",
|
||||
width: wrapWidth,
|
||||
lines: &lines,
|
||||
HelpOptions: options,
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (h *helpWriter) Printf(format string, args ...interface{}) {
|
||||
func (h *helpWriter) Printf(format string, args ...any) {
|
||||
h.Print(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
@@ -260,8 +407,9 @@ func (h *helpWriter) Print(text string) {
|
||||
*h.lines = append(*h.lines, strings.TrimRight(h.indent+text, " "))
|
||||
}
|
||||
|
||||
// Indent returns a new helpWriter indented by two characters.
|
||||
func (h *helpWriter) Indent() *helpWriter {
|
||||
return &helpWriter{indent: h.indent + " ", lines: h.lines, width: h.width - 2, HelpOptions: h.HelpOptions, helpFormatter: h.helpFormatter}
|
||||
return &helpWriter{indent: h.indent + " ", lines: h.lines, width: h.width - 2, HelpOptions: h.HelpOptions}
|
||||
}
|
||||
|
||||
func (h *helpWriter) String() string {
|
||||
@@ -280,7 +428,7 @@ func (h *helpWriter) Write(w io.Writer) error {
|
||||
|
||||
func (h *helpWriter) Wrap(text string) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
doc.ToText(w, strings.TrimSpace(text), "", " ", h.width)
|
||||
doc.ToText(w, strings.TrimSpace(text), "", " ", h.width) //nolint:staticcheck // cross-package links not possible
|
||||
for _, line := range strings.Split(strings.TrimSpace(w.String()), "\n") {
|
||||
h.Print(line)
|
||||
}
|
||||
@@ -289,7 +437,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(), w.helpFormatter(arg)})
|
||||
rows = append(rows, [2]string{arg.Summary(), w.HelpOptions.ValueFormatter(arg)})
|
||||
}
|
||||
writeTwoColumns(w, rows)
|
||||
}
|
||||
@@ -311,7 +459,7 @@ func writeFlags(w *helpWriter, groups [][]*Flag) {
|
||||
}
|
||||
for _, flag := range group {
|
||||
if !flag.Hidden {
|
||||
rows = append(rows, [2]string{formatFlag(haveShort, flag), w.helpFormatter(flag.Value)})
|
||||
rows = append(rows, [2]string{formatFlag(haveShort, flag), w.HelpOptions.ValueFormatter(flag.Value)})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +483,7 @@ func writeTwoColumns(w *helpWriter, rows [][2]string) {
|
||||
|
||||
for _, row := range rows {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
doc.ToText(buf, row[1], "", strings.Repeat(" ", defaultIndent), w.width-leftSize-defaultColumnPadding)
|
||||
doc.ToText(buf, row[1], "", strings.Repeat(" ", defaultIndent), w.width-leftSize-defaultColumnPadding) //nolint:staticcheck // cross-package links not possible
|
||||
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
|
||||
|
||||
line := fmt.Sprintf("%-*s", leftSize, row[0])
|
||||
@@ -355,16 +503,24 @@ func formatFlag(haveShort bool, flag *Flag) string {
|
||||
flagString := ""
|
||||
name := flag.Name
|
||||
isBool := flag.IsBool()
|
||||
isCounter := flag.IsCounter()
|
||||
|
||||
short := ""
|
||||
if flag.Short != 0 {
|
||||
flagString += fmt.Sprintf("-%c, --%s", flag.Short, name)
|
||||
} else {
|
||||
if haveShort {
|
||||
flagString += fmt.Sprintf(" --%s", name)
|
||||
} else {
|
||||
flagString += fmt.Sprintf("--%s", name)
|
||||
}
|
||||
short = "-" + string(flag.Short) + ", "
|
||||
} else if haveShort {
|
||||
short = " "
|
||||
}
|
||||
if !isBool {
|
||||
|
||||
if isBool && flag.Tag.Negatable == negatableDefault {
|
||||
name = "[no-]" + name
|
||||
} else if isBool && flag.Tag.Negatable != "" {
|
||||
name += "/" + flag.Tag.Negatable
|
||||
}
|
||||
|
||||
flagString += fmt.Sprintf("%s--%s", short, name)
|
||||
|
||||
if !isBool && !isCounter {
|
||||
flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
|
||||
}
|
||||
return flagString
|
||||
@@ -376,6 +532,9 @@ func (h *HelpOptions) CommandTree(node *Node, prefix string) (rows [][2]string)
|
||||
switch node.Type {
|
||||
default:
|
||||
nodeName += prefix + node.Name
|
||||
if len(node.Aliases) != 0 {
|
||||
nodeName += fmt.Sprintf(" (%s)", strings.Join(node.Aliases, ","))
|
||||
}
|
||||
case ArgumentNode:
|
||||
nodeName += prefix + "<" + node.Name + ">"
|
||||
}
|
||||
@@ -389,6 +548,9 @@ func (h *HelpOptions) CommandTree(node *Node, prefix string) (rows [][2]string)
|
||||
rows = append(rows, [2]string{prefix + arg.Summary(), arg.Help})
|
||||
}
|
||||
for _, subCmd := range node.Children {
|
||||
if subCmd.Hidden {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, h.CommandTree(subCmd, prefix)...)
|
||||
}
|
||||
return
|
||||
@@ -414,3 +576,12 @@ func TreeIndenter(prefix string) string {
|
||||
}
|
||||
return "|" + strings.Repeat(" ", defaultIndent) + prefix
|
||||
}
|
||||
|
||||
func formatEnvs(envs []string) string {
|
||||
formatted := make([]string, len(envs))
|
||||
for i := range envs {
|
||||
formatted[i] = "$" + envs[i]
|
||||
}
|
||||
|
||||
return strings.Join(formatted, ", ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user