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}