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

Request for comments: In-built autocompletion for shell #867

Closed
amitsaha opened this issue May 25, 2019 · 3 comments
Closed

Request for comments: In-built autocompletion for shell #867

amitsaha opened this issue May 25, 2019 · 3 comments

Comments

@amitsaha
Copy link

Hi all, since a cobra CLI application already knows what commands/sub-commands and flags it can handle, why not use the cmd API to implement auto-completion itself? That is, say for BASH, we register $complete -C mycli mycli - that is mycli is invoked whenever I type in mycli <TAB> and then I have logic in mycli itself to hint to bash what are the possible options for auto-completion. Here's how I implemented it in my main.go of a standard cobra CLI app:


func main() {
	// handle bash autocomplete
	if len(os.Getenv("COMP_LINE")) != 0 {
		if len(os.Getenv("COMP_DEBUG")) != 0 {
			fmt.Printf("%#v\n", os.Getenv("COMP_LINE"))
		}
		compLine := strings.Split(os.Getenv("COMP_LINE"), " ")

		// we only handle auto complete, if we are the command
		// being invoked
		if compLine[0] == cmd.RootCmd.Name() {
			var cmdArgs []string
			if len(compLine) > 1 {
				cmdArgs = compLine[1:]
			} else {
				cmdArgs = compLine[0:]
			}
			c, _, _ := cmd.RootCmd.Find(cmdArgs)
			suggestions := c.SuggestionsFor(cmdArgs[len(cmdArgs)-1])
			for _, s := range suggestions {
				fmt.Printf("%s\n", s)
			}

			localNonPersistentFlags := c.LocalNonPersistentFlags()
			c.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
				if nonCompletableFlag(flag) {
					return
				}
				if strings.HasPrefix(fmt.Sprintf("--%s", flag.Name), cmdArgs[len(cmdArgs)-1]) {
					fmt.Printf("--%s\n", flag.Name)
				}
				if localNonPersistentFlags.Lookup(flag.Name) != nil {
					if strings.HasPrefix(fmt.Sprintf("--%s", flag.Name), cmdArgs[len(cmdArgs)-1]) {
						fmt.Printf("--%s\n", flag.Name)
					}
				}
			})

			c.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
				if nonCompletableFlag(flag) {
					return
				}
				if strings.HasPrefix(flag.Name, cmdArgs[len(cmdArgs)-1]) {
					fmt.Printf("--%s\n", flag.Name)
				}
				if len(flag.Shorthand) > 0 {
					if strings.HasPrefix(flag.Name, cmdArgs[len(cmdArgs)-1]) {
						fmt.Printf("--%s\n", flag.Name)
					}
				}
			})

		}

	} else {
		cmd.Execute()
	}
}

It seems to be working for me.

Thoughts on this?

@github-actions
Copy link

github-actions bot commented Apr 6, 2020

This issue is being marked as stale due to a long period of inactivity

@marckhouzam
Copy link
Collaborator

@amitsaha I just fell upon this suggestion. You had a great idea! Coincidentally, Cobra has actually started using this technique to provide Fish shell completion (#1048). The same is being applied for zsh completion in #1070.

A hidden command was used to achieve this. So you can do things like:
myprog __complete sub<ENTER>
to obtain the completion.

@marckhouzam
Copy link
Collaborator

marckhouzam commented Jul 14, 2020

This suggestion is actually being used by Cobra. I believe this issue can be closed . /cc @jpmcb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants