From f3dba743f04a7768a4d97ddcd9f9b6ce4acb9fed Mon Sep 17 00:00:00 2001 From: Artem Belyakov Date: Sat, 6 Apr 2024 13:35:50 +0200 Subject: [PATCH] Add `SpinnerConfig` This new config section allows to customize frames and rate of thespinner --- docs/Config.md | 3 +++ pkg/config/user_config.go | 13 ++++++++++ pkg/gui/context/remotes_context.go | 2 +- pkg/gui/context/tags_context.go | 2 +- .../controllers/helpers/app_status_helper.go | 9 +++---- .../helpers/inline_status_helper.go | 2 +- pkg/gui/controllers/helpers/refresh_helper.go | 2 +- pkg/gui/controllers/status_controller.go | 2 +- pkg/gui/presentation/branches.go | 21 +++++++++++---- pkg/gui/presentation/remotes.go | 7 +++-- pkg/gui/presentation/status.go | 13 ++++++++-- pkg/gui/presentation/tags.go | 14 +++++++--- pkg/gui/status/status_manager.go | 5 ++-- pkg/utils/utils.go | 11 +++----- schema/config.json | 26 +++++++++++++++++++ 15 files changed, 101 insertions(+), 31 deletions(-) diff --git a/docs/Config.md b/docs/Config.md index 603f45b4f92..cf78a3113fe 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -87,6 +87,9 @@ gui: animateExplosion: true # shows an explosion animation when nuking the working tree portraitMode: 'auto' # one of 'auto' | 'never' | 'always' filterMode: 'substring' # one of 'substring' | 'fuzzy'; see 'Filtering' section below + spinner: + frames: ['|', '/', '-', '\\'] + rate: 50 # spinner rate in milliseconds git: paging: colorArg: always diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index e93fbb06c80..20a404b16ab 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -145,6 +145,8 @@ type GuiConfig struct { // How things are filtered when typing '/'. // One of 'substring' (default) | 'fuzzy' FilterMode string `yaml:"filterMode" jsonschema:"enum=substring,enum=fuzzy"` + // Config relating to the spinner. + Spinner SpinnerConfig `yaml:"spinner"` } func (c *GuiConfig) UseFuzzySearch() bool { @@ -182,6 +184,13 @@ type CommitLengthConfig struct { Show bool `yaml:"show"` } +type SpinnerConfig struct { + // The frames of the spinner animation. + Frames []string `yaml:"frames"` + // The "speed" of the spinner in milliseconds. + Rate int `yaml:"rate" jsonschema:"minimum=1"` +} + type GitConfig struct { // See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md Paging PagingConfig `yaml:"paging"` @@ -671,6 +680,10 @@ func GetDefaultConfig() *UserConfig { AnimateExplosion: true, PortraitMode: "auto", FilterMode: "substring", + Spinner: SpinnerConfig{ + Frames: []string{"|", "/", "-", "\\"}, + Rate: 50, + }, }, Git: GitConfig{ Paging: PagingConfig{ diff --git a/pkg/gui/context/remotes_context.go b/pkg/gui/context/remotes_context.go index 73ea428aaa9..ae01bb56b7c 100644 --- a/pkg/gui/context/remotes_context.go +++ b/pkg/gui/context/remotes_context.go @@ -26,7 +26,7 @@ func NewRemotesContext(c *ContextCommon) *RemotesContext { getDisplayStrings := func(_ int, _ int) [][]string { return presentation.GetRemoteListDisplayStrings( - viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr) + viewModel.GetItems(), c.Modes().Diffing.Ref, c.State().GetItemOperation, c.Tr, c.UserConfig) } return &RemotesContext{ diff --git a/pkg/gui/context/tags_context.go b/pkg/gui/context/tags_context.go index d827564dd56..b956c77f47a 100644 --- a/pkg/gui/context/tags_context.go +++ b/pkg/gui/context/tags_context.go @@ -30,7 +30,7 @@ func NewTagsContext( return presentation.GetTagListDisplayStrings( viewModel.GetItems(), c.State().GetItemOperation, - c.Modes().Diffing.Ref, c.Tr) + c.Modes().Diffing.Ref, c.Tr, c.UserConfig) } return &TagsContext{ diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index fd6bc247fa3..c0afcbbce94 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -6,7 +6,6 @@ import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui/status" "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/utils" ) type AppStatusHelper struct { @@ -88,16 +87,16 @@ func (self *AppStatusHelper) HasStatus() bool { } func (self *AppStatusHelper) GetStatusString() string { - appStatus, _ := self.statusMgr().GetStatusString() + appStatus, _ := self.statusMgr().GetStatusString(self.c.UserConfig) return appStatus } func (self *AppStatusHelper) renderAppStatus() { self.c.OnWorker(func(_ gocui.Task) { - ticker := time.NewTicker(time.Millisecond * utils.LoaderAnimationInterval) + ticker := time.NewTicker(time.Millisecond * time.Duration(self.c.UserConfig.Gui.Spinner.Rate)) defer ticker.Stop() for range ticker.C { - appStatus, color := self.statusMgr().GetStatusString() + appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig) self.c.Views().AppStatus.FgColor = color self.c.OnUIThread(func() error { self.c.SetViewContent(self.c.Views().AppStatus, appStatus) @@ -130,7 +129,7 @@ func (self *AppStatusHelper) renderAppStatusSync(stop chan struct{}) { for { select { case <-ticker.C: - appStatus, color := self.statusMgr().GetStatusString() + appStatus, color := self.statusMgr().GetStatusString(self.c.UserConfig) self.c.Views().AppStatus.FgColor = color self.c.SetViewContent(self.c.Views().AppStatus, appStatus) // Redraw all views of the bottom line: diff --git a/pkg/gui/controllers/helpers/inline_status_helper.go b/pkg/gui/controllers/helpers/inline_status_helper.go index 7bc8cb45637..bcf186a3193 100644 --- a/pkg/gui/controllers/helpers/inline_status_helper.go +++ b/pkg/gui/controllers/helpers/inline_status_helper.go @@ -105,7 +105,7 @@ func (self *InlineStatusHelper) start(opts InlineStatusOpts) { self.contextsWithInlineStatus[opts.ContextKey] = info go utils.Safe(func() { - ticker := time.NewTicker(time.Millisecond * utils.LoaderAnimationInterval) + ticker := time.NewTicker(time.Millisecond * time.Duration(self.c.UserConfig.Gui.Spinner.Rate)) defer ticker.Stop() outer: for { diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index fd0d11881d6..66645e698a1 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -706,7 +706,7 @@ func (self *RefreshHelper) refreshStatus() { repoName := self.c.Git().RepoPaths.RepoName() - status := presentation.FormatStatus(repoName, currentBranch, types.ItemOperationNone, linkedWorktreeName, workingTreeState, self.c.Tr) + status := presentation.FormatStatus(repoName, currentBranch, types.ItemOperationNone, linkedWorktreeName, workingTreeState, self.c.Tr, self.c.UserConfig) self.c.SetViewContent(self.c.Views().Status, status) } diff --git a/pkg/gui/controllers/status_controller.go b/pkg/gui/controllers/status_controller.go index 49a182fba4a..54bd04ad5f7 100644 --- a/pkg/gui/controllers/status_controller.go +++ b/pkg/gui/controllers/status_controller.go @@ -136,7 +136,7 @@ func (self *StatusController) onClick() error { } cx, _ := self.c.Views().Status.Cursor() - upstreamStatus := presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now()) + upstreamStatus := presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now(), self.c.UserConfig) repoName := self.c.Git().RepoPaths.RepoName() workingTreeState := self.c.Git().Status.WorkingTreeState() switch workingTreeState { diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index bb1f0bdc3c9..4f9a4aba4ff 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -50,7 +50,7 @@ func getBranchDisplayStrings( ) []string { checkedOutByWorkTree := git_commands.CheckedOutByOtherWorktree(b, worktrees) showCommitHash := fullDescription || userConfig.Gui.ShowBranchCommitHash - branchStatus := BranchStatus(b, itemOperation, tr, now) + branchStatus := BranchStatus(b, itemOperation, tr, now, userConfig) worktreeIcon := lo.Ternary(icons.IsIconEnabled(), icons.LINKED_WORKTREE_ICON, fmt.Sprintf("(%s)", tr.LcWorktree)) // Recency is always three characters, plus one for the space @@ -159,14 +159,25 @@ func branchStatusColor(branch *models.Branch, itemOperation types.ItemOperation) 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 ColoredBranchStatus( + branch *models.Branch, + itemOperation types.ItemOperation, + tr *i18n.TranslationSet, + userConfig *config.UserConfig, +) string { + return branchStatusColor(branch, itemOperation).Sprint(BranchStatus(branch, itemOperation, tr, time.Now(), userConfig)) } -func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet, now time.Time) string { +func BranchStatus( + branch *models.Branch, + itemOperation types.ItemOperation, + tr *i18n.TranslationSet, + now time.Time, + userConfig *config.UserConfig, +) string { itemOperationStr := ItemOperationToString(itemOperation, tr) if itemOperationStr != "" { - return itemOperationStr + " " + utils.Loader(now) + return itemOperationStr + " " + utils.Loader(now, userConfig.Gui.Spinner) } if !branch.IsTrackingRemote() { diff --git a/pkg/gui/presentation/remotes.go b/pkg/gui/presentation/remotes.go index dc0f39ec0fe..61fc82e023f 100644 --- a/pkg/gui/presentation/remotes.go +++ b/pkg/gui/presentation/remotes.go @@ -4,6 +4,7 @@ import ( "time" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -18,10 +19,11 @@ func GetRemoteListDisplayStrings( diffName string, getItemOperation func(item types.HasUrn) types.ItemOperation, tr *i18n.TranslationSet, + userConfig *config.UserConfig, ) [][]string { return lo.Map(remotes, func(remote *models.Remote, _ int) []string { diffed := remote.Name == diffName - return getRemoteDisplayStrings(remote, diffed, getItemOperation(remote), tr) + return getRemoteDisplayStrings(remote, diffed, getItemOperation(remote), tr, userConfig) }) } @@ -31,6 +33,7 @@ func getRemoteDisplayStrings( diffed bool, itemOperation types.ItemOperation, tr *i18n.TranslationSet, + userConfig *config.UserConfig, ) []string { branchCount := len(r.Branches) @@ -46,7 +49,7 @@ func getRemoteDisplayStrings( descriptionStr := style.FgBlue.Sprintf("%d branches", branchCount) itemOperationStr := ItemOperationToString(itemOperation, tr) if itemOperationStr != "" { - descriptionStr += " " + style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now())) + descriptionStr += " " + style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now(), userConfig.Gui.Spinner)) } res = append(res, textStyle.Sprint(r.Name), descriptionStr) return res diff --git a/pkg/gui/presentation/status.go b/pkg/gui/presentation/status.go index 7bf81948f43..d3686510eb4 100644 --- a/pkg/gui/presentation/status.go +++ b/pkg/gui/presentation/status.go @@ -5,17 +5,26 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" ) -func FormatStatus(repoName string, currentBranch *models.Branch, itemOperation types.ItemOperation, linkedWorktreeName string, workingTreeState enums.RebaseMode, tr *i18n.TranslationSet) string { +func FormatStatus( + repoName string, + currentBranch *models.Branch, + itemOperation types.ItemOperation, + linkedWorktreeName string, + workingTreeState enums.RebaseMode, + tr *i18n.TranslationSet, + userConfig *config.UserConfig, +) string { status := "" if currentBranch.IsRealBranch() { - status += ColoredBranchStatus(currentBranch, itemOperation, tr) + " " + status += ColoredBranchStatus(currentBranch, itemOperation, tr, userConfig) + " " } if workingTreeState != enums.REBASE_MODE_NONE { diff --git a/pkg/gui/presentation/tags.go b/pkg/gui/presentation/tags.go index 2d3a7375513..441cd229a50 100644 --- a/pkg/gui/presentation/tags.go +++ b/pkg/gui/presentation/tags.go @@ -4,6 +4,7 @@ import ( "time" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -18,15 +19,22 @@ func GetTagListDisplayStrings( getItemOperation func(item types.HasUrn) types.ItemOperation, diffName string, tr *i18n.TranslationSet, + userConfig *config.UserConfig, ) [][]string { return lo.Map(tags, func(tag *models.Tag, _ int) []string { diffed := tag.Name == diffName - return getTagDisplayStrings(tag, getItemOperation(tag), diffed, tr) + return getTagDisplayStrings(tag, getItemOperation(tag), diffed, tr, userConfig) }) } // getTagDisplayStrings returns the display string of branch -func getTagDisplayStrings(t *models.Tag, itemOperation types.ItemOperation, diffed bool, tr *i18n.TranslationSet) []string { +func getTagDisplayStrings( + t *models.Tag, + itemOperation types.ItemOperation, + diffed bool, + tr *i18n.TranslationSet, + userConfig *config.UserConfig, +) []string { textStyle := theme.DefaultTextColor if diffed { textStyle = theme.DiffTerminalColor @@ -39,7 +47,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(time.Now())) + " " + descriptionStr + descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now(), userConfig.Gui.Spinner)) + " " + descriptionStr } res = append(res, textStyle.Sprint(t.Name), descriptionStr) return res diff --git a/pkg/gui/status/status_manager.go b/pkg/gui/status/status_manager.go index eb5b01d4a39..b1433a6f98c 100644 --- a/pkg/gui/status/status_manager.go +++ b/pkg/gui/status/status_manager.go @@ -4,6 +4,7 @@ import ( "time" "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" @@ -69,13 +70,13 @@ func (self *StatusManager) AddToastStatus(message string, kind types.ToastKind) return id } -func (self *StatusManager) GetStatusString() (string, gocui.Attribute) { +func (self *StatusManager) GetStatusString(userConfig *config.UserConfig) (string, gocui.Attribute) { if len(self.statuses) == 0 { return "", gocui.ColorDefault } topStatus := self.statuses[0] if topStatus.statusType == "waiting" { - return topStatus.message + " " + utils.Loader(time.Now()), topStatus.color + return topStatus.message + " " + utils.Loader(time.Now(), userConfig.Gui.Spinner), topStatus.color } return topStatus.message, topStatus.color } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 053b7ac2f9b..3c4bda95b6f 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -11,6 +11,7 @@ import ( "time" "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/config" ) // GetProjectRoot returns the path to the root of the project. Only to be used @@ -24,15 +25,11 @@ func GetProjectRoot() string { return strings.Split(dir, "lazygit")[0] + "lazygit" } -// The duration between two frames of the loader animation in milliseconds -const LoaderAnimationInterval = 50 - // Loader dumps a string to be displayed as a loader -func Loader(now time.Time) string { - characters := "|/-\\" +func Loader(now time.Time, config config.SpinnerConfig) string { milliseconds := now.UnixMilli() - index := milliseconds / LoaderAnimationInterval % int64(len(characters)) - return characters[index : index+1] + index := milliseconds / int64(config.Rate) % int64(len(config.Frames)) + return config.Frames[index] } // Min returns the minimum of two integers diff --git a/schema/config.json b/schema/config.json index 3a2cad06085..21860c39ceb 100644 --- a/schema/config.json +++ b/schema/config.json @@ -366,6 +366,32 @@ ], "description": "How things are filtered when typing '/'.\nOne of 'substring' (default) | 'fuzzy'", "default": "substring" + }, + "spinner": { + "properties": { + "frames": { + "items": { + "type": "string" + }, + "type": "array", + "description": "The frames of the spinner animation.", + "default": [ + "|", + "/", + "-", + "\\" + ] + }, + "rate": { + "type": "integer", + "minimum": 1, + "description": "The \"speed\" of the spinner in milliseconds.", + "default": 50 + } + }, + "additionalProperties": false, + "type": "object", + "description": "Config relating to the spinner." } }, "additionalProperties": false,