1package complete
2
3// Command represents a command line
4// It holds the data that enables auto completion of command line
5// Command can also be a sub command.
6type Command struct {
7	// Sub is map of sub commands of the current command
8	// The key refer to the sub command name, and the value is it's
9	// Command descriptive struct.
10	Sub Commands
11
12	// Flags is a map of flags that the command accepts.
13	// The key is the flag name, and the value is it's predictions.
14	Flags Flags
15
16	// GlobalFlags is a map of flags that the command accepts.
17	// Global flags that can appear also after a sub command.
18	GlobalFlags Flags
19
20	// Args are extra arguments that the command accepts, those who are
21	// given without any flag before.
22	Args Predictor
23}
24
25// Predict returns all possible predictions for args according to the command struct
26func (c *Command) Predict(a Args) []string {
27	options, _ := c.predict(a)
28	return options
29}
30
31// Commands is the type of Sub member, it maps a command name to a command struct
32type Commands map[string]Command
33
34// Predict completion of sub command names names according to command line arguments
35func (c Commands) Predict(a Args) (prediction []string) {
36	for sub := range c {
37		prediction = append(prediction, sub)
38	}
39	return
40}
41
42// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions.
43type Flags map[string]Predictor
44
45// Predict completion of flags names according to command line arguments
46func (f Flags) Predict(a Args) (prediction []string) {
47	for flag := range f {
48		// If the flag starts with a hyphen, we avoid emitting the prediction
49		// unless the last typed arg contains a hyphen as well.
50		flagHyphenStart := len(flag) != 0 && flag[0] == '-'
51		lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-'
52		if flagHyphenStart && !lastHyphenStart {
53			continue
54		}
55		prediction = append(prediction, flag)
56	}
57	return
58}
59
60// predict options
61// only is set to true if no more options are allowed to be returned
62// those are in cases of special flag that has specific completion arguments,
63// and other flags or sub commands can't come after it.
64func (c *Command) predict(a Args) (options []string, only bool) {
65
66	// search sub commands for predictions first
67	subCommandFound := false
68	for i, arg := range a.Completed {
69		if cmd, ok := c.Sub[arg]; ok {
70			subCommandFound = true
71
72			// recursive call for sub command
73			options, only = cmd.predict(a.from(i))
74			if only {
75				return
76			}
77
78			// We matched so stop searching. Continuing to search can accidentally
79			// match a subcommand with current set of commands, see issue #46.
80			break
81		}
82	}
83
84	// if last completed word is a global flag that we need to complete
85	if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil {
86		Log("Predicting according to global flag %s", a.LastCompleted)
87		return predictor.Predict(a), true
88	}
89
90	options = append(options, c.GlobalFlags.Predict(a)...)
91
92	// if a sub command was entered, we won't add the parent command
93	// completions and we return here.
94	if subCommandFound {
95		return
96	}
97
98	// if last completed word is a command flag that we need to complete
99	if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
100		Log("Predicting according to flag %s", a.LastCompleted)
101		return predictor.Predict(a), true
102	}
103
104	options = append(options, c.Sub.Predict(a)...)
105	options = append(options, c.Flags.Predict(a)...)
106	if c.Args != nil {
107		options = append(options, c.Args.Predict(a)...)
108	}
109
110	return
111}
112