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}