1package repo
2
3import (
4 "fmt"
5 "io"
6 "io/fs"
7
8 "github.com/charmbracelet/bubbles/list"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/charmbracelet/lipgloss"
11 "github.com/charmbracelet/soft-serve/git"
12 "github.com/charmbracelet/soft-serve/ui/styles"
13 "github.com/dustin/go-humanize"
14)
15
16// FileItem is a list item for a file.
17type FileItem struct {
18 entry *git.TreeEntry
19}
20
21// ID returns the ID of the file item.
22func (i FileItem) ID() string {
23 return i.entry.Name()
24}
25
26// Title returns the title of the file item.
27func (i FileItem) Title() string {
28 return i.entry.Name()
29}
30
31// Description returns the description of the file item.
32func (i FileItem) Description() string {
33 return ""
34}
35
36// Mode returns the mode of the file item.
37func (i FileItem) Mode() fs.FileMode {
38 return i.entry.Mode()
39}
40
41// FilterValue implements list.Item.
42func (i FileItem) FilterValue() string { return i.Title() }
43
44// FileItems is a list of file items.
45type FileItems []FileItem
46
47// Len implements sort.Interface.
48func (cl FileItems) Len() int { return len(cl) }
49
50// Swap implements sort.Interface.
51func (cl FileItems) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
52
53// Less implements sort.Interface.
54func (cl FileItems) Less(i, j int) bool {
55 if cl[i].entry.IsTree() && cl[j].entry.IsTree() {
56 return cl[i].Title() < cl[j].Title()
57 } else if cl[i].entry.IsTree() {
58 return true
59 } else if cl[j].entry.IsTree() {
60 return false
61 } else {
62 return cl[i].Title() < cl[j].Title()
63 }
64}
65
66// FileItemDelegate is the delegate for the file item list.
67type FileItemDelegate struct {
68 style *styles.Styles
69}
70
71// Height returns the height of the file item list. Implements list.ItemDelegate.
72func (d FileItemDelegate) Height() int { return 1 }
73
74// Spacing returns the spacing of the file item list. Implements list.ItemDelegate.
75func (d FileItemDelegate) Spacing() int { return 0 }
76
77// Update implements list.ItemDelegate.
78func (d FileItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
79
80// Render implements list.ItemDelegate.
81func (d FileItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
82 s := d.style
83 i, ok := listItem.(FileItem)
84 if !ok {
85 return
86 }
87
88 name := i.Title()
89 size := humanize.Bytes(uint64(i.entry.Size()))
90 if i.entry.IsTree() {
91 size = ""
92 name = s.TreeFileDir.Render(name)
93 }
94 var cs lipgloss.Style
95 mode := i.Mode()
96 if index == m.Index() {
97 cs = s.TreeItemActive
98 fmt.Fprint(w, s.TreeItemSelector.Render(">"))
99 } else {
100 cs = s.TreeItemInactive
101 fmt.Fprint(w, s.TreeItemSelector.Render(" "))
102 }
103 leftMargin := s.TreeItemSelector.GetMarginLeft() +
104 s.TreeItemSelector.GetWidth() +
105 s.TreeFileMode.GetMarginLeft() +
106 s.TreeFileMode.GetWidth() +
107 cs.GetMarginLeft()
108 rightMargin := s.TreeFileSize.GetMarginLeft() + lipgloss.Width(size)
109 name = truncateString(name, m.Width()-leftMargin-rightMargin, "…")
110 sizeStyle := s.TreeFileSize.Copy().
111 Width(m.Width() -
112 leftMargin -
113 s.TreeFileSize.GetMarginLeft() -
114 lipgloss.Width(name)).
115 Align(lipgloss.Right)
116 fmt.Fprint(w, s.TreeFileMode.Render(mode.String())+
117 cs.Render(name)+
118 sizeStyle.Render(size))
119}