1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-31 14:24:25 +03:00

Truncate long branch names to make branch status visible

This commit is contained in:
Stefan Haller
2023-10-14 12:22:54 +02:00
parent 9e37ae3f5d
commit c550737a4f
3 changed files with 256 additions and 8 deletions

View File

@ -30,6 +30,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
c.State().GetItemOperation,
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().Diffing.Ref,
c.Views().Branches.Width(),
c.Tr,
c.UserConfig,
c.Model().Worktrees,

View File

@ -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 {

View File

@ -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)
})
}
}