File: complete.go

package info (click to toggle)
golang-sourcehut-rjarry-go-opt 2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 148 kB
  • sloc: makefile: 2
file content (145 lines) | stat: -rw-r--r-- 3,444 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package opt

import (
	"reflect"
	"strings"
)

type Completion struct {
	Value       string
	Description string
}

func (c *CmdSpec) unseenFlags(arg string) []Completion {
	var flags []Completion
	for i := 0; i < len(c.opts); i++ {
		spec := &c.opts[i]
		if !spec.appliesToAlias(c.name) || spec.seen {
			continue
		}
		switch spec.kind {
		case flag, option:
			if spec.short != "" && strings.HasPrefix(spec.short, arg) {
				flags = append(flags, Completion{
					Value:       spec.short + " ",
					Description: spec.description,
				})
			}
			if spec.long != "" && strings.HasPrefix(spec.long, arg) {
				flags = append(flags, Completion{
					Value:       spec.long + " ",
					Description: spec.description,
				})
			}
		}
	}
	return flags
}

func (c *CmdSpec) nextPositional() *optSpec {
	var spec *optSpec
	for p := len(c.positionals) - 1; p >= 0; p-- {
		spec = &c.opts[c.positionals[p]]
		if !spec.appliesToAlias(c.name) {
			continue
		}
		if spec.seenValue && (p+1) < len(c.positionals) {
			// first "unseen" positional argument
			return &c.opts[c.positionals[p+1]]
		}
	}
	return spec
}

func (s *optSpec) getCompletions(arg string) []Completion {
	if s.complete.IsValid() {
		in := []reflect.Value{reflect.ValueOf(arg)}
		out := s.complete.Call(in)
		if res, ok := out[0].Interface().([]string); ok {
			var completions []Completion
			for _, value := range res {
				completions = append(completions, Completion{
					Value:       value,
					Description: s.description,
				})
			}
			return completions
		}
	}
	return nil
}

func (c *CmdSpec) GetCompletions(args *Args) ([]Completion, string) {
	if args.Count() == 0 || (args.Count() == 1 && args.TrailingSpace() == "") {
		return nil, ""
	}

	var completions []Completion
	var prefix string
	var flags []Completion
	var last *seenArg
	var spec *optSpec

	_ = c.parseArgs(args.Clone())

	if len(c.seen) > 0 {
		last = c.seen[len(c.seen)-1]
	}

	if args.TrailingSpace() != "" {
		// Complete new argument
		prefix = args.String()
		if last != nil && !last.spec.seenValue {
			spec = last.spec
		}
		if spec == nil {
			// Last argument was not a flag that required a value.
			// Complete for the next unseen positional argument.
			flags = c.unseenFlags("")
			spec = c.nextPositional()
		}
		if spec != nil {
			completions = spec.getCompletions("")
		}
	} else {
		// Complete current argument
		arg := args.Cut(1)[0]
		prefix = args.String() + " "

		if last != nil && last.indexes[len(last.indexes)-1] == args.Count() {
			s := last.spec
			f := s.long + "="
			switch {
			case (s.kind == flag || s.kind == option) && (s.short == arg || s.long == arg):
				// Current argument is precisely a flag.
				spec = nil
				completions = []Completion{{Value: arg + " ", Description: s.description}}
			case s.kind == option && f != "=" && strings.HasPrefix(arg, f):
				// Current argument is a long flag in the format:
				//       --flag=value
				// Strip the prefix and complete the value.
				prefix += f
				arg = strings.TrimPrefix(arg, f)
				fallthrough
			default:
				spec = s
			}
		} else {
			// Current argument was not identified, attempt
			// completion from the next unseen positional.
			spec = c.nextPositional()
		}
		if spec != nil {
			completions = spec.getCompletions(arg)
		}
		if strings.HasPrefix(arg, "-") {
			flags = c.unseenFlags(arg)
		}
	}

	if flags != nil {
		completions = append(completions, flags...)
	}

	return completions, prefix
}