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