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/cache"
 13	_select "github.com/MichaelMure/git-bug/commands/select"
 14	"github.com/MichaelMure/git-bug/entities/bug"
 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}