Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(command): adding args to use line #1210

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 90 additions & 73 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ type Group struct {
// you to define the usage and description as part of your command
// definition to ensure usability.
type Command struct {
// Aliases is an array of aliases that can be used instead of the first word in Use.
Aliases []string

// SuggestFor is an array of command names for which this command will be suggested -
// similar to aliases but only suggests.
SuggestFor []string

// ValidArgs is list of all valid non-flag arguments that are accepted in shell completions
ValidArgs []string

// ArgAliases is List of aliases for ValidArgs.
// These are not suggested to the user in the shell completion,
// but accepted if entered manually.
ArgAliases []string

// groups for subcommands
commandgroups []*Group

// args is actual args parsed from flags.
args []string

// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}

// commands is the list of commands supported by this program.
commands []*Command

// Use is the one-line usage message.
// Recommended syntax is as follows:
// [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required.
Expand All @@ -57,13 +87,6 @@ type Command struct {
// Example: add [-F file | -D dir]... [-f format] profile
Use string

// Aliases is an array of aliases that can be used instead of the first word in Use.
Aliases []string

// SuggestFor is an array of command names for which this command will be suggested -
// similar to aliases but only suggests.
SuggestFor []string

// Short is the short description shown in the 'help' output.
Short string

Expand All @@ -76,38 +99,56 @@ type Command struct {
// Example is examples of how to use the command.
Example string

// ValidArgs is list of all valid non-flag arguments that are accepted in shell completions
ValidArgs []string
// 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)

// Expected arguments
Args PositionalArgs

// ArgAliases is List of aliases for ValidArgs.
// These are not suggested to the user in the shell completion,
// but accepted if entered manually.
ArgAliases []string

// BashCompletionFunction is custom bash functions used by the legacy bash autocompletion generator.
// For portability with other shells, it is recommended to instead use ValidArgsFunction
BashCompletionFunction string

// Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string

// Annotations are key/value pairs that can be used by applications to identify or
// group commands.
Annotations map[string]string

// Version defines the version for this command. If this value is non-empty and the command does not
// define a "version" flag, a "version" boolean flag will be added to the command and, if specified,
// will print content of the "Version" variable. A shorthand "v" flag will also be added if the
// command does not define one.
Version string

// usageTemplate is usage template defined by user.
usageTemplate string

// helpTemplate is help template defined by user.
helpTemplate string

// helpCommandGroupID is the group id for the helpCommand
helpCommandGroupID string

// completionCommandGroupID is the group id for the completion command
completionCommandGroupID string

// versionTemplate is the version template defined by user.
versionTemplate string

// errPrefix is the error message prefix defined by user.
errPrefix string

// inReader is a reader defined by the user that replaces stdin
inReader io.Reader
// outWriter is a writer defined by the user that replaces stdout
outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer

// 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)

// Expected arguments
Args PositionalArgs

// Annotations are key/value pairs that can be used by applications to identify or
// group commands.
Annotations map[string]string

// The *Run functions are executed in the following order:
// * PersistentPreRun()
// * PreRun()
Expand Down Expand Up @@ -139,11 +180,6 @@ type Command struct {
// PersistentPostRunE: PersistentPostRun but returns an error.
PersistentPostRunE func(cmd *Command, args []string) error

// groups for subcommands
commandgroups []*Group

// args is actual args parsed from flags.
args []string
// flagErrorBuf contains all error messages from pflag.
flagErrorBuf *bytes.Buffer
// flags is full set of flags.
Expand All @@ -162,36 +198,28 @@ type Command struct {

// usageFunc is usage func defined by user.
usageFunc func(*Command) error
// usageTemplate is usage template defined by user.
usageTemplate string
// 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
// helpFunc is help func defined by user.
helpFunc func(*Command, []string)
// helpCommand is command with usage 'help'. If it's not defined by user,
// cobra uses default help command.
helpCommand *Command
// helpCommandGroupID is the group id for the helpCommand
helpCommandGroupID string

// completionCommandGroupID is the group id for the completion command
completionCommandGroupID string
// parent is a parent command for this command.
parent *Command

// versionTemplate is the version template defined by user.
versionTemplate string
ctx context.Context

// errPrefix is the error message prefix defined by user.
errPrefix string
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int

// inReader is a reader defined by the user that replaces stdin
inReader io.Reader
// outWriter is a writer defined by the user that replaces stdout
outWriter io.Writer
// errWriter is a writer defined by the user that replaces stderr
errWriter io.Writer
// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int

// FParseErrWhitelist flag parse errors to be ignored
FParseErrWhitelist FParseErrWhitelist
Expand All @@ -201,22 +229,6 @@ type Command struct {

// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}

ctx context.Context

// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int

// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
Expand All @@ -242,13 +254,13 @@ type Command struct {
// line of a command when printing help or generating docs
DisableFlagsInUseLine bool

// DisableArgsInUseLine will disable the addition of [args] to the usage
// line of a command when printing help or generating docs
DisableArgsInUseLine bool

// DisableSuggestions disables the suggestions based on Levenshtein distance
// that go along with 'unknown command' messages.
DisableSuggestions bool

// SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions.
// Must be > 0.
SuggestionsMinimumDistance int
}

// Context returns underlying command context. If command was executed
Expand Down Expand Up @@ -1415,12 +1427,12 @@ func (c *Command) UseLine() string {
} else {
useline = c.Use
}
if c.DisableFlagsInUseLine {
return useline
}
if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") {
if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") && !c.DisableFlagsInUseLine {
useline += " [flags]"
}
if c.HasAvailableArgs() && !strings.Contains(useline, "[args]") && !c.DisableArgsInUseLine {
useline += " [args]"
}
return useline
}

Expand Down Expand Up @@ -1764,6 +1776,11 @@ func (c *Command) HasAvailableInheritedFlags() bool {
return c.InheritedFlags().HasAvailableFlags()
}

// HasAvailableArgs checks if the command has non-nil Args.
func (c *Command) HasAvailableArgs() bool {
return c.Args != nil
}

// Flag climbs up the command tree looking for matching flag.
func (c *Command) Flag(name string) (flag *flag.Flag) {
flag = c.Flags().Lookup(name)
Expand Down
100 changes: 100 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2768,3 +2768,103 @@ func TestUnknownFlagShouldReturnSameErrorRegardlessOfArgPosition(t *testing.T) {
})
}
}

func TestUseLine(t *testing.T) {
var testFlagSet pflag.FlagSet
testFlagSet.AddFlag(&pflag.Flag{
Name: "flag_0",
})
testArgs := func(cmd *Command, args []string) error { return nil }
testCases := []struct {
Cmd *Command
UseLine string
}{
{
&Command{
DisableFlagsInUseLine: true,
DisableArgsInUseLine: true,
parent: &Command{
Use: "parent_use_0",
},
flags: &testFlagSet,
Args: testArgs,
Use: "use_0",
},
"parent_use_0 use_0",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
parent: &Command{
Use: "parent_use_1",
},
Use: "use_1",
},
"parent_use_1 use_1",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
Use: "use_2",
},
"use_2",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: true,
parent: &Command{
Use: "parent_use_3",
},
flags: &testFlagSet,
Args: testArgs,
Use: "use_3",
},
"parent_use_3 use_3 [flags]",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
flags: &testFlagSet,
Args: testArgs,
Use: "use_4",
},
"use_4 [flags] [args]",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
Args: testArgs,
Use: "use_5",
},
"use_5 [args]",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
flags: &testFlagSet,
Use: "[flags] use_6",
},
"[flags] use_6",
},
{
&Command{
DisableFlagsInUseLine: false,
DisableArgsInUseLine: false,
Args: testArgs,
Use: "[args] use_7",
},
"[args] use_7",
},
}
for i, tc := range testCases {
if tc.Cmd.UseLine() != tc.UseLine {
t.Errorf("test case no. %d mismatch.\nResult: %s\nExpect: %s", i, tc.Cmd.UseLine(), tc.UseLine)
}
}
}