You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
6.8 KiB

  1. package flags
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "reflect"
  6. "sort"
  7. "strings"
  8. "unicode/utf8"
  9. )
  10. // Completion is a type containing information of a completion.
  11. type Completion struct {
  12. // The completed item
  13. Item string
  14. // A description of the completed item (optional)
  15. Description string
  16. }
  17. type completions []Completion
  18. func (c completions) Len() int {
  19. return len(c)
  20. }
  21. func (c completions) Less(i, j int) bool {
  22. return c[i].Item < c[j].Item
  23. }
  24. func (c completions) Swap(i, j int) {
  25. c[i], c[j] = c[j], c[i]
  26. }
  27. // Completer is an interface which can be implemented by types
  28. // to provide custom command line argument completion.
  29. type Completer interface {
  30. // Complete receives a prefix representing a (partial) value
  31. // for its type and should provide a list of possible valid
  32. // completions.
  33. Complete(match string) []Completion
  34. }
  35. type completion struct {
  36. parser *Parser
  37. }
  38. // Filename is a string alias which provides filename completion.
  39. type Filename string
  40. func completionsWithoutDescriptions(items []string) []Completion {
  41. ret := make([]Completion, len(items))
  42. for i, v := range items {
  43. ret[i].Item = v
  44. }
  45. return ret
  46. }
  47. // Complete returns a list of existing files with the given
  48. // prefix.
  49. func (f *Filename) Complete(match string) []Completion {
  50. ret, _ := filepath.Glob(match + "*")
  51. return completionsWithoutDescriptions(ret)
  52. }
  53. func (c *completion) skipPositional(s *parseState, n int) {
  54. if n >= len(s.positional) {
  55. s.positional = nil
  56. } else {
  57. s.positional = s.positional[n:]
  58. }
  59. }
  60. func (c *completion) completeOptionNames(s *parseState, prefix string, match string, short bool) []Completion {
  61. if short && len(match) != 0 {
  62. return []Completion{
  63. Completion{
  64. Item: prefix + match,
  65. },
  66. }
  67. }
  68. var results []Completion
  69. repeats := map[string]bool{}
  70. for name, opt := range s.lookup.longNames {
  71. if strings.HasPrefix(name, match) && !opt.Hidden {
  72. results = append(results, Completion{
  73. Item: defaultLongOptDelimiter + name,
  74. Description: opt.Description,
  75. })
  76. if short {
  77. repeats[string(opt.ShortName)] = true
  78. }
  79. }
  80. }
  81. if short {
  82. for name, opt := range s.lookup.shortNames {
  83. if _, exist := repeats[name]; !exist && strings.HasPrefix(name, match) && !opt.Hidden {
  84. results = append(results, Completion{
  85. Item: string(defaultShortOptDelimiter) + name,
  86. Description: opt.Description,
  87. })
  88. }
  89. }
  90. }
  91. return results
  92. }
  93. func (c *completion) completeNamesForLongPrefix(s *parseState, prefix string, match string) []Completion {
  94. return c.completeOptionNames(s, prefix, match, false)
  95. }
  96. func (c *completion) completeNamesForShortPrefix(s *parseState, prefix string, match string) []Completion {
  97. return c.completeOptionNames(s, prefix, match, true)
  98. }
  99. func (c *completion) completeCommands(s *parseState, match string) []Completion {
  100. n := make([]Completion, 0, len(s.command.commands))
  101. for _, cmd := range s.command.commands {
  102. if cmd.data != c && strings.HasPrefix(cmd.Name, match) {
  103. n = append(n, Completion{
  104. Item: cmd.Name,
  105. Description: cmd.ShortDescription,
  106. })
  107. }
  108. }
  109. return n
  110. }
  111. func (c *completion) completeValue(value reflect.Value, prefix string, match string) []Completion {
  112. if value.Kind() == reflect.Slice {
  113. value = reflect.New(value.Type().Elem())
  114. }
  115. i := value.Interface()
  116. var ret []Completion
  117. if cmp, ok := i.(Completer); ok {
  118. ret = cmp.Complete(match)
  119. } else if value.CanAddr() {
  120. if cmp, ok = value.Addr().Interface().(Completer); ok {
  121. ret = cmp.Complete(match)
  122. }
  123. }
  124. for i, v := range ret {
  125. ret[i].Item = prefix + v.Item
  126. }
  127. return ret
  128. }
  129. func (c *completion) complete(args []string) []Completion {
  130. if len(args) == 0 {
  131. args = []string{""}
  132. }
  133. s := &parseState{
  134. args: args,
  135. }
  136. c.parser.fillParseState(s)
  137. var opt *Option
  138. for len(s.args) > 1 {
  139. arg := s.pop()
  140. if (c.parser.Options&PassDoubleDash) != None && arg == "--" {
  141. opt = nil
  142. c.skipPositional(s, len(s.args)-1)
  143. break
  144. }
  145. if argumentIsOption(arg) {
  146. prefix, optname, islong := stripOptionPrefix(arg)
  147. optname, _, argument := splitOption(prefix, optname, islong)
  148. if argument == nil {
  149. var o *Option
  150. canarg := true
  151. if islong {
  152. o = s.lookup.longNames[optname]
  153. } else {
  154. for i, r := range optname {
  155. sname := string(r)
  156. o = s.lookup.shortNames[sname]
  157. if o == nil {
  158. break
  159. }
  160. if i == 0 && o.canArgument() && len(optname) != len(sname) {
  161. canarg = false
  162. break
  163. }
  164. }
  165. }
  166. if o == nil && (c.parser.Options&PassAfterNonOption) != None {
  167. opt = nil
  168. c.skipPositional(s, len(s.args)-1)
  169. break
  170. } else if o != nil && o.canArgument() && !o.OptionalArgument && canarg {
  171. if len(s.args) > 1 {
  172. s.pop()
  173. } else {
  174. opt = o
  175. }
  176. }
  177. }
  178. } else {
  179. if len(s.positional) > 0 {
  180. if !s.positional[0].isRemaining() {
  181. // Don't advance beyond a remaining positional arg (because
  182. // it consumes all subsequent args).
  183. s.positional = s.positional[1:]
  184. }
  185. } else if cmd, ok := s.lookup.commands[arg]; ok {
  186. cmd.fillParseState(s)
  187. }
  188. opt = nil
  189. }
  190. }
  191. lastarg := s.args[len(s.args)-1]
  192. var ret []Completion
  193. if opt != nil {
  194. // Completion for the argument of 'opt'
  195. ret = c.completeValue(opt.value, "", lastarg)
  196. } else if argumentStartsOption(lastarg) {
  197. // Complete the option
  198. prefix, optname, islong := stripOptionPrefix(lastarg)
  199. optname, split, argument := splitOption(prefix, optname, islong)
  200. if argument == nil && !islong {
  201. rname, n := utf8.DecodeRuneInString(optname)
  202. sname := string(rname)
  203. if opt := s.lookup.shortNames[sname]; opt != nil && opt.canArgument() {
  204. ret = c.completeValue(opt.value, prefix+sname, optname[n:])
  205. } else {
  206. ret = c.completeNamesForShortPrefix(s, prefix, optname)
  207. }
  208. } else if argument != nil {
  209. if islong {
  210. opt = s.lookup.longNames[optname]
  211. } else {
  212. opt = s.lookup.shortNames[optname]
  213. }
  214. if opt != nil {
  215. ret = c.completeValue(opt.value, prefix+optname+split, *argument)
  216. }
  217. } else if islong {
  218. ret = c.completeNamesForLongPrefix(s, prefix, optname)
  219. } else {
  220. ret = c.completeNamesForShortPrefix(s, prefix, optname)
  221. }
  222. } else if len(s.positional) > 0 {
  223. // Complete for positional argument
  224. ret = c.completeValue(s.positional[0].value, "", lastarg)
  225. } else if len(s.command.commands) > 0 {
  226. // Complete for command
  227. ret = c.completeCommands(s, lastarg)
  228. }
  229. sort.Sort(completions(ret))
  230. return ret
  231. }
  232. func (c *completion) print(items []Completion, showDescriptions bool) {
  233. if showDescriptions && len(items) > 1 {
  234. maxl := 0
  235. for _, v := range items {
  236. if len(v.Item) > maxl {
  237. maxl = len(v.Item)
  238. }
  239. }
  240. for _, v := range items {
  241. fmt.Printf("%s", v.Item)
  242. if len(v.Description) > 0 {
  243. fmt.Printf("%s # %s", strings.Repeat(" ", maxl-len(v.Item)), v.Description)
  244. }
  245. fmt.Printf("\n")
  246. }
  247. } else {
  248. for _, v := range items {
  249. fmt.Println(v.Item)
  250. }
  251. }
  252. }