summaryrefslogtreecommitdiffstats
path: root/pkg/gui/presentation
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2023-10-14 12:22:54 +0200
committerStefan Haller <stefan@haller-berlin.de>2023-10-16 13:15:05 +0200
commitc550737a4f52240cd405d1dff0440397242cfe3a (patch)
tree1254171f87cebd99406ec3db31e4d32409c768db /pkg/gui/presentation
parent9e37ae3f5d39daea9cbbdc06dfdaf149a3abecfd (diff)
Truncate long branch names to make branch status visible
Diffstat (limited to 'pkg/gui/presentation')
-rw-r--r--pkg/gui/presentation/branches.go49
-rw-r--r--pkg/gui/presentation/branches_test.go214
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)
+ })
+ }
+}