mirror of
https://github.com/spf13/cobra
synced 2025-05-05 12:57:22 +00:00
Support subcommands checking for suggestions
Till now, only the root command is looked for suggestions. Now `findSuggestions` works for nested commands as well. For example ``` Error: unknown command "rewin" for "root times" Did you mean this? rewind Run 'root times --help' for usage. ``` Signed-off-by: Yuval Goldberg <yuvigoldi@gmail.com>
This commit is contained in:
parent
4fd30b69ee
commit
bbadd15b86
2 changed files with 98 additions and 26 deletions
8
args.go
8
args.go
|
@ -8,8 +8,8 @@ import (
|
|||
type PositionalArgs func(cmd *Command, args []string) error
|
||||
|
||||
// Legacy arg validation has the following behaviour:
|
||||
// - root commands with no subcommands can take arbitrary arguments
|
||||
// - root commands with subcommands will do subcommand validity checking
|
||||
// - commands with no subcommands can take arbitrary arguments
|
||||
// - commands with subcommands will do subcommand validity checking
|
||||
// - subcommands will always accept arbitrary arguments
|
||||
func legacyArgs(cmd *Command, args []string) error {
|
||||
// no subcommand, always take args
|
||||
|
@ -17,8 +17,8 @@ func legacyArgs(cmd *Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// root command with subcommands, do subcommand checking.
|
||||
if !cmd.HasParent() && len(args) > 0 {
|
||||
// do subcommand checking
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||
}
|
||||
return nil
|
||||
|
|
116
command_test.go
116
command_test.go
|
@ -1194,39 +1194,111 @@ func TestSuggestions(t *testing.T) {
|
|||
SuggestFor: []string{"counts"},
|
||||
Run: emptyRun,
|
||||
}
|
||||
rewindCmd := &Command{
|
||||
Use: "rewind",
|
||||
SuggestFor: []string{"dejavu"},
|
||||
Run: emptyRun,
|
||||
}
|
||||
timesCmd.AddCommand(rewindCmd)
|
||||
rootCmd.AddCommand(timesCmd)
|
||||
|
||||
templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n"
|
||||
templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n"
|
||||
templateSuggestions := "\nDid you mean this?\n\t%s\n\n"
|
||||
|
||||
tests := map[string]string{
|
||||
"time": "times",
|
||||
"tiems": "times",
|
||||
"tims": "times",
|
||||
"timeS": "times",
|
||||
"rimes": "times",
|
||||
"ti": "times",
|
||||
"t": "times",
|
||||
"timely": "times",
|
||||
"ri": "",
|
||||
"timezone": "",
|
||||
"foo": "",
|
||||
"counts": "times",
|
||||
templatePrefix := "Error: unknown command \"%s\" for \"%s\"\n"
|
||||
templateSuffix := "Run '%s --help' for usage.\n"
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
targetCommand string
|
||||
expectedSuggestion string
|
||||
}{
|
||||
{
|
||||
input: "time",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "tiems",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "tims",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "timeS",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "rimes",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "ti",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "t",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "timely",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "ri",
|
||||
expectedSuggestion: "",
|
||||
},
|
||||
{
|
||||
input: "timezone",
|
||||
expectedSuggestion: "",
|
||||
},
|
||||
{
|
||||
input: "foo",
|
||||
expectedSuggestion: "",
|
||||
},
|
||||
{
|
||||
input: "counts",
|
||||
expectedSuggestion: "times",
|
||||
},
|
||||
{
|
||||
input: "foo rewind",
|
||||
expectedSuggestion: "",
|
||||
},
|
||||
{
|
||||
input: "times rewin",
|
||||
targetCommand: "root times",
|
||||
expectedSuggestion: "rewind",
|
||||
},
|
||||
{
|
||||
input: "times dejavu",
|
||||
targetCommand: "root times",
|
||||
expectedSuggestion: "rewind",
|
||||
},
|
||||
}
|
||||
|
||||
for typo, suggestion := range tests {
|
||||
for index := range tests {
|
||||
for _, suggestionsDisabled := range []bool{true, false} {
|
||||
test := tests[index]
|
||||
|
||||
timesCmd.DisableSuggestions = suggestionsDisabled
|
||||
rootCmd.DisableSuggestions = suggestionsDisabled
|
||||
|
||||
var expected string
|
||||
output, _ := executeCommand(rootCmd, typo)
|
||||
args := strings.Split(test.input, " ")
|
||||
output, _ := executeCommand(rootCmd, args...)
|
||||
|
||||
if suggestion == "" || suggestionsDisabled {
|
||||
expected = fmt.Sprintf(templateWithoutSuggestions, typo)
|
||||
} else {
|
||||
expected = fmt.Sprintf(templateWithSuggestions, typo, suggestion)
|
||||
unknownArg := args[len(args)-1]
|
||||
if test.targetCommand == "" {
|
||||
test.targetCommand = rootCmd.Use
|
||||
unknownArg = args[0]
|
||||
}
|
||||
|
||||
expected := fmt.Sprintf(templatePrefix, unknownArg, test.targetCommand)
|
||||
if test.expectedSuggestion != "" && !suggestionsDisabled {
|
||||
expected += fmt.Sprintf(templateSuggestions, test.expectedSuggestion)
|
||||
}
|
||||
|
||||
expected += fmt.Sprintf(templateSuffix, test.targetCommand)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf("Unexpected response.\nExpected:\n %q\nGot:\n %q\n", expected, output)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue