]>
Commit | Line | Data |
---|---|---|
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. | |
6 | package complete | |
7 | ||
8 | import ( | |
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 | ||
19 | const ( | |
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 | |
26 | type 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. | |
37 | func 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. | |
47 | func (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. | |
59 | func (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 | ||
89 | func 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 | ||
104 | func (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 | } |