filesitem.go

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