1
0
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:
Stefan Haller 2023-10-16 13:30:24 +02:00 committed by GitHub
commit c4fc30c243
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 373 additions and 101 deletions

View File

@ -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!).

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@ import (
const unitTestDescription = "test test"
const (
defaultWidth = 100
defaultWidth = 150
defaultHeight = 100
)

View File

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