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

Support subcommands checking for suggestions #1500

Closed
wants to merge 1 commit into from
Closed
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
8 changes: 4 additions & 4 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ 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
if !cmd.HasSubCommands() {
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
Expand Down
122 changes: 97 additions & 25 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

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",
}

for typo, suggestion := range tests {
templateSuggestions := "\nDid you mean this?\n\t%s\n\n"

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 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)
}
Expand Down