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 "strconv" 14 15 "github.com/posener/complete/cmd" 16 "github.com/posener/complete/match" 17) 18 19const ( 20 envLine = "COMP_LINE" 21 envPoint = "COMP_POINT" 22 envDebug = "COMP_DEBUG" 23) 24 25// Complete structs define completion for a command with CLI options 26type Complete struct { 27 Command Command 28 cmd.CLI 29 Out io.Writer 30} 31 32// New creates a new complete command. 33// name is the name of command we want to auto complete. 34// IMPORTANT: it must be the same name - if the auto complete 35// completes the 'go' command, name must be equal to "go". 36// command is the struct of the command completion. 37func New(name string, command Command) *Complete { 38 return &Complete{ 39 Command: command, 40 CLI: cmd.CLI{Name: name}, 41 Out: os.Stdout, 42 } 43} 44 45// Run runs the completion and add installation flags beforehand. 46// The flags are added to the main flag CommandLine variable. 47func (c *Complete) Run() bool { 48 c.AddFlags(nil) 49 flag.Parse() 50 return c.Complete() 51} 52 53// Complete a command from completion line in environment variable, 54// and print out the complete options. 55// returns success if the completion ran or if the cli matched 56// any of the given flags, false otherwise 57// For installation: it assumes that flags were added and parsed before 58// it was called. 59func (c *Complete) Complete() bool { 60 line, point, ok := getEnv() 61 if !ok { 62 // make sure flags parsed, 63 // in case they were not added in the main program 64 return c.CLI.Run() 65 } 66 67 if point >= 0 && point < len(line) { 68 line = line[:point] 69 } 70 71 Log("Completing phrase: %s", line) 72 a := newArgs(line) 73 Log("Completing last field: %s", a.Last) 74 options := c.Command.Predict(a) 75 Log("Options: %s", options) 76 77 // filter only options that match the last argument 78 matches := []string{} 79 for _, option := range options { 80 if match.Prefix(option, a.Last) { 81 matches = append(matches, option) 82 } 83 } 84 Log("Matches: %s", matches) 85 c.output(matches) 86 return true 87} 88 89func getEnv() (line string, point int, ok bool) { 90 line = os.Getenv(envLine) 91 if line == "" { 92 return 93 } 94 point, err := strconv.Atoi(os.Getenv(envPoint)) 95 if err != nil { 96 // If failed parsing point for some reason, set it to point 97 // on the end of the line. 98 Log("Failed parsing point %s: %v", os.Getenv(envPoint), err) 99 point = len(line) 100 } 101 return line, point, true 102} 103 104func (c *Complete) output(options []string) { 105 // stdout of program defines the complete options 106 for _, option := range options { 107 fmt.Fprintln(c.Out, option) 108 } 109} 110