123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
package git
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/data"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/icons"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme"
|
|
)
|
|
|
|
// Branch is a local or remote branch.
|
|
type Branch struct {
|
|
// Name is unique; remote names carry the remote, as origin/main.
|
|
Name string
|
|
Remote bool
|
|
// Current marks the checked-out branch.
|
|
Current bool
|
|
// Upstream is the tracked remote branch, if any.
|
|
Upstream string
|
|
Ahead, Behind int
|
|
}
|
|
|
|
// GitAction is a row action of a version-control list.
|
|
type GitAction struct {
|
|
ID string
|
|
Label string
|
|
// Icon names an icon from icons/.
|
|
Icon string
|
|
}
|
|
|
|
// BranchListOptions configure a BranchList.
|
|
type BranchListOptions struct {
|
|
// Actions show on every row; Action returns their IDs.
|
|
Actions []GitAction
|
|
Status data.DataStatus
|
|
Error string
|
|
}
|
|
|
|
// BranchList shows branches with upstream and ahead/behind counts.
|
|
func BranchList(c *ui.Context, s *data.ListState[string], branches []Branch, opts BranchListOptions) GitListResult {
|
|
gitActionsCheck(opts.Actions)
|
|
var r GitListResult
|
|
v := data.List(c, s, len(branches), data.ListOptions[string]{
|
|
Key: func(i int) string { return branchKey(branches[i]) },
|
|
Label: func(i int) string { return branches[i].Name },
|
|
Status: opts.Status, Error: opts.Error, Empty: core.Msg(c, "branch_list.empty"),
|
|
}, func(i int) {
|
|
b := branches[i]
|
|
branchListRow(c, b)
|
|
gitRowActions(c, opts.Actions, b.Name, &r)
|
|
})
|
|
r.Element, r.changed, r.submitted = v.Element, v.Changed(), v.Submitted()
|
|
return r
|
|
}
|
|
|
|
func branchKey(b Branch) string {
|
|
if b.Name == "" {
|
|
panic("mujica: Branch needs a Name")
|
|
}
|
|
if b.Ahead < 0 || b.Behind < 0 {
|
|
panic("mujica: Branch Ahead and Behind must not be negative")
|
|
}
|
|
return b.Name
|
|
}
|
|
|
|
func branchListRow(c *ui.Context, b Branch) {
|
|
k := core.Tokens(c)
|
|
u := core.Density(c).Unit()
|
|
small := core.FontSize(c, theme.CaptionSize)
|
|
mark := ui.Box(c).Size(16, 16).Shrink(0).Center().Role(ui.RoleNone)
|
|
if b.Current {
|
|
mark.TextColor(k.AccentText).Children(func() { ui.Icon(c, icons.Must("check")).Size(14, 14) })
|
|
}
|
|
ui.Icon(c, icons.Must("git-branch")).Size(14, 14).Shrink(0).TextColor(k.TextMuted)
|
|
name := ui.Text(c, b.Name).SingleLine().MinWidth(0).Shrink(1)
|
|
if b.Current {
|
|
name.FontWeight(600)
|
|
ui.Text(c, core.Msg(c, "branch_list.current")).FontSize(small).TextColor(k.AccentText).Shrink(0)
|
|
}
|
|
ui.Spacer(c)
|
|
if b.Upstream != "" {
|
|
ui.Row(c).Gap(u).AlignItems(ui.Center).Shrink(0).TextColor(k.TextMuted).Children(func() {
|
|
ui.Text(c, b.Upstream).FontSize(small).SingleLine().MaxWidth(160)
|
|
if b.Ahead > 0 {
|
|
ui.Text(c, fmt.Sprintf("↑%d", b.Ahead)).FontSize(small).FontFeatures("tnum").Label(fmt.Sprintf(core.Msg(c, "branch_list.ahead"), b.Ahead))
|
|
}
|
|
if b.Behind > 0 {
|
|
ui.Text(c, fmt.Sprintf("↓%d", b.Behind)).FontSize(small).FontFeatures("tnum").Label(fmt.Sprintf(core.Msg(c, "branch_list.behind"), b.Behind))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// gitActionsCheck panics on incomplete or duplicate actions.
|
|
func gitActionsCheck(actions []GitAction) {
|
|
seen := map[string]bool{}
|
|
for _, a := range actions {
|
|
if a.ID == "" || a.Label == "" || a.Icon == "" {
|
|
panic("mujica: GitAction needs an ID, Label and Icon")
|
|
}
|
|
if seen[a.ID] {
|
|
panic("mujica: duplicate GitAction " + a.ID)
|
|
}
|
|
seen[a.ID] = true
|
|
icons.Must(a.Icon)
|
|
}
|
|
}
|
|
|
|
// gitRowActions shows row actions and records the one used.
|
|
func gitRowActions(c *ui.Context, actions []GitAction, item string, r *GitListResult) {
|
|
for _, a := range actions {
|
|
b := core.IconAction(c, icons.Must(a.Icon), a.Label).Tooltip(a.Label).Shrink(0)
|
|
if b.Clicked() && !b.IsDisabled() {
|
|
r.action, r.item = a.ID, item
|
|
slog.Debug("mujica: git list action", "action", a.ID, "item", item)
|
|
}
|
|
}
|
|
}
|