bubble.go

  1package refs
  2
  3import (
  4	"fmt"
  5	"io"
  6	"sort"
  7
  8	"github.com/charmbracelet/bubbles/list"
  9	tea "github.com/charmbracelet/bubbletea"
 10	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
 11	"github.com/charmbracelet/soft-serve/internal/tui/style"
 12	"github.com/go-git/go-git/v5/plumbing"
 13)
 14
 15type item struct {
 16	*plumbing.Reference
 17}
 18
 19func (i item) Short() string {
 20	return i.Name().Short()
 21}
 22
 23func (i item) FilterValue() string { return i.Short() }
 24
 25type items []item
 26
 27func (cl items) Len() int      { return len(cl) }
 28func (cl items) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
 29func (cl items) Less(i, j int) bool {
 30	return cl[i].Name().Short() < cl[j].Name().Short()
 31}
 32
 33type itemDelegate struct {
 34	style *style.Styles
 35}
 36
 37func (d itemDelegate) Height() int                               { return 1 }
 38func (d itemDelegate) Spacing() int                              { return 0 }
 39func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
 40func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
 41	s := d.style
 42	i, ok := listItem.(item)
 43	if !ok {
 44		return
 45	}
 46
 47	ref := i.Short()
 48	if i.Name().IsTag() {
 49		ref = s.RefItemTag.Render(ref)
 50	}
 51	ref = s.RefItemBranch.Render(ref)
 52	refMaxWidth := m.Width() -
 53		s.RefItemSelector.GetMarginLeft() -
 54		s.RefItemSelector.GetWidth() -
 55		s.RefItemInactive.GetMarginLeft()
 56	ref = types.TruncateString(ref, refMaxWidth, "…")
 57	if index == m.Index() {
 58		fmt.Fprint(w, s.RefItemSelector.Render(">")+
 59			s.RefItemActive.Render(ref))
 60	} else {
 61		fmt.Fprint(w, s.LogItemSelector.Render(" ")+
 62			s.RefItemInactive.Render(ref))
 63	}
 64}
 65
 66type Bubble struct {
 67	repo         types.Repo
 68	list         list.Model
 69	style        *style.Styles
 70	width        int
 71	widthMargin  int
 72	height       int
 73	heightMargin int
 74}
 75
 76func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
 77	l := list.NewModel([]list.Item{}, itemDelegate{style}, width-widthMargin, height-heightMargin)
 78	l.SetShowFilter(false)
 79	l.SetShowHelp(false)
 80	l.SetShowPagination(false)
 81	l.SetShowStatusBar(false)
 82	l.SetShowTitle(false)
 83	l.SetFilteringEnabled(false)
 84	l.DisableQuitKeybindings()
 85	b := &Bubble{
 86		repo:         repo,
 87		style:        style,
 88		width:        width,
 89		height:       height,
 90		widthMargin:  widthMargin,
 91		heightMargin: heightMargin,
 92		list:         l,
 93	}
 94	b.SetSize(width, height)
 95	return b
 96}
 97
 98func (b *Bubble) SetBranch(ref *plumbing.Reference) {
 99	b.repo.SetReference(ref)
100}
101
102func (b *Bubble) Init() tea.Cmd {
103	return b.updateItems()
104}
105
106func (b *Bubble) SetSize(width, height int) {
107	b.width = width
108	b.height = height
109	b.list.SetSize(width-b.widthMargin, height-b.heightMargin)
110}
111
112func (b *Bubble) Help() []types.HelpEntry {
113	return []types.HelpEntry{
114		{"enter", "select"},
115	}
116}
117
118func (b *Bubble) updateItems() tea.Cmd {
119	its := make(items, 0)
120	tags := make(items, 0)
121	ri, err := b.repo.Repository().References()
122	if err != nil {
123		return nil
124	}
125	if err = ri.ForEach(func(r *plumbing.Reference) error {
126		if r.Type() == plumbing.HashReference {
127			if r.Name().IsTag() {
128				tags = append(tags, item{r})
129			} else {
130				its = append(its, item{r})
131			}
132		}
133		return nil
134	}); err != nil {
135		return nil
136	}
137	sort.Sort(its)
138	sort.Sort(tags)
139	its = append(its, tags...)
140	itt := make([]list.Item, len(its))
141	for i, it := range its {
142		itt[i] = it
143	}
144	return b.list.SetItems(itt)
145}
146
147func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
148	cmds := make([]tea.Cmd, 0)
149	switch msg := msg.(type) {
150	case tea.WindowSizeMsg:
151		b.SetSize(msg.Width, msg.Height)
152
153	case tea.KeyMsg:
154		switch msg.String() {
155		case "R":
156			cmds = append(cmds, b.updateItems())
157		case "enter", "right", "l":
158			if b.list.Index() >= 0 {
159				ref := b.list.SelectedItem().(item).Reference
160				b.SetBranch(ref)
161			}
162		}
163	}
164
165	l, cmd := b.list.Update(msg)
166	b.list = l
167	cmds = append(cmds, cmd)
168
169	return b, tea.Batch(cmds...)
170}
171
172func (b *Bubble) View() string {
173	return b.list.View()
174}