1package repo
  2
  3import (
  4	"sort"
  5	"strings"
  6
  7	"github.com/charmbracelet/bubbles/key"
  8	tea "github.com/charmbracelet/bubbletea"
  9	ggit "github.com/charmbracelet/soft-serve/git"
 10	"github.com/charmbracelet/soft-serve/ui/common"
 11	"github.com/charmbracelet/soft-serve/ui/components/selector"
 12	"github.com/charmbracelet/soft-serve/ui/components/tabs"
 13	"github.com/charmbracelet/soft-serve/ui/git"
 14)
 15
 16// RefItemsMsg is a message that contains a list of RefItem.
 17type RefItemsMsg struct {
 18	prefix string
 19	items  []selector.IdentifiableItem
 20}
 21
 22// Refs is a component that displays a list of references.
 23type Refs struct {
 24	common    common.Common
 25	selector  *selector.Selector
 26	repo      git.GitRepo
 27	ref       *ggit.Reference
 28	activeRef *ggit.Reference
 29	refPrefix string
 30}
 31
 32// NewRefs creates a new Refs component.
 33func NewRefs(common common.Common, refPrefix string) *Refs {
 34	r := &Refs{
 35		common:    common,
 36		refPrefix: refPrefix,
 37	}
 38	s := selector.New(common, []selector.IdentifiableItem{}, RefItemDelegate{&common})
 39	s.SetShowFilter(false)
 40	s.SetShowHelp(false)
 41	s.SetShowPagination(true)
 42	s.SetShowStatusBar(false)
 43	s.SetShowTitle(false)
 44	s.SetFilteringEnabled(false)
 45	s.DisableQuitKeybindings()
 46	r.selector = s
 47	return r
 48}
 49
 50// SetSize implements common.Component.
 51func (r *Refs) SetSize(width, height int) {
 52	r.common.SetSize(width, height)
 53	r.selector.SetSize(width, height)
 54}
 55
 56// ShortHelp implements help.KeyMap.
 57func (r *Refs) ShortHelp() []key.Binding {
 58	copyKey := r.common.KeyMap.Copy
 59	copyKey.SetHelp("c", "copy ref")
 60	k := r.selector.KeyMap
 61	return []key.Binding{
 62		r.common.KeyMap.SelectItem,
 63		k.CursorUp,
 64		k.CursorDown,
 65		copyKey,
 66	}
 67}
 68
 69// FullHelp implements help.KeyMap.
 70func (r *Refs) FullHelp() [][]key.Binding {
 71	copyKey := r.common.KeyMap.Copy
 72	copyKey.SetHelp("c", "copy ref")
 73	k := r.selector.KeyMap
 74	return [][]key.Binding{
 75		{r.common.KeyMap.SelectItem},
 76		{
 77			k.CursorUp,
 78			k.CursorDown,
 79		},
 80		{
 81			k.NextPage,
 82			k.PrevPage,
 83		},
 84		{
 85			k.GoToStart,
 86			k.GoToEnd,
 87		},
 88		{
 89			copyKey,
 90		},
 91	}
 92}
 93
 94// Init implements tea.Model.
 95func (r *Refs) Init() tea.Cmd {
 96	return r.updateItemsCmd
 97}
 98
 99// Update implements tea.Model.
100func (r *Refs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
101	cmds := make([]tea.Cmd, 0)
102	switch msg := msg.(type) {
103	case RepoMsg:
104		r.selector.Select(0)
105		r.repo = git.GitRepo(msg)
106		cmds = append(cmds, r.Init())
107	case RefMsg:
108		r.ref = msg
109		cmds = append(cmds, r.Init())
110	case RefItemsMsg:
111		cmds = append(cmds, r.selector.SetItems(msg.items))
112		i := r.selector.SelectedItem()
113		if i != nil {
114			r.activeRef = i.(RefItem).Reference
115		}
116	case selector.ActiveMsg:
117		switch sel := msg.IdentifiableItem.(type) {
118		case RefItem:
119			r.activeRef = sel.Reference
120		}
121		cmds = append(cmds, updateStatusBarCmd)
122	case selector.SelectMsg:
123		switch i := msg.IdentifiableItem.(type) {
124		case RefItem:
125			cmds = append(cmds,
126				switchRefCmd(i.Reference),
127				tabs.SelectTabCmd(int(filesTab)),
128			)
129		}
130	case tea.KeyMsg:
131		switch msg.String() {
132		case "l", "right":
133			cmds = append(cmds, r.selector.SelectItem)
134		}
135	}
136	m, cmd := r.selector.Update(msg)
137	r.selector = m.(*selector.Selector)
138	if cmd != nil {
139		cmds = append(cmds, cmd)
140	}
141	return r, tea.Batch(cmds...)
142}
143
144// View implements tea.Model.
145func (r *Refs) View() string {
146	return r.selector.View()
147}
148
149// StausBarValue implements statusbar.StatusBar.
150func (r *Refs) StatusBarValue() string {
151	if r.activeRef == nil {
152		return ""
153	}
154	return r.activeRef.Name().String()
155}
156
157// StatusBarInfo implements statusbar.StatusBar.
158func (r *Refs) StatusBarInfo() string {
159	return ""
160}
161
162func (r *Refs) updateItemsCmd() tea.Msg {
163	its := make(RefItems, 0)
164	refs, err := r.repo.References()
165	if err != nil {
166		return common.ErrorMsg(err)
167	}
168	for _, ref := range refs {
169		if strings.HasPrefix(ref.Name().String(), r.refPrefix) {
170			its = append(its, RefItem{Reference: ref})
171		}
172	}
173	sort.Sort(its)
174	items := make([]selector.IdentifiableItem, len(its))
175	for i, it := range its {
176		items[i] = it
177	}
178	return RefItemsMsg{
179		items:  items,
180		prefix: r.refPrefix,
181	}
182}
183
184func switchRefCmd(ref *ggit.Reference) tea.Cmd {
185	return func() tea.Msg {
186		return RefMsg(ref)
187	}
188}