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
}
|