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

Remove __complete cmd for programs without subcmds #1563

Merged
merged 1 commit into from Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion completions.go
Expand Up @@ -228,7 +228,17 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
if c.Root().TraverseChildren {
finalCmd, finalArgs, err = c.Root().Traverse(trimmedArgs)
} else {
finalCmd, finalArgs, err = c.Root().Find(trimmedArgs)
// For Root commands that don't specify any value for their Args fields, when we call
// Find(), if those Root commands don't have any sub-commands, they will accept arguments.
// However, because we have added the __complete sub-command in the current code path, the
// call to Find() -> legacyArgs() will return an error if there are any arguments.
// To avoid this, we first remove the __complete command to get back to having no sub-commands.
rootCmd := c.Root()
if len(rootCmd.Commands()) == 1 {
rootCmd.RemoveCommand(c)
}

finalCmd, finalArgs, err = rootCmd.Find(trimmedArgs)
}
if err != nil {
// Unable to find the real command. E.g., <program> someInvalidCmd <TAB>
Expand Down
45 changes: 45 additions & 0 deletions completions_test.go
Expand Up @@ -2619,3 +2619,48 @@ func TestCompleteWithDisableFlagParsing(t *testing.T) {
t.Errorf("expected: %q, got: %q", expected, output)
}
}

func TestCompleteWithRootAndLegacyArgs(t *testing.T) {
// Test a lonely root command which uses legacyArgs(). In such a case, the root
// command should accept any number of arguments and completion should behave accordingly.
rootCmd := &Command{
Use: "root",
Args: nil, // Args must be nil to trigger the legacyArgs() function
Run: emptyRun,
ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
return []string{"arg1", "arg2"}, ShellCompDirectiveNoFileComp
},
}

// Make sure the first arg is completed
output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

expected := strings.Join([]string{
"arg1",
"arg2",
":4",
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")

if output != expected {
t.Errorf("expected: %q, got: %q", expected, output)
}

// Make sure the completion of arguments continues
output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "arg1", "")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

expected = strings.Join([]string{
"arg1",
"arg2",
":4",
"Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n")

if output != expected {
t.Errorf("expected: %q, got: %q", expected, output)
}
}