1package repo
2
3import (
4 "fmt"
5 "io"
6 "strings"
7 "time"
8
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/styles"
14 "github.com/muesli/reflow/truncate"
15)
16
17// LogItem is a item in the log list that displays a git commit.
18type LogItem struct {
19 *git.Commit
20}
21
22// ID implements selector.IdentifiableItem.
23func (i LogItem) ID() string {
24 return i.Commit.ID.String()
25}
26
27// Title returns the item title. Implements list.DefaultItem.
28func (i LogItem) Title() string {
29 if i.Commit != nil {
30 return strings.Split(i.Commit.Message, "\n")[0]
31 }
32 return ""
33}
34
35// Description returns the item description. Implements list.DefaultItem.
36func (i LogItem) Description() string { return "" }
37
38// FilterValue implements list.Item.
39func (i LogItem) FilterValue() string { return i.Title() }
40
41// LogItemDelegate is the delegate for LogItem.
42type LogItemDelegate struct {
43 style *styles.Styles
44}
45
46// Height returns the item height. Implements list.ItemDelegate.
47func (d LogItemDelegate) Height() int { return 2 }
48
49// Spacing returns the item spacing. Implements list.ItemDelegate.
50func (d LogItemDelegate) Spacing() int { return 1 }
51
52// Update updates the item. Implements list.ItemDelegate.
53func (d LogItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
54
55// Render renders the item. Implements list.ItemDelegate.
56func (d LogItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
57 i, ok := listItem.(LogItem)
58 if !ok {
59 return
60 }
61 if i.Commit == nil {
62 return
63 }
64
65 width := lipgloss.Width
66 titleStyle := d.style.LogItemTitle.Copy()
67 style := d.style.LogItemInactive
68 if index == m.Index() {
69 titleStyle.Bold(true)
70 style = d.style.LogItemActive
71 }
72 hash := " " + i.Commit.ID.String()[:7]
73 title := titleStyle.Render(
74 truncateString(i.Title(), m.Width()-style.GetHorizontalFrameSize()-width(hash)-2, "…"),
75 )
76 hash = d.style.LogItemHash.Copy().
77 Align(lipgloss.Right).
78 Width(m.Width() -
79 style.GetHorizontalFrameSize() -
80 width(title) -
81 // FIXME where this "1" is coming from?
82 1).
83 Render(hash)
84 author := i.Author.Name
85 commiter := i.Committer.Name
86 who := ""
87 if author != "" && commiter != "" {
88 who = fmt.Sprintf("%s committed", commiter)
89 if author != commiter {
90 who = fmt.Sprintf("%s authored and %s", author, who)
91 }
92 who += " "
93 }
94 date := fmt.Sprintf("on %s", i.Committer.When.Format("Feb 02"))
95 if i.Committer.When.Year() != time.Now().Year() {
96 date += fmt.Sprintf(" %d", i.Committer.When.Year())
97 }
98 who += date
99 who = truncateString(who, m.Width()-style.GetHorizontalFrameSize(), "…")
100 fmt.Fprint(w,
101 style.Render(
102 lipgloss.JoinVertical(lipgloss.Top,
103 lipgloss.JoinHorizontal(lipgloss.Left,
104 title,
105 hash,
106 ),
107 who,
108 ),
109 ),
110 )
111
112 // leftMargin := d.style.LogItemSelector.GetMarginLeft() +
113 // d.style.LogItemSelector.GetWidth() +
114 // d.style.LogItemHash.GetMarginLeft() +
115 // d.style.LogItemHash.GetWidth() +
116 // d.style.LogItemInactive.GetMarginLeft()
117 // title := truncateString(i.Title(), m.Width()-leftMargin, "…")
118 // if index == m.Index() {
119 // fmt.Fprint(w, d.style.LogItemSelector.Render(">")+
120 // d.style.LogItemHash.Bold(true).Render(hash[:7])+
121 // d.style.LogItemActive.Render(title))
122 // } else {
123 // fmt.Fprint(w, d.style.LogItemSelector.Render(" ")+
124 // d.style.LogItemHash.Render(hash[:7])+
125 // d.style.LogItemInactive.Render(title))
126 // }
127 // fmt.Fprintln(w)
128}
129
130func truncateString(s string, max int, tail string) string {
131 if max < 0 {
132 max = 0
133 }
134 return truncate.StringWithTail(s, uint(max), tail)
135}