|
|
@@ -33,10 +33,13 @@ import (
|
|
|
const (
|
|
|
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
|
|
|
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
|
|
|
+
|
|
|
+ helpFlagName = "help"
|
|
|
+ helpCommandName = "help"
|
|
|
)
|
|
|
|
|
|
// FParseErrWhitelist configures Flag parse errors to be ignored
|
|
|
-type FParseErrWhitelist flag.ParseErrorsWhitelist
|
|
|
+type FParseErrWhitelist flag.ParseErrorsAllowlist
|
|
|
|
|
|
// Group Structure to manage groups for commands
|
|
|
type Group struct {
|
|
|
@@ -80,11 +83,11 @@ type Command struct {
|
|
|
Example string
|
|
|
|
|
|
// ValidArgs is list of all valid non-flag arguments that are accepted in shell completions
|
|
|
- ValidArgs []string
|
|
|
+ ValidArgs []Completion
|
|
|
// ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion.
|
|
|
// It is a dynamic version of using ValidArgs.
|
|
|
// Only one of ValidArgs and ValidArgsFunction can be used for a command.
|
|
|
- ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)
|
|
|
+ ValidArgsFunction CompletionFunc
|
|
|
|
|
|
// Expected arguments
|
|
|
Args PositionalArgs
|
|
|
@@ -154,8 +157,10 @@ type Command struct {
|
|
|
// pflags contains persistent flags.
|
|
|
pflags *flag.FlagSet
|
|
|
// lflags contains local flags.
|
|
|
+ // This field does not represent internal state, it's used as a cache to optimise LocalFlags function call
|
|
|
lflags *flag.FlagSet
|
|
|
// iflags contains inherited flags.
|
|
|
+ // This field does not represent internal state, it's used as a cache to optimise InheritedFlags function call
|
|
|
iflags *flag.FlagSet
|
|
|
// parentsPflags is all persistent flags of cmd's parents.
|
|
|
parentsPflags *flag.FlagSet
|
|
|
@@ -166,12 +171,12 @@ type Command struct {
|
|
|
// usageFunc is usage func defined by user.
|
|
|
usageFunc func(*Command) error
|
|
|
// usageTemplate is usage template defined by user.
|
|
|
- usageTemplate string
|
|
|
+ usageTemplate *tmplFunc
|
|
|
// flagErrorFunc is func defined by user and it's called when the parsing of
|
|
|
// flags returns an error.
|
|
|
flagErrorFunc func(*Command, error) error
|
|
|
// helpTemplate is help template defined by user.
|
|
|
- helpTemplate string
|
|
|
+ helpTemplate *tmplFunc
|
|
|
// helpFunc is help func defined by user.
|
|
|
helpFunc func(*Command, []string)
|
|
|
// helpCommand is command with usage 'help'. If it's not defined by user,
|
|
|
@@ -184,7 +189,7 @@ type Command struct {
|
|
|
completionCommandGroupID string
|
|
|
|
|
|
// versionTemplate is the version template defined by user.
|
|
|
- versionTemplate string
|
|
|
+ versionTemplate *tmplFunc
|
|
|
|
|
|
// errPrefix is the error message prefix defined by user.
|
|
|
errPrefix string
|
|
|
@@ -279,6 +284,7 @@ func (c *Command) SetArgs(a []string) {
|
|
|
|
|
|
// SetOutput sets the destination for usage and error messages.
|
|
|
// If output is nil, os.Stderr is used.
|
|
|
+//
|
|
|
// Deprecated: Use SetOut and/or SetErr instead
|
|
|
func (c *Command) SetOutput(output io.Writer) {
|
|
|
c.outWriter = output
|
|
|
@@ -310,7 +316,11 @@ func (c *Command) SetUsageFunc(f func(*Command) error) {
|
|
|
|
|
|
// SetUsageTemplate sets usage template. Can be defined by Application.
|
|
|
func (c *Command) SetUsageTemplate(s string) {
|
|
|
- c.usageTemplate = s
|
|
|
+ if s == "" {
|
|
|
+ c.usageTemplate = nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c.usageTemplate = tmpl(s)
|
|
|
}
|
|
|
|
|
|
// SetFlagErrorFunc sets a function to generate an error when flag parsing
|
|
|
@@ -346,12 +356,20 @@ func (c *Command) SetCompletionCommandGroupID(groupID string) {
|
|
|
|
|
|
// SetHelpTemplate sets help template to be used. Application can use it to set custom template.
|
|
|
func (c *Command) SetHelpTemplate(s string) {
|
|
|
- c.helpTemplate = s
|
|
|
+ if s == "" {
|
|
|
+ c.helpTemplate = nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c.helpTemplate = tmpl(s)
|
|
|
}
|
|
|
|
|
|
// SetVersionTemplate sets version template to be used. Application can use it to set custom template.
|
|
|
func (c *Command) SetVersionTemplate(s string) {
|
|
|
- c.versionTemplate = s
|
|
|
+ if s == "" {
|
|
|
+ c.versionTemplate = nil
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c.versionTemplate = tmpl(s)
|
|
|
}
|
|
|
|
|
|
// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix.
|
|
|
@@ -432,7 +450,8 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
|
|
}
|
|
|
return func(c *Command) error {
|
|
|
c.mergePersistentFlags()
|
|
|
- err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c)
|
|
|
+ fn := c.getUsageTemplateFunc()
|
|
|
+ err := fn(c.OutOrStderr(), c)
|
|
|
if err != nil {
|
|
|
c.PrintErrln(err)
|
|
|
}
|
|
|
@@ -440,6 +459,19 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// getUsageTemplateFunc returns the usage template function for the command
|
|
|
+// going up the command tree if necessary.
|
|
|
+func (c *Command) getUsageTemplateFunc() func(w io.Writer, data interface{}) error {
|
|
|
+ if c.usageTemplate != nil {
|
|
|
+ return c.usageTemplate.fn
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.HasParent() {
|
|
|
+ return c.parent.getUsageTemplateFunc()
|
|
|
+ }
|
|
|
+ return defaultUsageFunc
|
|
|
+}
|
|
|
+
|
|
|
// Usage puts out the usage for the command.
|
|
|
// Used when a user provides invalid input.
|
|
|
// Can be defined by user by overriding UsageFunc.
|
|
|
@@ -458,15 +490,30 @@ func (c *Command) HelpFunc() func(*Command, []string) {
|
|
|
}
|
|
|
return func(c *Command, a []string) {
|
|
|
c.mergePersistentFlags()
|
|
|
+ fn := c.getHelpTemplateFunc()
|
|
|
// The help should be sent to stdout
|
|
|
// See https://github.com/spf13/cobra/issues/1002
|
|
|
- err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c)
|
|
|
+ err := fn(c.OutOrStdout(), c)
|
|
|
if err != nil {
|
|
|
c.PrintErrln(err)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// getHelpTemplateFunc returns the help template function for the command
|
|
|
+// going up the command tree if necessary.
|
|
|
+func (c *Command) getHelpTemplateFunc() func(w io.Writer, data interface{}) error {
|
|
|
+ if c.helpTemplate != nil {
|
|
|
+ return c.helpTemplate.fn
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.HasParent() {
|
|
|
+ return c.parent.getHelpTemplateFunc()
|
|
|
+ }
|
|
|
+
|
|
|
+ return defaultHelpFunc
|
|
|
+}
|
|
|
+
|
|
|
// Help puts out the help for the command.
|
|
|
// Used when a user calls help [command].
|
|
|
// Can be defined by user by overriding HelpFunc.
|
|
|
@@ -510,7 +557,7 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-var minUsagePadding = 25
|
|
|
+const minUsagePadding = 25
|
|
|
|
|
|
// UsagePadding return padding for the usage.
|
|
|
func (c *Command) UsagePadding() int {
|
|
|
@@ -520,7 +567,7 @@ func (c *Command) UsagePadding() int {
|
|
|
return c.parent.commandsMaxUseLen
|
|
|
}
|
|
|
|
|
|
-var minCommandPathPadding = 11
|
|
|
+const minCommandPathPadding = 11
|
|
|
|
|
|
// CommandPathPadding return padding for the command path.
|
|
|
func (c *Command) CommandPathPadding() int {
|
|
|
@@ -530,7 +577,7 @@ func (c *Command) CommandPathPadding() int {
|
|
|
return c.parent.commandsMaxCommandPathLen
|
|
|
}
|
|
|
|
|
|
-var minNamePadding = 11
|
|
|
+const minNamePadding = 11
|
|
|
|
|
|
// NamePadding returns padding for the name.
|
|
|
func (c *Command) NamePadding() int {
|
|
|
@@ -541,71 +588,55 @@ func (c *Command) NamePadding() int {
|
|
|
}
|
|
|
|
|
|
// UsageTemplate returns usage template for the command.
|
|
|
+// This function is kept for backwards-compatibility reasons.
|
|
|
func (c *Command) UsageTemplate() string {
|
|
|
- if c.usageTemplate != "" {
|
|
|
- return c.usageTemplate
|
|
|
+ if c.usageTemplate != nil {
|
|
|
+ return c.usageTemplate.tmpl
|
|
|
}
|
|
|
|
|
|
if c.HasParent() {
|
|
|
return c.parent.UsageTemplate()
|
|
|
}
|
|
|
- return `Usage:{{if .Runnable}}
|
|
|
- {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
|
|
- {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
|
|
-
|
|
|
-Aliases:
|
|
|
- {{.NameAndAliases}}{{end}}{{if .HasExample}}
|
|
|
-
|
|
|
-Examples:
|
|
|
-{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
|
|
|
-
|
|
|
-Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
|
|
- {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
|
|
|
-
|
|
|
-{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
|
|
|
- {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
|
|
-
|
|
|
-Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
|
|
- {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
|
|
-
|
|
|
-Flags:
|
|
|
-{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
|
|
-
|
|
|
-Global Flags:
|
|
|
-{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
|
|
-
|
|
|
-Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
|
|
- {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
|
|
-
|
|
|
-Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
|
|
|
-`
|
|
|
+ return defaultUsageTemplate
|
|
|
}
|
|
|
|
|
|
// HelpTemplate return help template for the command.
|
|
|
+// This function is kept for backwards-compatibility reasons.
|
|
|
func (c *Command) HelpTemplate() string {
|
|
|
- if c.helpTemplate != "" {
|
|
|
- return c.helpTemplate
|
|
|
+ if c.helpTemplate != nil {
|
|
|
+ return c.helpTemplate.tmpl
|
|
|
}
|
|
|
|
|
|
if c.HasParent() {
|
|
|
return c.parent.HelpTemplate()
|
|
|
}
|
|
|
- return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
|
|
|
-
|
|
|
-{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
|
|
+ return defaultHelpTemplate
|
|
|
}
|
|
|
|
|
|
// VersionTemplate return version template for the command.
|
|
|
+// This function is kept for backwards-compatibility reasons.
|
|
|
func (c *Command) VersionTemplate() string {
|
|
|
- if c.versionTemplate != "" {
|
|
|
- return c.versionTemplate
|
|
|
+ if c.versionTemplate != nil {
|
|
|
+ return c.versionTemplate.tmpl
|
|
|
}
|
|
|
|
|
|
if c.HasParent() {
|
|
|
return c.parent.VersionTemplate()
|
|
|
}
|
|
|
- return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
|
|
|
-`
|
|
|
+ return defaultVersionTemplate
|
|
|
+}
|
|
|
+
|
|
|
+// getVersionTemplateFunc returns the version template function for the command
|
|
|
+// going up the command tree if necessary.
|
|
|
+func (c *Command) getVersionTemplateFunc() func(w io.Writer, data interface{}) error {
|
|
|
+ if c.versionTemplate != nil {
|
|
|
+ return c.versionTemplate.fn
|
|
|
+ }
|
|
|
+
|
|
|
+ if c.HasParent() {
|
|
|
+ return c.parent.getVersionTemplateFunc()
|
|
|
+ }
|
|
|
+ return defaultVersionFunc
|
|
|
}
|
|
|
|
|
|
// ErrPrefix return error message prefix for the command
|
|
|
@@ -706,7 +737,7 @@ Loop:
|
|
|
// This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so,
|
|
|
// return the args, excluding the one at this position.
|
|
|
if s == x {
|
|
|
- ret := []string{}
|
|
|
+ ret := make([]string, 0, len(args)-1)
|
|
|
ret = append(ret, args[:pos]...)
|
|
|
ret = append(ret, args[pos+1:]...)
|
|
|
return ret
|
|
|
@@ -754,14 +785,14 @@ func (c *Command) findSuggestions(arg string) string {
|
|
|
if c.SuggestionsMinimumDistance <= 0 {
|
|
|
c.SuggestionsMinimumDistance = 2
|
|
|
}
|
|
|
- suggestionsString := ""
|
|
|
+ var sb strings.Builder
|
|
|
if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
|
|
|
- suggestionsString += "\n\nDid you mean this?\n"
|
|
|
+ sb.WriteString("\n\nDid you mean this?\n")
|
|
|
for _, s := range suggestions {
|
|
|
- suggestionsString += fmt.Sprintf("\t%v\n", s)
|
|
|
+ _, _ = fmt.Fprintf(&sb, "\t%v\n", s)
|
|
|
}
|
|
|
}
|
|
|
- return suggestionsString
|
|
|
+ return sb.String()
|
|
|
}
|
|
|
|
|
|
func (c *Command) findNext(next string) *Command {
|
|
|
@@ -873,7 +904,7 @@ func (c *Command) ArgsLenAtDash() int {
|
|
|
|
|
|
func (c *Command) execute(a []string) (err error) {
|
|
|
if c == nil {
|
|
|
- return fmt.Errorf("Called Execute() on a nil Command")
|
|
|
+ return fmt.Errorf("called Execute() on a nil Command")
|
|
|
}
|
|
|
|
|
|
if len(c.Deprecated) > 0 {
|
|
|
@@ -892,7 +923,7 @@ func (c *Command) execute(a []string) (err error) {
|
|
|
|
|
|
// If help is called, regardless of other flags, return we want help.
|
|
|
// Also say we need help if the command isn't runnable.
|
|
|
- helpVal, err := c.Flags().GetBool("help")
|
|
|
+ helpVal, err := c.Flags().GetBool(helpFlagName)
|
|
|
if err != nil {
|
|
|
// should be impossible to get here as we always declare a help
|
|
|
// flag in InitDefaultHelpFlag()
|
|
|
@@ -912,7 +943,8 @@ func (c *Command) execute(a []string) (err error) {
|
|
|
return err
|
|
|
}
|
|
|
if versionVal {
|
|
|
- err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c)
|
|
|
+ fn := c.getVersionTemplateFunc()
|
|
|
+ err := fn(c.OutOrStdout(), c)
|
|
|
if err != nil {
|
|
|
c.Println(err)
|
|
|
}
|
|
|
@@ -1066,12 +1098,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
|
|
|
|
|
// initialize help at the last point to allow for user overriding
|
|
|
c.InitDefaultHelpCmd()
|
|
|
- // initialize completion at the last point to allow for user overriding
|
|
|
- c.InitDefaultCompletionCmd()
|
|
|
-
|
|
|
- // Now that all commands have been created, let's make sure all groups
|
|
|
- // are properly created also
|
|
|
- c.checkCommandGroups()
|
|
|
|
|
|
args := c.args
|
|
|
|
|
|
@@ -1080,9 +1106,16 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
|
|
args = os.Args[1:]
|
|
|
}
|
|
|
|
|
|
- // initialize the hidden command to be used for shell completion
|
|
|
+ // initialize the __complete command to be used for shell completion
|
|
|
c.initCompleteCmd(args)
|
|
|
|
|
|
+ // initialize the default completion command
|
|
|
+ c.InitDefaultCompletionCmd(args...)
|
|
|
+
|
|
|
+ // Now that all commands have been created, let's make sure all groups
|
|
|
+ // are properly created also
|
|
|
+ c.checkCommandGroups()
|
|
|
+
|
|
|
var flags []string
|
|
|
if c.TraverseChildren {
|
|
|
cmd, flags, err = c.Traverse(args)
|
|
|
@@ -1185,15 +1218,16 @@ func (c *Command) checkCommandGroups() {
|
|
|
// If c already has help flag, it will do nothing.
|
|
|
func (c *Command) InitDefaultHelpFlag() {
|
|
|
c.mergePersistentFlags()
|
|
|
- if c.Flags().Lookup("help") == nil {
|
|
|
+ if c.Flags().Lookup(helpFlagName) == nil {
|
|
|
usage := "help for "
|
|
|
- if c.Name() == "" {
|
|
|
+ name := c.DisplayName()
|
|
|
+ if name == "" {
|
|
|
usage += "this command"
|
|
|
} else {
|
|
|
- usage += c.Name()
|
|
|
+ usage += name
|
|
|
}
|
|
|
- c.Flags().BoolP("help", "h", false, usage)
|
|
|
- _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"})
|
|
|
+ c.Flags().BoolP(helpFlagName, "h", false, usage)
|
|
|
+ _ = c.Flags().SetAnnotation(helpFlagName, FlagSetByCobraAnnotation, []string{"true"})
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1212,7 +1246,7 @@ func (c *Command) InitDefaultVersionFlag() {
|
|
|
if c.Name() == "" {
|
|
|
usage += "this command"
|
|
|
} else {
|
|
|
- usage += c.Name()
|
|
|
+ usage += c.DisplayName()
|
|
|
}
|
|
|
if c.Flags().ShorthandLookup("v") == nil {
|
|
|
c.Flags().BoolP("version", "v", false, usage)
|
|
|
@@ -1236,9 +1270,9 @@ func (c *Command) InitDefaultHelpCmd() {
|
|
|
Use: "help [command]",
|
|
|
Short: "Help about any command",
|
|
|
Long: `Help provides help for any command in the application.
|
|
|
-Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
|
|
- ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
|
|
- var completions []string
|
|
|
+Simply type ` + c.DisplayName() + ` help [path to command] for full details.`,
|
|
|
+ ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) {
|
|
|
+ var completions []Completion
|
|
|
cmd, _, e := c.Root().Find(args)
|
|
|
if e != nil {
|
|
|
return nil, ShellCompDirectiveNoFileComp
|
|
|
@@ -1250,7 +1284,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
|
|
for _, subCmd := range cmd.Commands() {
|
|
|
if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand {
|
|
|
if strings.HasPrefix(subCmd.Name(), toComplete) {
|
|
|
- completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short))
|
|
|
+ completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1262,6 +1296,11 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
|
|
c.Printf("Unknown help topic %#q\n", args)
|
|
|
CheckErr(c.Root().Usage())
|
|
|
} else {
|
|
|
+ // FLow the context down to be used in help text
|
|
|
+ if cmd.ctx == nil {
|
|
|
+ cmd.ctx = c.ctx
|
|
|
+ }
|
|
|
+
|
|
|
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
|
|
cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown
|
|
|
CheckErr(cmd.Help())
|
|
|
@@ -1427,6 +1466,12 @@ func (c *Command) CommandPath() string {
|
|
|
if c.HasParent() {
|
|
|
return c.Parent().CommandPath() + " " + c.Name()
|
|
|
}
|
|
|
+ return c.DisplayName()
|
|
|
+}
|
|
|
+
|
|
|
+// DisplayName returns the name to display in help text. Returns command Name()
|
|
|
+// If CommandDisplayNameAnnoation is not set
|
|
|
+func (c *Command) DisplayName() string {
|
|
|
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
|
|
|
return displayName
|
|
|
}
|
|
|
@@ -1436,10 +1481,11 @@ func (c *Command) CommandPath() string {
|
|
|
// UseLine puts out the full usage for a given command (including parents).
|
|
|
func (c *Command) UseLine() string {
|
|
|
var useline string
|
|
|
+ use := strings.Replace(c.Use, c.Name(), c.DisplayName(), 1)
|
|
|
if c.HasParent() {
|
|
|
- useline = c.parent.CommandPath() + " " + c.Use
|
|
|
+ useline = c.parent.CommandPath() + " " + use
|
|
|
} else {
|
|
|
- useline = c.Use
|
|
|
+ useline = use
|
|
|
}
|
|
|
if c.DisableFlagsInUseLine {
|
|
|
return useline
|
|
|
@@ -1452,7 +1498,6 @@ func (c *Command) UseLine() string {
|
|
|
|
|
|
// DebugFlags used to determine which flags have been assigned to which commands
|
|
|
// and which persist.
|
|
|
-// nolint:goconst
|
|
|
func (c *Command) DebugFlags() {
|
|
|
c.Println("DebugFlags called on", c.Name())
|
|
|
var debugflags func(*Command)
|
|
|
@@ -1642,7 +1687,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f
|
|
|
// to this command (local and persistent declared here and by all parents).
|
|
|
func (c *Command) Flags() *flag.FlagSet {
|
|
|
if c.flags == nil {
|
|
|
- c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
if c.flagErrorBuf == nil {
|
|
|
c.flagErrorBuf = new(bytes.Buffer)
|
|
|
}
|
|
|
@@ -1653,10 +1698,11 @@ func (c *Command) Flags() *flag.FlagSet {
|
|
|
}
|
|
|
|
|
|
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands.
|
|
|
+// This function does not modify the flags of the current command, it's purpose is to return the current state.
|
|
|
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
|
|
persistentFlags := c.PersistentFlags()
|
|
|
|
|
|
- out := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ out := flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
c.LocalFlags().VisitAll(func(f *flag.Flag) {
|
|
|
if persistentFlags.Lookup(f.Name) == nil {
|
|
|
out.AddFlag(f)
|
|
|
@@ -1666,11 +1712,12 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
|
|
}
|
|
|
|
|
|
// LocalFlags returns the local FlagSet specifically set in the current command.
|
|
|
+// This function does not modify the flags of the current command, it's purpose is to return the current state.
|
|
|
func (c *Command) LocalFlags() *flag.FlagSet {
|
|
|
c.mergePersistentFlags()
|
|
|
|
|
|
if c.lflags == nil {
|
|
|
- c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.lflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
if c.flagErrorBuf == nil {
|
|
|
c.flagErrorBuf = new(bytes.Buffer)
|
|
|
}
|
|
|
@@ -1693,11 +1740,12 @@ func (c *Command) LocalFlags() *flag.FlagSet {
|
|
|
}
|
|
|
|
|
|
// InheritedFlags returns all flags which were inherited from parent commands.
|
|
|
+// This function does not modify the flags of the current command, it's purpose is to return the current state.
|
|
|
func (c *Command) InheritedFlags() *flag.FlagSet {
|
|
|
c.mergePersistentFlags()
|
|
|
|
|
|
if c.iflags == nil {
|
|
|
- c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.iflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
if c.flagErrorBuf == nil {
|
|
|
c.flagErrorBuf = new(bytes.Buffer)
|
|
|
}
|
|
|
@@ -1718,6 +1766,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
|
|
|
}
|
|
|
|
|
|
// NonInheritedFlags returns all flags which were not inherited from parent commands.
|
|
|
+// This function does not modify the flags of the current command, it's purpose is to return the current state.
|
|
|
func (c *Command) NonInheritedFlags() *flag.FlagSet {
|
|
|
return c.LocalFlags()
|
|
|
}
|
|
|
@@ -1725,7 +1774,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet {
|
|
|
// PersistentFlags returns the persistent FlagSet specifically set in the current command.
|
|
|
func (c *Command) PersistentFlags() *flag.FlagSet {
|
|
|
if c.pflags == nil {
|
|
|
- c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
if c.flagErrorBuf == nil {
|
|
|
c.flagErrorBuf = new(bytes.Buffer)
|
|
|
}
|
|
|
@@ -1738,9 +1787,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
|
|
|
func (c *Command) ResetFlags() {
|
|
|
c.flagErrorBuf = new(bytes.Buffer)
|
|
|
c.flagErrorBuf.Reset()
|
|
|
- c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
c.flags.SetOutput(c.flagErrorBuf)
|
|
|
- c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
c.pflags.SetOutput(c.flagErrorBuf)
|
|
|
|
|
|
c.lflags = nil
|
|
|
@@ -1828,7 +1877,7 @@ func (c *Command) ParseFlags(args []string) error {
|
|
|
c.mergePersistentFlags()
|
|
|
|
|
|
// do it here after merging all flags and just before parse
|
|
|
- c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist)
|
|
|
+ c.Flags().ParseErrorsAllowlist = flag.ParseErrorsAllowlist(c.FParseErrWhitelist)
|
|
|
|
|
|
err := c.Flags().Parse(args)
|
|
|
// Print warnings if they occurred (e.g. deprecated flag messages).
|
|
|
@@ -1857,7 +1906,7 @@ func (c *Command) mergePersistentFlags() {
|
|
|
// If c.parentsPflags == nil, it makes new.
|
|
|
func (c *Command) updateParentsPflags() {
|
|
|
if c.parentsPflags == nil {
|
|
|
- c.parentsPflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
|
|
+ c.parentsPflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError)
|
|
|
c.parentsPflags.SetOutput(c.flagErrorBuf)
|
|
|
c.parentsPflags.SortFlags = false
|
|
|
}
|
|
|
@@ -1883,3 +1932,141 @@ func commandNameMatches(s string, t string) bool {
|
|
|
|
|
|
return s == t
|
|
|
}
|
|
|
+
|
|
|
+// tmplFunc holds a template and a function that will execute said template.
|
|
|
+type tmplFunc struct {
|
|
|
+ tmpl string
|
|
|
+ fn func(io.Writer, interface{}) error
|
|
|
+}
|
|
|
+
|
|
|
+const defaultUsageTemplate = `Usage:{{if .Runnable}}
|
|
|
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
|
|
+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
|
|
+
|
|
|
+Aliases:
|
|
|
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
|
|
|
+
|
|
|
+Examples:
|
|
|
+{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
|
|
|
+
|
|
|
+Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
|
|
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
|
|
|
+
|
|
|
+{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
|
|
|
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
|
|
+
|
|
|
+Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
|
|
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
|
|
+
|
|
|
+Flags:
|
|
|
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
|
|
+
|
|
|
+Global Flags:
|
|
|
+{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
|
|
+
|
|
|
+Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
|
|
+ {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
|
|
+
|
|
|
+Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
|
|
|
+`
|
|
|
+
|
|
|
+// defaultUsageFunc is equivalent to executing defaultUsageTemplate. The two should be changed in sync.
|
|
|
+func defaultUsageFunc(w io.Writer, in interface{}) error {
|
|
|
+ c := in.(*Command)
|
|
|
+ fmt.Fprint(w, "Usage:")
|
|
|
+ if c.Runnable() {
|
|
|
+ fmt.Fprintf(w, "\n %s", c.UseLine())
|
|
|
+ }
|
|
|
+ if c.HasAvailableSubCommands() {
|
|
|
+ fmt.Fprintf(w, "\n %s [command]", c.CommandPath())
|
|
|
+ }
|
|
|
+ if len(c.Aliases) > 0 {
|
|
|
+ fmt.Fprintf(w, "\n\nAliases:\n")
|
|
|
+ fmt.Fprintf(w, " %s", c.NameAndAliases())
|
|
|
+ }
|
|
|
+ if c.HasExample() {
|
|
|
+ fmt.Fprintf(w, "\n\nExamples:\n")
|
|
|
+ fmt.Fprintf(w, "%s", c.Example)
|
|
|
+ }
|
|
|
+ if c.HasAvailableSubCommands() {
|
|
|
+ cmds := c.Commands()
|
|
|
+ if len(c.Groups()) == 0 {
|
|
|
+ fmt.Fprintf(w, "\n\nAvailable Commands:")
|
|
|
+ for _, subcmd := range cmds {
|
|
|
+ if subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName {
|
|
|
+ fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for _, group := range c.Groups() {
|
|
|
+ fmt.Fprintf(w, "\n\n%s", group.Title)
|
|
|
+ for _, subcmd := range cmds {
|
|
|
+ if subcmd.GroupID == group.ID && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) {
|
|
|
+ fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !c.AllChildCommandsHaveGroup() {
|
|
|
+ fmt.Fprintf(w, "\n\nAdditional Commands:")
|
|
|
+ for _, subcmd := range cmds {
|
|
|
+ if subcmd.GroupID == "" && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) {
|
|
|
+ fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if c.HasAvailableLocalFlags() {
|
|
|
+ fmt.Fprintf(w, "\n\nFlags:\n")
|
|
|
+ fmt.Fprint(w, trimRightSpace(c.LocalFlags().FlagUsages()))
|
|
|
+ }
|
|
|
+ if c.HasAvailableInheritedFlags() {
|
|
|
+ fmt.Fprintf(w, "\n\nGlobal Flags:\n")
|
|
|
+ fmt.Fprint(w, trimRightSpace(c.InheritedFlags().FlagUsages()))
|
|
|
+ }
|
|
|
+ if c.HasHelpSubCommands() {
|
|
|
+ fmt.Fprintf(w, "\n\nAdditional help topics:")
|
|
|
+ for _, subcmd := range c.Commands() {
|
|
|
+ if subcmd.IsAdditionalHelpTopicCommand() {
|
|
|
+ fmt.Fprintf(w, "\n %s %s", rpad(subcmd.CommandPath(), subcmd.CommandPathPadding()), subcmd.Short)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if c.HasAvailableSubCommands() {
|
|
|
+ fmt.Fprintf(w, "\n\nUse \"%s [command] --help\" for more information about a command.", c.CommandPath())
|
|
|
+ }
|
|
|
+ fmt.Fprintln(w)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+const defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
|
|
|
+
|
|
|
+{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
|
|
+
|
|
|
+// defaultHelpFunc is equivalent to executing defaultHelpTemplate. The two should be changed in sync.
|
|
|
+func defaultHelpFunc(w io.Writer, in interface{}) error {
|
|
|
+ c := in.(*Command)
|
|
|
+ usage := c.Long
|
|
|
+ if usage == "" {
|
|
|
+ usage = c.Short
|
|
|
+ }
|
|
|
+ usage = trimRightSpace(usage)
|
|
|
+ if usage != "" {
|
|
|
+ fmt.Fprintln(w, usage)
|
|
|
+ fmt.Fprintln(w)
|
|
|
+ }
|
|
|
+ if c.Runnable() || c.HasSubCommands() {
|
|
|
+ fmt.Fprint(w, c.UsageString())
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+const defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}}
|
|
|
+`
|
|
|
+
|
|
|
+// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync.
|
|
|
+func defaultVersionFunc(w io.Writer, in interface{}) error {
|
|
|
+ c := in.(*Command)
|
|
|
+ _, err := fmt.Fprintf(w, "%s version %s\n", c.DisplayName(), c.Version)
|
|
|
+ return err
|
|
|
+}
|