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