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 k.NextPage,
81 k.PrevPage,
82 },
83 {
84 k.GoToStart,
85 k.GoToEnd,
86 copyKey,
87 },
88 }
89}
90
91// Init implements tea.Model.
92func (r *Refs) Init() tea.Cmd {
93 return r.updateItemsCmd
94}
95
96// Update implements tea.Model.
97func (r *Refs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
98 cmds := make([]tea.Cmd, 0)
99 switch msg := msg.(type) {
100 case RepoMsg:
101 r.selector.Select(0)
102 r.repo = git.GitRepo(msg)
103 cmds = append(cmds, r.Init())
104 case RefMsg:
105 r.ref = msg
106 cmds = append(cmds, r.Init())
107 case RefItemsMsg:
108 cmds = append(cmds, r.selector.SetItems(msg.items))
109 i := r.selector.SelectedItem()
110 if i != nil {
111 r.activeRef = i.(RefItem).Reference
112 }
113 case selector.ActiveMsg:
114 switch sel := msg.IdentifiableItem.(type) {
115 case RefItem:
116 r.activeRef = sel.Reference
117 }
118 cmds = append(cmds, updateStatusBarCmd)
119 case selector.SelectMsg:
120 switch i := msg.IdentifiableItem.(type) {
121 case RefItem:
122 cmds = append(cmds,
123 switchRefCmd(i.Reference),
124 tabs.SelectTabCmd(int(filesTab)),
125 )
126 }
127 case tea.KeyMsg:
128 switch msg.String() {
129 case "l", "right":
130 cmds = append(cmds, r.selector.SelectItem)
131 }
132 }
133 m, cmd := r.selector.Update(msg)
134 r.selector = m.(*selector.Selector)
135 if cmd != nil {
136 cmds = append(cmds, cmd)
137 }
138 return r, tea.Batch(cmds...)
139}
140
141// View implements tea.Model.
142func (r *Refs) View() string {
143 return r.selector.View()
144}
145
146// StausBarValue implements statusbar.StatusBar.
147func (r *Refs) StatusBarValue() string {
148 if r.activeRef == nil {
149 return ""
150 }
151 return r.activeRef.Name().String()
152}
153
154// StatusBarInfo implements statusbar.StatusBar.
155func (r *Refs) StatusBarInfo() string {
156 totalPages := r.selector.TotalPages()
157 if totalPages > 1 {
158 return fmt.Sprintf("p. %d/%d", r.selector.Page()+1, totalPages)
159 }
160 return ""
161}
162
163func (r *Refs) updateItemsCmd() tea.Msg {
164 its := make(RefItems, 0)
165 refs, err := r.repo.References()
166 if err != nil {
167 return common.ErrorMsg(err)
168 }
169 for _, ref := range refs {
170 if strings.HasPrefix(ref.Name().String(), r.refPrefix) {
171 its = append(its, RefItem{Reference: ref})
172 }
173 }
174 sort.Sort(its)
175 items := make([]selector.IdentifiableItem, len(its))
176 for i, it := range its {
177 items[i] = it
178 }
179 return RefItemsMsg{
180 items: items,
181 prefix: r.refPrefix,
182 }
183}
184
185func switchRefCmd(ref *ggit.Reference) tea.Cmd {
186 return func() tea.Msg {
187 return RefMsg(ref)
188 }
189}