refs.go

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