mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-17 21:18:31 +02:00
Truncate branch names to make branch status always visible (#3075)
This commit is contained in:
commit
c4fc30c243
@ -97,6 +97,10 @@ To run gofumpt from your terminal go:
|
||||
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
|
||||
```
|
||||
|
||||
## Programming Font
|
||||
|
||||
Lazygit supports [Nerd Fonts](https://www.nerdfonts.com) to render certain icons. Sometimes we use some of these icons verbatim in string literals in the code (mainly in tests), so you need to set your development environment to use a nerd font to see these.
|
||||
|
||||
## Internationalisation
|
||||
|
||||
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
|
||||
|
@ -20,10 +20,11 @@ type BaseContext struct {
|
||||
onFocusFn onFocusFn
|
||||
onFocusLostFn onFocusLostFn
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
highlightOnFocus bool
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
needsRerenderOnWidthChange bool
|
||||
highlightOnFocus bool
|
||||
|
||||
*ParentContextMgr
|
||||
}
|
||||
@ -36,14 +37,15 @@ type (
|
||||
var _ types.IBaseContext = &BaseContext{}
|
||||
|
||||
type NewBaseContextOpts struct {
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
View *gocui.View
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
HasUncontrolledBounds bool // negating for the sake of making false the default
|
||||
HighlightOnFocus bool
|
||||
Kind types.ContextKind
|
||||
Key types.ContextKey
|
||||
View *gocui.View
|
||||
WindowName string
|
||||
Focusable bool
|
||||
Transient bool
|
||||
HasUncontrolledBounds bool // negating for the sake of making false the default
|
||||
HighlightOnFocus bool
|
||||
NeedsRerenderOnWidthChange bool
|
||||
|
||||
OnGetOptionsMap func() map[string]string
|
||||
}
|
||||
@ -54,17 +56,18 @@ func NewBaseContext(opts NewBaseContextOpts) *BaseContext {
|
||||
hasControlledBounds := !opts.HasUncontrolledBounds
|
||||
|
||||
return &BaseContext{
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
view: opts.View,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
hasControlledBounds: hasControlledBounds,
|
||||
highlightOnFocus: opts.HighlightOnFocus,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
kind: opts.Kind,
|
||||
key: opts.Key,
|
||||
view: opts.View,
|
||||
windowName: opts.WindowName,
|
||||
onGetOptionsMap: opts.OnGetOptionsMap,
|
||||
focusable: opts.Focusable,
|
||||
transient: opts.Transient,
|
||||
hasControlledBounds: hasControlledBounds,
|
||||
highlightOnFocus: opts.HighlightOnFocus,
|
||||
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,6 +193,10 @@ func (self *BaseContext) HasControlledBounds() bool {
|
||||
return self.hasControlledBounds
|
||||
}
|
||||
|
||||
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
|
||||
return self.needsRerenderOnWidthChange
|
||||
}
|
||||
|
||||
func (self *BaseContext) Title() string {
|
||||
return ""
|
||||
}
|
||||
|
@ -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,
|
||||
@ -40,11 +41,12 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
FilteredListViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
@ -68,11 +68,12 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
SearchTrait: NewSearchTrait(c),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
@ -43,11 +43,12 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
FilteredListViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
@ -36,12 +36,13 @@ func NewRemoteBranchesContext(
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
@ -115,12 +115,13 @@ func NewSubCommitsContext(
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
@ -1,7 +1,6 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@ -17,7 +16,7 @@ func (self *ScreenModeActions) Next() error {
|
||||
),
|
||||
)
|
||||
|
||||
return self.rerenderViewsWithScreenModeDependentContent()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ScreenModeActions) Prev() error {
|
||||
@ -28,32 +27,9 @@ func (self *ScreenModeActions) Prev() error {
|
||||
),
|
||||
)
|
||||
|
||||
return self.rerenderViewsWithScreenModeDependentContent()
|
||||
}
|
||||
|
||||
// these views need to be re-rendered when the screen mode changes. The commits view,
|
||||
// for example, will show authorship information in half and full screen mode.
|
||||
func (self *ScreenModeActions) rerenderViewsWithScreenModeDependentContent() error {
|
||||
// for now we re-render all list views.
|
||||
for _, context := range self.c.Context().AllList() {
|
||||
if err := self.rerenderView(context.GetView()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ScreenModeActions) rerenderView(view *gocui.View) error {
|
||||
context, ok := self.c.Helpers().View.ContextForView(view.Name())
|
||||
if !ok {
|
||||
self.c.Log.Errorf("no context found for view %s", view.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
return context.HandleRender()
|
||||
}
|
||||
|
||||
func nextIntInCycle(sl []types.WindowMaximisation, current types.WindowMaximisation) types.WindowMaximisation {
|
||||
for i, val := range sl {
|
||||
if val == current {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
@ -106,7 +107,7 @@ func (self *StatusController) onClick() error {
|
||||
}
|
||||
|
||||
cx, _ := self.c.Views().Status.Cursor()
|
||||
upstreamStatus := presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr)
|
||||
upstreamStatus := presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now())
|
||||
repoName := self.c.Git().RepoPaths.RepoName()
|
||||
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||
switch workingTreeState {
|
||||
|
@ -44,8 +44,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
contextsToRerender := []types.Context{}
|
||||
|
||||
// we assume that the view has already been created.
|
||||
setViewFromDimensions := func(viewName string, windowName string) (*gocui.View, error) {
|
||||
setViewFromDimensions := func(context types.Context) (*gocui.View, error) {
|
||||
viewName := context.GetViewName()
|
||||
windowName := context.GetWindowName()
|
||||
|
||||
dimensionsObj, ok := viewDimensions[windowName]
|
||||
|
||||
view, err := g.View(viewName)
|
||||
@ -67,6 +72,16 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if view.Frame {
|
||||
frameOffset = 0
|
||||
}
|
||||
|
||||
if context.NeedsRerenderOnWidthChange() {
|
||||
// view.Width() returns the width -1 for some reason
|
||||
oldWidth := view.Width() + 1
|
||||
newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset
|
||||
if oldWidth != newWidth {
|
||||
contextsToRerender = append(contextsToRerender, context)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = g.SetView(
|
||||
viewName,
|
||||
dimensionsObj.X0-frameOffset,
|
||||
@ -85,7 +100,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := setViewFromDimensions(context.GetViewName(), context.GetWindowName())
|
||||
_, err := setViewFromDimensions(context)
|
||||
if err != nil && !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
@ -146,6 +161,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
for _, context := range contextsToRerender {
|
||||
if err := context.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// here is a good place log some stuff
|
||||
// if you run `lazygit --logs`
|
||||
// this will let you see these branches as prettified json
|
||||
|
@ -3,6 +3,7 @@ package presentation
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@ -13,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"
|
||||
)
|
||||
|
||||
@ -23,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)
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, viewWidth, tr, userConfig, worktrees, time.Now())
|
||||
})
|
||||
}
|
||||
|
||||
@ -39,10 +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
|
||||
@ -53,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 == " *" {
|
||||
@ -73,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))
|
||||
}
|
||||
|
||||
@ -112,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
|
||||
@ -124,13 +155,17 @@ func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperatio
|
||||
colour = style.FgMagenta
|
||||
}
|
||||
|
||||
return colour.Sprint(BranchStatus(branch, itemOperation, tr))
|
||||
return colour
|
||||
}
|
||||
|
||||
func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
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 {
|
||||
itemOperationStr := itemOperationToString(itemOperation, tr)
|
||||
if itemOperationStr != "" {
|
||||
return itemOperationStr + " " + utils.Loader()
|
||||
return itemOperationStr + " " + utils.Loader(now)
|
||||
}
|
||||
|
||||
if !branch.IsTrackingRemote() {
|
||||
|
214
pkg/gui/presentation/branches_test.go
Normal file
214
pkg/gui/presentation/branches_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -13,14 +13,18 @@ func IsIconEnabled() bool {
|
||||
}
|
||||
|
||||
func SetNerdFontsVersion(version string) {
|
||||
if !lo.Contains([]string{"2", "3"}, version) {
|
||||
log.Fatalf("Unsupported nerdFontVersion %s", version)
|
||||
}
|
||||
if version == "" {
|
||||
isIconEnabled = false
|
||||
} else {
|
||||
if !lo.Contains([]string{"2", "3"}, version) {
|
||||
log.Fatalf("Unsupported nerdFontVersion %s", version)
|
||||
}
|
||||
|
||||
if version == "2" {
|
||||
patchGitIconsForNerdFontsV2()
|
||||
patchFileIconsForNerdFontsV2()
|
||||
}
|
||||
if version == "2" {
|
||||
patchGitIconsForNerdFontsV2()
|
||||
patchFileIconsForNerdFontsV2()
|
||||
}
|
||||
|
||||
isIconEnabled = true
|
||||
isIconEnabled = true
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
@ -37,7 +39,7 @@ func getTagDisplayStrings(t *models.Tag, itemOperation types.ItemOperation, diff
|
||||
descriptionStr := descriptionColor.Sprint(t.Description())
|
||||
itemOperationStr := itemOperationToString(itemOperation, tr)
|
||||
if itemOperationStr != "" {
|
||||
descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader()) + " " + descriptionStr
|
||||
descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now())) + " " + descriptionStr
|
||||
}
|
||||
res = append(res, textStyle.Sprint(t.Name), descriptionStr)
|
||||
return res
|
||||
|
@ -71,7 +71,7 @@ func (self *StatusManager) GetStatusString() string {
|
||||
}
|
||||
topStatus := self.statuses[0]
|
||||
if topStatus.statusType == "waiting" {
|
||||
return topStatus.message + " " + utils.Loader()
|
||||
return topStatus.message + " " + utils.Loader(time.Now())
|
||||
}
|
||||
return topStatus.message
|
||||
}
|
||||
|
@ -60,6 +60,9 @@ type IBaseContext interface {
|
||||
// determined independently.
|
||||
HasControlledBounds() bool
|
||||
|
||||
// true if the view needs to be rerendered when its width changes
|
||||
NeedsRerenderOnWidthChange() bool
|
||||
|
||||
// returns the desired title for the view upon activation. If there is no desired title (returns empty string), then
|
||||
// no title will be set
|
||||
Title() string
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
const unitTestDescription = "test test"
|
||||
|
||||
const (
|
||||
defaultWidth = 100
|
||||
defaultWidth = 150
|
||||
defaultHeight = 100
|
||||
)
|
||||
|
||||
|
@ -28,9 +28,8 @@ func GetProjectRoot() string {
|
||||
const LoaderAnimationInterval = 50
|
||||
|
||||
// Loader dumps a string to be displayed as a loader
|
||||
func Loader() string {
|
||||
func Loader(now time.Time) string {
|
||||
characters := "|/-\\"
|
||||
now := time.Now()
|
||||
milliseconds := now.UnixMilli()
|
||||
index := milliseconds / LoaderAnimationInterval % int64(len(characters))
|
||||
return characters[index : index+1]
|
||||
|
Loading…
x
Reference in New Issue
Block a user