Skip to content

Instantly share code, notes, and snippets.

@icholy
Last active January 9, 2022 19:19
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save icholy/5314423 to your computer and use it in GitHub Desktop.
Save icholy/5314423 to your computer and use it in GitHub Desktop.
Zsh Tab Completion for Golang

Zsh Tab Completion for Golang

This is pretty specific to my setup but the idea can be adapted to work with pretty much anything.

Go has a flag package which makes parsing command line arguments really easy.

package main

import "flag"
import "fmt"

var param1 = flag.String("param1", "default-value", "a description for param1")
var param2 = flag.Int64("param2", 123, "a description for param2")

func init() {
  flag.Parse()
}

func main() {
  fmt.Printf("param1: %s, param2 %d", *param1, *param2)
}

Zsh lets you define custom tab completion scripts. Here's one for the example program above. foo is what triggers the completion. So the program would have to be called foo and be somewhere in your PATH.

#compdef foo

_arguments -s \
  '-param1[a description for param1]' \
  '-param2[a description for param2]'

This is cool, but typing these out is a pain so let's automate it! The flag package has a VisitAll method which lets you iterate over all defined flags. Using that we can easily create the completion script.

func PrintCompletions(name string) {
  var cmpl []string
  flag.VisitAll(func(f *flag.Flag) {
    cmpl = append(
      cmpl, fmt.Sprintf("\t'-%s[%s]' \\\n", f.Name, f.Usage))
  })
  fmt.Printf("#compdef %s\n\n_arguments -s \\\n%s\n\n",
		name, strings.Join(cmpl, " "))
}

Here's a complete example go playground

package main

import (
  "flag"
  "fmt"
  "strings"
)

var (
  param1   = flag.String("param1", "foo", "this is param1, it's useless")
  param2   = flag.String("param2", "foo", "this is param2, even more useless")
  complete = flag.Bool("complete", true, "output zsh completions")
)

func init() {
  flag.Parse()
}

func PrintCompletions(name string) {
  var cmpl []string
  flag.VisitAll(func(f *flag.Flag) {
    cmpl = append(
      cmpl, fmt.Sprintf("\t'-%s[%s]' \\\n", f.Name, f.Usage))
  })
  fmt.Print(
    fmt.Sprintf("#compdef %s\n\n_arguments -s \\\n%s\n\n",
      name, strings.Join(cmpl, " ")),
  )
}

func main() {
  if *complete {
    PrintCompletions("main")
    return
  }
}

To use it, just pipe the output into a new completion script

$ myapp -complete > ~/.zsh/completion/_myapp

If you need more info about zsh completion scripts, check out this article.

@rmmh
Copy link

rmmh commented Apr 4, 2013

if you instead parsed the output of -help into a completion definition you wouldn't have to bake zsh support into you code

@icholy
Copy link
Author

icholy commented Apr 4, 2013

@rmmh that was actually the first thing I tried. This was just easier.

@leahneukirchen
Copy link

Since --help also works for programs using the flag package, this is superflous:

% compdef _gnu_generic godoc
% godoc -<TAB>
-goroot          -- Go root directory
-html            -- print HTML in command-line mode
-http            -- HTTP service address (e.g., '-6060')
-index           -- enable search index
-index_files     -- glob pattern specifying index files;if not empty, the index
-index_throttle  -- index throttle value; 0.0 = no time allocated, 1.0 = full t
-maxresults      -- maximum number of full text search results shown
-path            -- additional package directories (colon-separated)
-q               -- arguments are considered search queries
-server          -- webserver address for command line searches
-src             -- print (exported) source in command-line mode
-tabwidth        -- tab width
-templates       -- directory containing alternate template files
-testdir         -- Go root subdirectory - for testing only (faster startups)
-timestamps      -- show timestamps with directory listings
-url             -- print HTML for named URL
-v               -- verbose mode
-write_index     -- write index to a file; the file name must be specified with
-zip             -- zip file providing the file system to serve; disabled if em

@icholy
Copy link
Author

icholy commented Apr 5, 2013

@chneukirchen I didn't know you could do that. That's awesome!

@icholy
Copy link
Author

icholy commented Apr 5, 2013

Since this ended up being useless, here's a zsh completion for godoc https://gist.github.com/icholy/5320428

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment