diff options
author | Stefan Haller <stefan@haller-berlin.de> | 2023-10-14 12:22:54 +0200 |
---|---|---|
committer | Stefan Haller <stefan@haller-berlin.de> | 2023-10-16 13:15:05 +0200 |
commit | c550737a4f52240cd405d1dff0440397242cfe3a (patch) | |
tree | 1254171f87cebd99406ec3db31e4d32409c768db /pkg/gui/presentation | |
parent | 9e37ae3f5d39daea9cbbdc06dfdaf149a3abecfd (diff) |
Truncate long branch names to make branch status visible
Diffstat (limited to 'pkg/gui/presentation')
-rw-r--r-- | pkg/gui/presentation/branches.go | 49 | ||||
-rw-r--r-- | pkg/gui/presentation/branches_test.go | 214 |
2 files changed, 255 insertions, 8 deletions
diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index 22512aca2..a2e9f139f 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -14,6 +14,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/mattn/go-runewidth" "github.com/samber/lo" ) @@ -24,13 +25,14 @@ func GetBranchListDisplayStrings( getItemOperation func(item types.HasUrn) types.ItemOperation, fullDescription bool, diffName string, + viewWidth int, tr *i18n.TranslationSet, userConfig *config.UserConfig, worktrees []*models.Worktree, ) [][]string { return lo.Map(branches, func(branch *models.Branch, _ int) []string { diffed := branch.Name == diffName - return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, tr, userConfig, worktrees, time.Now()) + return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, viewWidth, tr, userConfig, worktrees, time.Now()) }) } @@ -40,11 +42,32 @@ func getBranchDisplayStrings( itemOperation types.ItemOperation, fullDescription bool, diffed bool, + viewWidth int, tr *i18n.TranslationSet, userConfig *config.UserConfig, worktrees []*models.Worktree, now time.Time, ) []string { + checkedOutByWorkTree := git_commands.CheckedOutByOtherWorktree(b, worktrees) + showCommitHash := fullDescription || userConfig.Gui.ShowBranchCommitHash + branchStatus := BranchStatus(b, itemOperation, tr, now) + worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree)) + + // Recency is always three characters, plus one for the space + availableWidth := viewWidth - 4 + if len(branchStatus) > 0 { + availableWidth -= runewidth.StringWidth(branchStatus) + 1 + } + if icons.IsIconEnabled() { + availableWidth -= 2 // one for the icon, one for the space + } + if showCommitHash { + availableWidth -= utils.COMMIT_HASH_SHORT_SIZE + 1 + } + if checkedOutByWorkTree { + availableWidth -= runewidth.StringWidth(worktreeIcon) + 1 + } + displayName := b.Name if b.DisplayName != "" { displayName = b.DisplayName @@ -55,13 +78,19 @@ func getBranchDisplayStrings( nameTextStyle = theme.DiffTerminalColor } + if len(displayName) > availableWidth { + // Never shorten the branch name to less then 3 characters + len := utils.Max(availableWidth, 4) + displayName = displayName[:len-1] + "…" + } coloredName := nameTextStyle.Sprint(displayName) - branchStatus := utils.WithPadding(ColoredBranchStatus(b, itemOperation, tr), 2, utils.AlignLeft) - if git_commands.CheckedOutByOtherWorktree(b, worktrees) { - worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree)) + if checkedOutByWorkTree { coloredName = fmt.Sprintf("%s %s", coloredName, style.FgDefault.Sprint(worktreeIcon)) } - coloredName = fmt.Sprintf("%s %s", coloredName, branchStatus) + if len(branchStatus) > 0 { + coloredStatus := branchStatusColor(b, itemOperation).Sprint(branchStatus) + coloredName = fmt.Sprintf("%s %s", coloredName, coloredStatus) + } recencyColor := style.FgCyan if b.Recency == " *" { @@ -75,7 +104,7 @@ func getBranchDisplayStrings( res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b))) } - if fullDescription || userConfig.Gui.ShowBranchCommitHash { + if showCommitHash { res = append(res, utils.ShortSha(b.CommitHash)) } @@ -114,7 +143,7 @@ func GetBranchTextStyle(name string) style.TextStyle { } } -func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string { +func branchStatusColor(branch *models.Branch, itemOperation types.ItemOperation) style.TextStyle { colour := style.FgYellow if itemOperation != types.ItemOperationNone { colour = style.FgCyan @@ -126,7 +155,11 @@ func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperatio colour = style.FgMagenta } - return colour.Sprint(BranchStatus(branch, itemOperation, tr, time.Now())) + return colour +} + +func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string { + return branchStatusColor(branch, itemOperation).Sprint(BranchStatus(branch, itemOperation, tr, time.Now())) } func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet, now time.Time) string { diff --git a/pkg/gui/presentation/branches_test.go b/pkg/gui/presentation/branches_test.go new file mode 100644 index 000000000..2c8374feb --- /dev/null +++ b/pkg/gui/presentation/branches_test.go @@ -0,0 +1,214 @@ +package presentation + +import ( + "fmt" + "testing" + "time" + + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" + "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" +) + +func Test_getBranchDisplayStrings(t *testing.T) { + scenarios := []struct { + branch *models.Branch + itemOperation types.ItemOperation + fullDescription bool + viewWidth int + useIcons bool + checkedOutByWorktree bool + expected []string + }{ + // First some tests for when the view is wide enough so that everything fits: + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branch_name"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: true, + expected: []string{"1m", "branch_name (worktree)"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 100, + useIcons: true, + checkedOutByWorktree: true, + expected: []string{"1m", "", "branch_name "}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + UpstreamRemote: "origin", + Pushables: "0", + Pullables: "0", + }, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branch_name ✓"}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + UpstreamRemote: "origin", + Pushables: "3", + Pullables: "5", + }, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: true, + expected: []string{"1m", "branch_name (worktree) ↑3↓5"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationPushing, + fullDescription: false, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branch_name Pushing |"}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + CommitHash: "1234567890", + UpstreamRemote: "origin", + UpstreamBranch: "branch_name", + Pushables: "0", + Pullables: "0", + Subject: "commit title", + }, + itemOperation: types.ItemOperationNone, + fullDescription: true, + viewWidth: 100, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "12345678", "branch_name ✓", "origin branch_name", "commit title"}, + }, + + // Now tests for how we truncate the branch name when there's not enough room: + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 14, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branch_na…"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 14, + useIcons: false, + checkedOutByWorktree: true, + expected: []string{"1m", "bra… (worktree)"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 14, + useIcons: true, + checkedOutByWorktree: true, + expected: []string{"1m", "", "branc… "}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + UpstreamRemote: "origin", + Pushables: "0", + Pullables: "0", + }, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 14, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branch_… ✓"}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + UpstreamRemote: "origin", + Pushables: "3", + Pullables: "5", + }, + itemOperation: types.ItemOperationNone, + fullDescription: false, + viewWidth: 30, + useIcons: false, + checkedOutByWorktree: true, + expected: []string{"1m", "branch_na… (worktree) ↑3↓5"}, + }, + { + branch: &models.Branch{Name: "branch_name", Recency: "1m"}, + itemOperation: types.ItemOperationPushing, + fullDescription: false, + viewWidth: 20, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "branc… Pushing |"}, + }, + { + branch: &models.Branch{ + Name: "branch_name", + Recency: "1m", + CommitHash: "1234567890", + UpstreamRemote: "origin", + UpstreamBranch: "branch_name", + Pushables: "0", + Pullables: "0", + Subject: "commit title", + }, + itemOperation: types.ItemOperationNone, + fullDescription: true, + viewWidth: 20, + useIcons: false, + checkedOutByWorktree: false, + expected: []string{"1m", "12345678", "bran… ✓", "origin branch_name", "commit title"}, + }, + } + + c := utils.NewDummyCommon() + + for i, s := range scenarios { + icons.SetNerdFontsVersion(lo.Ternary(s.useIcons, "3", "")) + + worktrees := []*models.Worktree{} + if s.checkedOutByWorktree { + worktrees = append(worktrees, &models.Worktree{Branch: s.branch.Name}) + } + + t.Run(fmt.Sprintf("getBranchDisplayStrings_%d", i), func(t *testing.T) { + strings := getBranchDisplayStrings(s.branch, s.itemOperation, s.fullDescription, false, s.viewWidth, c.Tr, c.UserConfig, worktrees, time.Time{}) + assert.Equal(t, s.expected, strings) + }) + } +} |