helper_completion.go

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