1// Package complete provides a tool for bash writing bash completion in go. 2// 3// Writing bash completion scripts is a hard work. This package provides an easy way 4// to create bash completion scripts for any command, and also an easy way to install/uninstall 5// the completion of the command. 6package complete 7 8import ( 9 "flag" 10 "fmt" 11 "io" 12 "os" 13 14 "github.com/posener/complete/cmd" 15 "github.com/posener/complete/match" 16) 17 18const ( 19 envComplete = "COMP_LINE" 20 envDebug = "COMP_DEBUG" 21) 22 23// Complete structs define completion for a command with CLI options 24type Complete struct { 25 Command Command 26 cmd.CLI 27 Out io.Writer 28} 29 30// New creates a new complete command. 31// name is the name of command we want to auto complete. 32// IMPORTANT: it must be the same name - if the auto complete 33// completes the 'go' command, name must be equal to "go". 34// command is the struct of the command completion. 35func New(name string, command Command) *Complete { 36 return &Complete{ 37 Command: command, 38 CLI: cmd.CLI{Name: name}, 39 Out: os.Stdout, 40 } 41} 42 43// Run runs the completion and add installation flags beforehand. 44// The flags are added to the main flag CommandLine variable. 45func (c *Complete) Run() bool { 46 c.AddFlags(nil) 47 flag.Parse() 48 return c.Complete() 49} 50 51// Complete a command from completion line in environment variable, 52// and print out the complete options. 53// returns success if the completion ran or if the cli matched 54// any of the given flags, false otherwise 55// For installation: it assumes that flags were added and parsed before 56// it was called. 57func (c *Complete) Complete() bool { 58 line, ok := getLine() 59 if !ok { 60 // make sure flags parsed, 61 // in case they were not added in the main program 62 return c.CLI.Run() 63 } 64 Log("Completing line: %s", line) 65 a := newArgs(line) 66 Log("Completing last field: %s", a.Last) 67 options := c.Command.Predict(a) 68 Log("Options: %s", options) 69 70 // filter only options that match the last argument 71 matches := []string{} 72 for _, option := range options { 73 if match.Prefix(option, a.Last) { 74 matches = append(matches, option) 75 } 76 } 77 Log("Matches: %s", matches) 78 c.output(matches) 79 return true 80} 81 82func getLine() (string, bool) { 83 line := os.Getenv(envComplete) 84 if line == "" { 85 return "", false 86 } 87 return line, true 88} 89 90func (c *Complete) output(options []string) { 91 // stdout of program defines the complete options 92 for _, option := range options { 93 fmt.Fprintln(c.Out, option) 94 } 95} 96