completion-parse-args.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package cli
  3. import (
  4. "fmt"
  5. "os"
  6. "strings"
  7. )
  8. var _ = fmt.Print
  9. var _ = os.Getenv
  10. func (self *Completions) add_options_group(options []*Option, word string) {
  11. group := self.AddMatchGroup("Options")
  12. if strings.HasPrefix(word, "--") {
  13. if word == "--" {
  14. group.Matches = append(group.Matches, &Match{Word: "--", Description: "End of options"})
  15. }
  16. for _, opt := range options {
  17. for _, q := range opt.Aliases {
  18. if strings.HasPrefix(q.String(), word) {
  19. group.Matches = append(group.Matches, &Match{Word: q.String(), Description: opt.Help})
  20. break
  21. }
  22. }
  23. }
  24. } else {
  25. if word == "-" {
  26. group.Matches = append(group.Matches, &Match{Word: "--", Description: "End of options"})
  27. for _, opt := range options {
  28. has_single_letter_alias := false
  29. for _, q := range opt.Aliases {
  30. if q.IsShort {
  31. group.AddMatch("-"+q.NameWithoutHyphens, opt.Help)
  32. has_single_letter_alias = true
  33. break
  34. }
  35. }
  36. if !has_single_letter_alias {
  37. for _, q := range opt.Aliases {
  38. if !q.IsShort {
  39. group.AddMatch(q.String(), opt.Help)
  40. break
  41. }
  42. }
  43. }
  44. }
  45. } else {
  46. runes := []rune(word)
  47. last_letter := string(runes[len(runes)-1])
  48. for _, opt := range options {
  49. for _, q := range opt.Aliases {
  50. if q.IsShort && q.NameWithoutHyphens == last_letter {
  51. group.AddMatch(word, opt.Help)
  52. return
  53. }
  54. }
  55. }
  56. }
  57. }
  58. }
  59. func (self *Command) sub_command_allowed_at(completions *Completions, arg_num int) bool {
  60. if self.SubCommandMustBeFirst {
  61. return arg_num == 1 && completions.CurrentWordIdxInParent == 1
  62. }
  63. return arg_num == 1
  64. }
  65. func complete_word(word string, completions *Completions, only_args_allowed bool, expecting_arg_for *Option, arg_num int) {
  66. cmd := completions.CurrentCmd
  67. if expecting_arg_for != nil {
  68. if expecting_arg_for.Completer != nil {
  69. expecting_arg_for.Completer(completions, word, arg_num)
  70. }
  71. return
  72. }
  73. if !only_args_allowed && strings.HasPrefix(word, "-") {
  74. if strings.HasPrefix(word, "--") && strings.Contains(word, "=") {
  75. idx := strings.Index(word, "=")
  76. option := cmd.FindOption(word[:idx])
  77. if option != nil {
  78. if option.Completer != nil {
  79. option.Completer(completions, word[idx+1:], arg_num)
  80. completions.AddPrefixToAllMatches(word[:idx+1])
  81. }
  82. }
  83. } else {
  84. completions.add_options_group(cmd.AllOptions(), word)
  85. }
  86. return
  87. }
  88. if cmd.HasVisibleSubCommands() && cmd.sub_command_allowed_at(completions, arg_num) {
  89. for _, cg := range cmd.SubCommandGroups {
  90. group := completions.AddMatchGroup(cg.Title)
  91. if group.Title == "" {
  92. group.Title = "Sub-commands"
  93. }
  94. for _, sc := range cg.SubCommands {
  95. if !sc.Hidden && strings.HasPrefix(sc.Name, word) {
  96. t := sc.ShortDescription
  97. if t == "" {
  98. t = sc.HelpText
  99. }
  100. group.AddMatch(sc.Name, t)
  101. }
  102. }
  103. }
  104. if cmd.SubCommandIsOptional && cmd.ArgCompleter != nil {
  105. cmd.ArgCompleter(completions, word, arg_num)
  106. }
  107. return
  108. }
  109. if cmd.ArgCompleter != nil {
  110. cmd.ArgCompleter(completions, word, arg_num)
  111. }
  112. }
  113. func completion_parse_args(cmd *Command, words []string, completions *Completions) {
  114. completions.CurrentCmd = cmd
  115. if len(words) == 0 {
  116. complete_word("", completions, false, nil, 0)
  117. return
  118. }
  119. completions.AllWords = words
  120. var expecting_arg_for *Option
  121. only_args_allowed := false
  122. arg_num := 0
  123. for i, word := range words {
  124. cmd = completions.CurrentCmd
  125. completions.CurrentWordIdx = i
  126. completions.CurrentWordIdxInParent++
  127. is_last_word := i == len(words)-1
  128. is_option_equal := completions.split_on_equals && word == "=" && expecting_arg_for != nil
  129. if only_args_allowed || (expecting_arg_for == nil && !strings.HasPrefix(word, "-")) {
  130. if !is_option_equal {
  131. arg_num++
  132. }
  133. if arg_num == 1 {
  134. cmd.IndexOfFirstArg = completions.CurrentWordIdx
  135. }
  136. }
  137. if is_last_word {
  138. if is_option_equal {
  139. word = ""
  140. }
  141. complete_word(word, completions, only_args_allowed, expecting_arg_for, arg_num)
  142. } else {
  143. if expecting_arg_for != nil {
  144. if is_option_equal {
  145. continue
  146. }
  147. expecting_arg_for = nil
  148. continue
  149. }
  150. if word == "--" {
  151. only_args_allowed = true
  152. continue
  153. }
  154. if !only_args_allowed && strings.HasPrefix(word, "-") {
  155. if !strings.Contains(word, "=") {
  156. option := cmd.FindOption(word)
  157. if option != nil && option.needs_argument() {
  158. expecting_arg_for = option
  159. }
  160. }
  161. continue
  162. }
  163. if cmd.HasVisibleSubCommands() && cmd.sub_command_allowed_at(completions, arg_num) {
  164. sc := cmd.FindSubCommand(word)
  165. if sc == nil {
  166. only_args_allowed = true
  167. continue
  168. }
  169. completions.CurrentCmd = sc
  170. cmd = sc
  171. arg_num = 0
  172. completions.CurrentWordIdxInParent = 0
  173. only_args_allowed = cmd.OnlyArgsAllowed
  174. if cmd.ParseArgsForCompletion != nil {
  175. cmd.ParseArgsForCompletion(cmd, words[i+1:], completions)
  176. return
  177. }
  178. } else if cmd.StopCompletingAtArg > 0 && arg_num >= cmd.StopCompletingAtArg {
  179. return
  180. } else {
  181. only_args_allowed = true
  182. continue
  183. }
  184. }
  185. }
  186. }