1package commands
2
3import (
4 "fmt"
5 "sort"
6 "strings"
7
8 "github.com/spf13/cobra"
9
10 "github.com/MichaelMure/git-bug/bridge"
11 "github.com/MichaelMure/git-bug/bridge/core/auth"
12 "github.com/MichaelMure/git-bug/bug"
13 "github.com/MichaelMure/git-bug/cache"
14 _select "github.com/MichaelMure/git-bug/commands/select"
15)
16
17type validArgsFunction func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective)
18
19func completionHandlerError(err error) (completions []string, directives cobra.ShellCompDirective) {
20 return nil, cobra.ShellCompDirectiveError
21}
22
23func completeBridge(env *Env) validArgsFunction {
24 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
25 if err := loadBackend(env)(cmd, args); err != nil {
26 return completionHandlerError(err)
27 }
28 defer func() {
29 _ = env.backend.Close()
30 }()
31
32 bridges, err := bridge.ConfiguredBridges(env.backend)
33 if err != nil {
34 return completionHandlerError(err)
35 }
36
37 completions = make([]string, len(bridges))
38 for i, bridge := range bridges {
39 completions[i] = bridge + "\t" + "Bridge"
40 }
41
42 return completions, cobra.ShellCompDirectiveNoFileComp
43 }
44}
45
46func completeBridgeAuth(env *Env) validArgsFunction {
47 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
48 if err := loadBackend(env)(cmd, args); err != nil {
49 return completionHandlerError(err)
50 }
51 defer func() {
52 _ = env.backend.Close()
53 }()
54
55 creds, err := auth.List(env.backend)
56 if err != nil {
57 return completionHandlerError(err)
58 }
59
60 completions = make([]string, len(creds))
61 for i, cred := range creds {
62 meta := make([]string, 0, len(cred.Metadata()))
63 for k, v := range cred.Metadata() {
64 meta = append(meta, k+":"+v)
65 }
66 sort.Strings(meta)
67 metaFmt := strings.Join(meta, ",")
68
69 completions[i] = cred.ID().Human() + "\t" + cred.Target() + " " + string(cred.Kind()) + " " + metaFmt
70 }
71
72 return completions, cobra.ShellCompDirectiveNoFileComp
73 }
74}
75
76func completeBug(env *Env) validArgsFunction {
77 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
78 if err := loadBackend(env)(cmd, args); err != nil {
79 return completionHandlerError(err)
80 }
81 defer func() {
82 _ = env.backend.Close()
83 }()
84
85 return completeBugWithBackend(env.backend, toComplete)
86 }
87}
88
89func completeBugWithBackend(backend *cache.RepoCache, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
90 allIds := backend.AllBugsIds()
91 bugExcerpt := make([]*cache.BugExcerpt, len(allIds))
92 for i, id := range allIds {
93 var err error
94 bugExcerpt[i], err = backend.ResolveBugExcerpt(id)
95 if err != nil {
96 return completionHandlerError(err)
97 }
98 }
99
100 for i, id := range allIds {
101 if strings.Contains(id.String(), strings.TrimSpace(toComplete)) {
102 completions = append(completions, id.Human()+"\t"+bugExcerpt[i].Title)
103 }
104 }
105
106 return completions, cobra.ShellCompDirectiveNoFileComp
107}
108
109func completeBugAndLabels(env *Env, addOrRemove bool) validArgsFunction {
110 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
111 if err := loadBackend(env)(cmd, args); err != nil {
112 return completionHandlerError(err)
113 }
114 defer func() {
115 _ = env.backend.Close()
116 }()
117
118 b, args, err := _select.ResolveBug(env.backend, args)
119 if err == _select.ErrNoValidId {
120 // we need a bug first to complete labels
121 return completeBugWithBackend(env.backend, toComplete)
122 }
123 if err != nil {
124 return completionHandlerError(err)
125 }
126
127 snap := b.Snapshot()
128
129 seenLabels := map[bug.Label]bool{}
130 for _, label := range args {
131 seenLabels[bug.Label(label)] = addOrRemove
132 }
133
134 var labels []bug.Label
135 if addOrRemove {
136 for _, label := range snap.Labels {
137 seenLabels[label] = true
138 }
139
140 allLabels := env.backend.ValidLabels()
141 labels = make([]bug.Label, 0, len(allLabels))
142 for _, label := range allLabels {
143 if !seenLabels[label] {
144 labels = append(labels, label)
145 }
146 }
147 } else {
148 labels = make([]bug.Label, 0, len(snap.Labels))
149 for _, label := range snap.Labels {
150 if seenLabels[label] {
151 labels = append(labels, label)
152 }
153 }
154 }
155
156 completions = make([]string, len(labels))
157 for i, label := range labels {
158 completions[i] = string(label) + "\t" + "Label"
159 }
160
161 return completions, cobra.ShellCompDirectiveNoFileComp
162 }
163}
164
165func completeFrom(choices []string) validArgsFunction {
166 return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
167 return choices, cobra.ShellCompDirectiveNoFileComp
168 }
169}
170
171func completeGitRemote(env *Env) validArgsFunction {
172 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
173 if err := loadBackend(env)(cmd, args); err != nil {
174 return completionHandlerError(err)
175 }
176 defer func() {
177 _ = env.backend.Close()
178 }()
179
180 remoteMap, err := env.backend.GetRemotes()
181 if err != nil {
182 return completionHandlerError(err)
183 }
184 completions = make([]string, 0, len(remoteMap))
185 for remote, url := range remoteMap {
186 completions = append(completions, remote+"\t"+"Remote: "+url)
187 }
188 sort.Strings(completions)
189 return completions, cobra.ShellCompDirectiveNoFileComp
190 }
191}
192
193func completeLabel(env *Env) validArgsFunction {
194 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
195 if err := loadBackend(env)(cmd, args); err != nil {
196 return completionHandlerError(err)
197 }
198 defer func() {
199 _ = env.backend.Close()
200 }()
201
202 labels := env.backend.ValidLabels()
203 completions = make([]string, len(labels))
204 for i, label := range labels {
205 if strings.Contains(label.String(), " ") {
206 completions[i] = fmt.Sprintf("\"%s\"\tLabel", label.String())
207 } else {
208 completions[i] = fmt.Sprintf("%s\tLabel", label.String())
209 }
210 }
211 return completions, cobra.ShellCompDirectiveNoFileComp
212 }
213}
214
215func completeLs(env *Env) validArgsFunction {
216 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
217 if strings.HasPrefix(toComplete, "status:") {
218 completions = append(completions, "status:open\tOpen bugs")
219 completions = append(completions, "status:closed\tClosed bugs")
220 return completions, cobra.ShellCompDirectiveDefault
221 }
222
223 byPerson := []string{"author:", "participant:", "actor:"}
224 byLabel := []string{"label:", "no:"}
225 needBackend := false
226 for _, key := range append(byPerson, byLabel...) {
227 if strings.HasPrefix(toComplete, key) {
228 needBackend = true
229 }
230 }
231
232 if needBackend {
233 if err := loadBackend(env)(cmd, args); err != nil {
234 return completionHandlerError(err)
235 }
236 defer func() {
237 _ = env.backend.Close()
238 }()
239 }
240
241 for _, key := range byPerson {
242 if !strings.HasPrefix(toComplete, key) {
243 continue
244 }
245 ids := env.backend.AllIdentityIds()
246 completions = make([]string, len(ids))
247 for i, id := range ids {
248 user, err := env.backend.ResolveIdentityExcerpt(id)
249 if err != nil {
250 return completionHandlerError(err)
251 }
252 var handle string
253 if user.Login != "" {
254 handle = user.Login
255 } else {
256 // "author:John Doe" does not work yet, so use the first name.
257 handle = strings.Split(user.Name, " ")[0]
258 }
259 completions[i] = key + handle + "\t" + user.DisplayName()
260 }
261 return completions, cobra.ShellCompDirectiveNoFileComp
262 }
263
264 for _, key := range byLabel {
265 if !strings.HasPrefix(toComplete, key) {
266 continue
267 }
268 labels := env.backend.ValidLabels()
269 completions = make([]string, len(labels))
270 for i, label := range labels {
271 if strings.Contains(label.String(), " ") {
272 completions[i] = key + "\"" + string(label) + "\""
273 } else {
274 completions[i] = key + string(label)
275 }
276 }
277 return completions, cobra.ShellCompDirectiveNoFileComp
278 }
279
280 completions = []string{
281 "actor:\tFilter by actor",
282 "author:\tFilter by author",
283 "label:\tFilter by label",
284 "no:\tExclude bugs by label",
285 "participant:\tFilter by participant",
286 "status:\tFilter by open/close status",
287 "title:\tFilter by title",
288 }
289 return completions, cobra.ShellCompDirectiveNoSpace
290 }
291}
292
293func completeUser(env *Env) validArgsFunction {
294 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
295 if err := loadBackend(env)(cmd, args); err != nil {
296 return completionHandlerError(err)
297 }
298 defer func() {
299 _ = env.backend.Close()
300 }()
301
302 ids := env.backend.AllIdentityIds()
303 completions = make([]string, len(ids))
304 for i, id := range ids {
305 user, err := env.backend.ResolveIdentityExcerpt(id)
306 if err != nil {
307 return completionHandlerError(err)
308 }
309 completions[i] = user.Id.Human() + "\t" + user.DisplayName()
310 }
311 return completions, cobra.ShellCompDirectiveNoFileComp
312 }
313}
314
315func completeUserForQuery(env *Env) validArgsFunction {
316 return func(cmd *cobra.Command, args []string, toComplete string) (completions []string, directives cobra.ShellCompDirective) {
317 if err := loadBackend(env)(cmd, args); err != nil {
318 return completionHandlerError(err)
319 }
320 defer func() {
321 _ = env.backend.Close()
322 }()
323
324 ids := env.backend.AllIdentityIds()
325 completions = make([]string, len(ids))
326 for i, id := range ids {
327 user, err := env.backend.ResolveIdentityExcerpt(id)
328 if err != nil {
329 return completionHandlerError(err)
330 }
331 var handle string
332 if user.Login != "" {
333 handle = user.Login
334 } else {
335 // "author:John Doe" does not work yet, so use the first name.
336 handle = strings.Split(user.Name, " ")[0]
337 }
338 completions[i] = handle + "\t" + user.DisplayName()
339 }
340 return completions, cobra.ShellCompDirectiveNoFileComp
341 }
342}