mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-03 20:46:47 -04:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
017367f940 | ||
|
|
16369dc3c2 | ||
|
|
86bb73cff1 | ||
|
|
bac12231c3 |
@@ -21,8 +21,6 @@ welcome your pull requests:
|
||||
4. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
5. Issue that pull request!
|
||||
|
||||
Please do not raise pull request from your fork's master branch: make a feature branch instead. Lazygit maintainers will sometimes push changes to your branch when reviewing a PR and we often can't do this if you use your master branch.
|
||||
|
||||
If you've never written Go in your life, then join the club! Lazygit was the maintainer's first Go program, and most contributors have never used Go before. Go is widely considered an easy-to-learn language, so if you're looking for an open source project to gain dev experience, you've come to the right place.
|
||||
|
||||
## Running in a VSCode dev container
|
||||
@@ -99,10 +97,6 @@ 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!).
|
||||
|
||||
+1
-1
@@ -319,7 +319,7 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `lvim`, `emacs`, `nano`, `micro`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
Supported presets are `vim`, `nvim`, `nvim-remote`, `emacs`, `nano`, `micro`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
|
||||
`nvim-remote` is an experimental preset for when you have invoked lazygit from within a neovim process, allowing lazygit to open the file from within the parent process rather than spawning a new one.
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ type GitCommand struct {
|
||||
|
||||
type Loaders struct {
|
||||
BranchLoader *git_commands.BranchLoader
|
||||
MergedBranchLoader *git_commands.MergedBranchLoader
|
||||
CommitFileLoader *git_commands.CommitFileLoader
|
||||
CommitLoader *git_commands.CommitLoader
|
||||
FileLoader *git_commands.FileLoader
|
||||
@@ -162,6 +163,7 @@ func NewGitCommandAux(
|
||||
worktreeCommands := git_commands.NewWorktreeCommands(gitCommon)
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands)
|
||||
mergedBranchLoader := git_commands.NewMergedBranchLoader(gitCommon)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon)
|
||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||
@@ -200,6 +202,7 @@ func NewGitCommandAux(
|
||||
Worktrees: worktreeLoader,
|
||||
StashLoader: stashLoader,
|
||||
TagLoader: tagLoader,
|
||||
MergedBranchLoader: mergedBranchLoader,
|
||||
},
|
||||
RepoPaths: repoPaths,
|
||||
}
|
||||
|
||||
@@ -568,50 +568,26 @@ func (self *CommitLoader) getMergeBase(refName string) string {
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getExistingMainBranches() []string {
|
||||
var existingBranches []string
|
||||
var wg sync.WaitGroup
|
||||
|
||||
mainBranches := self.UserConfig.Git.MainBranches
|
||||
existingBranches = make([]string, len(mainBranches))
|
||||
|
||||
for i, branchName := range mainBranches {
|
||||
wg.Add(1)
|
||||
i := i
|
||||
branchName := branchName
|
||||
go utils.Safe(func() {
|
||||
defer wg.Done()
|
||||
existingBranches := utils.ConcurrentMap(mainBranches, func(mainBranch string) string {
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", mainBranch+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
return strings.TrimSpace(ref)
|
||||
}
|
||||
|
||||
// Try to determine upstream of local main branch
|
||||
if ref, err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
|
||||
).DontLog().RunWithOutput(); err == nil {
|
||||
existingBranches[i] = strings.TrimSpace(ref)
|
||||
return
|
||||
}
|
||||
// If this failed, fallback to the local branch
|
||||
ref := "refs/heads/" + mainBranch
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
return ref
|
||||
}
|
||||
|
||||
// If this failed, a local branch for this main branch doesn't exist or it
|
||||
// has no upstream configured. Try looking for one in the "origin" remote.
|
||||
ref := "refs/remotes/origin/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
return
|
||||
}
|
||||
|
||||
// If this failed as well, try if we have the main branch as a local
|
||||
// branch. This covers the case where somebody is using git locally
|
||||
// for something, but never pushing anywhere.
|
||||
ref = "refs/heads/" + branchName
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
|
||||
).DontLog().Run(); err == nil {
|
||||
existingBranches[i] = ref
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return ""
|
||||
})
|
||||
|
||||
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
|
||||
return branch != ""
|
||||
|
||||
@@ -74,12 +74,10 @@ func TestGetCommits(t *testing.T) {
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, commitsOutput, nil).
|
||||
// here it's testing which of the configured main branches have an upstream
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", nil). // yep, origin/main exists
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "", errors.New("error")). // this one doesn't, so it checks origin instead
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/develop"}, "", errors.New("error")). // doesn't exist there, either, so it checks for a local branch
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", errors.New("error")). // no local branch either
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil). // this one does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "refs/remotes/origin/main", nil). // this one does too
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "", errors.New("error")). // this one doesn't, so check for a local branch
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", errors.New("error")). // no local branch either
|
||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/remotes/origin/master", "refs/remotes/origin/main"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||
|
||||
@@ -212,10 +210,8 @@ func TestGetCommits(t *testing.T) {
|
||||
ExpectGitArgs([]string{"log", "HEAD", "--topo-order", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s%x00%m", "--abbrev=40", "--no-show-signature", "--"}, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist; neither does
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/master"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")),
|
||||
|
||||
expectedCommits: []*models.Commit{
|
||||
@@ -250,7 +246,6 @@ func TestGetCommits(t *testing.T) {
|
||||
// here it's testing which of the configured main branches exist
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "master@{u}"}, "refs/remotes/origin/master", nil).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "main@{u}"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/remotes/origin/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "develop@{u}"}, "refs/remotes/origin/develop", nil).
|
||||
ExpectGitArgs([]string{"rev-parse", "--symbolic-full-name", "1.0-hotfixes@{u}"}, "refs/remotes/origin/1.0-hotfixes", nil).
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// Returns a set of branch names which are merged into a main branch.
|
||||
// This
|
||||
type MergedBranchLoader struct {
|
||||
c *GitCommon
|
||||
}
|
||||
|
||||
func NewMergedBranchLoader(c *GitCommon) *MergedBranchLoader {
|
||||
return &MergedBranchLoader{c: c}
|
||||
}
|
||||
|
||||
// TODO: check against upstreams, and share code with commit loader that determines main branches
|
||||
func (self *MergedBranchLoader) Load() *set.Set[string] {
|
||||
set := set.New[string]()
|
||||
|
||||
mainBranches := self.c.UserConfig.Git.MainBranches
|
||||
results := utils.ConcurrentMap(mainBranches, func(mainBranch string) []string {
|
||||
mergedBranches, err := self.GetMergedBranches(mainBranch)
|
||||
if err != nil {
|
||||
self.c.Log.Warnf("Failed to get merged branches for %s: %s", mainBranch, err)
|
||||
return nil
|
||||
}
|
||||
return mergedBranches
|
||||
})
|
||||
|
||||
for i := 0; i < len(mainBranches); i++ {
|
||||
for _, mergedBranch := range results[i] {
|
||||
set.Add(mergedBranch)
|
||||
}
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
func (self *MergedBranchLoader) GetMergedBranches(mainBranch string) ([]string, error) {
|
||||
// git for-each-ref --merged master --format '%(refname)' refs/heads/
|
||||
cmdArgs := NewGitCmd("for-each-ref").Arg(
|
||||
"--merged",
|
||||
mainBranch,
|
||||
"--format",
|
||||
"%(refname)",
|
||||
"refs/heads/",
|
||||
).ToArgv()
|
||||
|
||||
output, err := self.c.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := utils.SplitLines(output)
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
@@ -59,7 +59,6 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
|
||||
openDirInEditorTemplate: `nvim --server "$NVIM" --remote-tab {{dir}}`,
|
||||
suspend: false,
|
||||
},
|
||||
"lvim": standardTerminalEditorPreset("lvim"),
|
||||
"emacs": standardTerminalEditorPreset("emacs"),
|
||||
"micro": standardTerminalEditorPreset("micro"),
|
||||
"nano": standardTerminalEditorPreset("nano"),
|
||||
|
||||
@@ -20,11 +20,10 @@ type BaseContext struct {
|
||||
onFocusFn onFocusFn
|
||||
onFocusLostFn onFocusLostFn
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
needsRerenderOnWidthChange bool
|
||||
highlightOnFocus bool
|
||||
focusable bool
|
||||
transient bool
|
||||
hasControlledBounds bool
|
||||
highlightOnFocus bool
|
||||
|
||||
*ParentContextMgr
|
||||
}
|
||||
@@ -37,15 +36,14 @@ 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
|
||||
NeedsRerenderOnWidthChange 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
|
||||
|
||||
OnGetOptionsMap func() map[string]string
|
||||
}
|
||||
@@ -56,18 +54,17 @@ 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,
|
||||
needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange,
|
||||
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,
|
||||
ParentContextMgr: &ParentContextMgr{},
|
||||
viewTrait: viewTrait,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,10 +190,6 @@ func (self *BaseContext) HasControlledBounds() bool {
|
||||
return self.hasControlledBounds
|
||||
}
|
||||
|
||||
func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
|
||||
return self.needsRerenderOnWidthChange
|
||||
}
|
||||
|
||||
func (self *BaseContext) Title() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ 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,
|
||||
c.Model().MergedBranches,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,12 +41,11 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -68,12 +68,11 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -43,12 +43,11 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -36,13 +36,12 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -115,13 +115,12 @@ 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,
|
||||
NeedsRerenderOnWidthChange: true,
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
})),
|
||||
ListRenderer: ListRenderer{
|
||||
list: viewModel,
|
||||
|
||||
@@ -429,25 +429,43 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool) {
|
||||
self.c.Mutexes().RefreshingBranchesMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
|
||||
|
||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
// in filter mode we filter our reflog commits to just those containing the path
|
||||
// however we need all the reflog entries to populate the recencies of our branches
|
||||
// which allows us to order them correctly. So if we're filtering we'll just
|
||||
// manually load all the reflog commits here
|
||||
var err error
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
reflogCommits := self.c.Model().FilteredReflogCommits
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
// in filter mode we filter our reflog commits to just those containing the path
|
||||
// however we need all the reflog entries to populate the recencies of our branches
|
||||
// which allows us to order them correctly. So if we're filtering we'll just
|
||||
// manually load all the reflog commits here
|
||||
var err error
|
||||
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "")
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Model().Branches = branches
|
||||
self.c.Model().Branches = branches
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
mergedBranches := self.c.Git().Loaders.MergedBranchLoader.Load()
|
||||
|
||||
self.c.Model().MergedBranches = mergedBranches
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if refreshWorktrees {
|
||||
self.loadWorktrees()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -16,7 +17,7 @@ func (self *ScreenModeActions) Next() error {
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
return self.rerenderViewsWithScreenModeDependentContent()
|
||||
}
|
||||
|
||||
func (self *ScreenModeActions) Prev() error {
|
||||
@@ -27,9 +28,32 @@ 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,7 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
@@ -107,7 +106,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)
|
||||
repoName := self.c.Git().RepoPaths.RepoName()
|
||||
workingTreeState := self.c.Git().Status.WorkingTreeState()
|
||||
switch workingTreeState {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazycore/pkg/boxlayout"
|
||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||
@@ -384,6 +385,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||
BisectInfo: git_commands.NewNullBisectInfo(),
|
||||
FilesTrie: patricia.NewTrie(),
|
||||
Authors: map[string]*models.Author{},
|
||||
MergedBranches: set.New[string](),
|
||||
},
|
||||
Modes: &types.Modes{
|
||||
Filtering: filtering.New(startArgs.FilterPath),
|
||||
|
||||
+2
-23
@@ -44,13 +44,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
contextsToRerender := []types.Context{}
|
||||
|
||||
// we assume that the view has already been created.
|
||||
setViewFromDimensions := func(context types.Context) (*gocui.View, error) {
|
||||
viewName := context.GetViewName()
|
||||
windowName := context.GetWindowName()
|
||||
|
||||
setViewFromDimensions := func(viewName string, windowName string) (*gocui.View, error) {
|
||||
dimensionsObj, ok := viewDimensions[windowName]
|
||||
|
||||
view, err := g.View(viewName)
|
||||
@@ -72,16 +67,6 @@ 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,
|
||||
@@ -100,7 +85,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := setViewFromDimensions(context)
|
||||
_, err := setViewFromDimensions(context.GetViewName(), context.GetWindowName())
|
||||
if err != nil && !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
@@ -161,12 +146,6 @@ 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,8 +3,8 @@ package presentation
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
@@ -14,7 +14,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -25,14 +24,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,
|
||||
mergedBranches *set.Set[string],
|
||||
) [][]string {
|
||||
return lo.Map(branches, func(branch *models.Branch, _ int) []string {
|
||||
diffed := branch.Name == diffName
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, viewWidth, tr, userConfig, worktrees, time.Now())
|
||||
return getBranchDisplayStrings(branch, getItemOperation(branch), fullDescription, diffed, tr, userConfig, worktrees, mergedBranches)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,32 +41,11 @@ func getBranchDisplayStrings(
|
||||
itemOperation types.ItemOperation,
|
||||
fullDescription bool,
|
||||
diffed bool,
|
||||
viewWidth int,
|
||||
tr *i18n.TranslationSet,
|
||||
userConfig *config.UserConfig,
|
||||
worktrees []*models.Worktree,
|
||||
now time.Time,
|
||||
mergedBranches *set.Set[string],
|
||||
) []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
|
||||
@@ -78,19 +56,13 @@ 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)
|
||||
if checkedOutByWorkTree {
|
||||
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))
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, style.FgDefault.Sprint(worktreeIcon))
|
||||
}
|
||||
if len(branchStatus) > 0 {
|
||||
coloredStatus := branchStatusColor(b, itemOperation).Sprint(branchStatus)
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, coloredStatus)
|
||||
}
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, branchStatus)
|
||||
|
||||
recencyColor := style.FgCyan
|
||||
if b.Recency == " *" {
|
||||
@@ -104,8 +76,16 @@ func getBranchDisplayStrings(
|
||||
res = append(res, nameTextStyle.Sprint(icons.IconForBranch(b)))
|
||||
}
|
||||
|
||||
if showCommitHash {
|
||||
res = append(res, utils.ShortSha(b.CommitHash))
|
||||
if fullDescription || userConfig.Gui.ShowBranchCommitHash {
|
||||
var commitStyle style.TextStyle
|
||||
if b.HasCommitsToPush() {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusUnpushed)
|
||||
} else if mergedBranches.Includes(b.FullRefName()) {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusMerged)
|
||||
} else {
|
||||
commitStyle = getShaStyleFromStatus(models.StatusPushed)
|
||||
}
|
||||
res = append(res, commitStyle.Sprint(utils.ShortSha(b.CommitHash)))
|
||||
}
|
||||
|
||||
res = append(res, coloredName)
|
||||
@@ -143,7 +123,7 @@ func GetBranchTextStyle(name string) style.TextStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func branchStatusColor(branch *models.Branch, itemOperation types.ItemOperation) style.TextStyle {
|
||||
func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
colour := style.FgYellow
|
||||
if itemOperation != types.ItemOperationNone {
|
||||
colour = style.FgCyan
|
||||
@@ -155,17 +135,13 @@ func branchStatusColor(branch *models.Branch, itemOperation types.ItemOperation)
|
||||
colour = style.FgMagenta
|
||||
}
|
||||
|
||||
return colour
|
||||
return colour.Sprint(BranchStatus(branch, itemOperation, tr))
|
||||
}
|
||||
|
||||
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 {
|
||||
func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet) string {
|
||||
itemOperationStr := itemOperationToString(itemOperation, tr)
|
||||
if itemOperationStr != "" {
|
||||
return itemOperationStr + " " + utils.Loader(now)
|
||||
return itemOperationStr + " " + utils.Loader()
|
||||
}
|
||||
|
||||
if !branch.IsTrackingRemote() {
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -413,20 +413,8 @@ func getShaColor(
|
||||
}
|
||||
|
||||
diffed := commit.Sha != "" && commit.Sha == diffName
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch commit.Status {
|
||||
case models.StatusUnpushed:
|
||||
shaColor = style.FgRed
|
||||
case models.StatusPushed:
|
||||
shaColor = style.FgYellow
|
||||
case models.StatusMerged:
|
||||
shaColor = style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
shaColor = style.FgBlue
|
||||
case models.StatusReflog:
|
||||
shaColor = style.FgBlue
|
||||
default:
|
||||
}
|
||||
|
||||
shaColor := getShaStyleFromStatus(commit.Status)
|
||||
|
||||
if diffed {
|
||||
shaColor = theme.DiffTerminalColor
|
||||
@@ -439,6 +427,23 @@ func getShaColor(
|
||||
return shaColor
|
||||
}
|
||||
|
||||
func getShaStyleFromStatus(status models.CommitStatus) style.TextStyle {
|
||||
switch status {
|
||||
case models.StatusUnpushed:
|
||||
return style.FgRed
|
||||
case models.StatusPushed:
|
||||
return style.FgYellow
|
||||
case models.StatusMerged:
|
||||
return style.FgGreen
|
||||
case models.StatusRebasing:
|
||||
return style.FgBlue
|
||||
case models.StatusReflog:
|
||||
return style.FgBlue
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
func actionColorMap(action todo.TodoCommand) style.TextStyle {
|
||||
switch action {
|
||||
case todo.Pick:
|
||||
|
||||
@@ -3,7 +3,6 @@ package presentation
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
@@ -160,9 +159,7 @@ func getFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, di
|
||||
isDirectory := file == nil
|
||||
|
||||
if icons.IsIconEnabled() {
|
||||
icon := icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory)
|
||||
paint := color.C256(icon.Color, false)
|
||||
output += paint.Sprint(icon.Icon) + " "
|
||||
output += restColor.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory))
|
||||
}
|
||||
|
||||
output += restColor.Sprint(utils.EscapeSpecialChars(name))
|
||||
@@ -201,9 +198,7 @@ func getCommitFileLine(name string, diffName string, commitFile *models.CommitFi
|
||||
isDirectory := commitFile == nil
|
||||
|
||||
if icons.IsIconEnabled() {
|
||||
icon := icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory)
|
||||
paint := color.C256(icon.Color, false)
|
||||
output += paint.Sprint(icon.Icon) + " "
|
||||
output += colour.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory))
|
||||
}
|
||||
|
||||
output += colour.Sprint(name)
|
||||
|
||||
@@ -5,328 +5,327 @@ import (
|
||||
)
|
||||
|
||||
// https://github.com/ogham/exa/blob/master/src/output/icons.rs
|
||||
var (
|
||||
DEFAULT_FILE_ICON = IconProperties{Icon: "\uf15b", Color: 241} //
|
||||
DEFAULT_SUBMODULE_ICON = IconProperties{Icon: "\uf1d3", Color: 202} //
|
||||
DEFAULT_DIRECTORY_ICON = IconProperties{Icon: "\uf07b", Color: 241} //
|
||||
const (
|
||||
DEFAULT_FILE_ICON = "\uf15b" //
|
||||
DEFAULT_SUBMODULE_ICON = "\uf1d3" //
|
||||
DEFAULT_DIRECTORY_ICON = "\uf114" //
|
||||
)
|
||||
|
||||
// See https://github.com/nvim-tree/nvim-web-devicons/blob/master/lua/nvim-web-devicons/icons-default.lua
|
||||
var nameIconMap = map[string]IconProperties{
|
||||
".Trash": {Icon: "\uf1f8", Color: 241}, //
|
||||
".atom": {Icon: "\ue764", Color: 241}, //
|
||||
".bashprofile": {Icon: "\ue615", Color: 113}, //
|
||||
".bashrc": {Icon: "\ue795", Color: 113}, //
|
||||
".idea": {Icon: "\ue7b5", Color: 241}, //
|
||||
".git": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitattributes": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitconfig": {Icon: "\uf1d3", Color: 202}, //
|
||||
".github": {Icon: "\uf408", Color: 241}, //
|
||||
".gitignore": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitmodules": {Icon: "\uf1d3", Color: 202}, //
|
||||
".rvm": {Icon: "\ue21e", Color: 160}, //
|
||||
".vimrc": {Icon: "\ue62b", Color: 28}, //
|
||||
".vscode": {Icon: "\ue70c", Color: 39}, //
|
||||
".zshrc": {Icon: "\ue795", Color: 113}, //
|
||||
"Cargo.lock": {Icon: "\ue7a8", Color: 216}, //
|
||||
"Cargo.toml": {Icon: "\ue7a8", Color: 216}, //
|
||||
"bin": {Icon: "\ue5fc", Color: 241}, //
|
||||
"config": {Icon: "\ue5fc", Color: 241}, //
|
||||
"docker-compose.yml": {Icon: "\uf308", Color: 68}, //
|
||||
"Dockerfile": {Icon: "\uf308", Color: 68}, //
|
||||
"ds_store": {Icon: "\uf179", Color: 15}, //
|
||||
"gitignore_global": {Icon: "\uf1d3", Color: 202}, //
|
||||
"go.mod": {Icon: "\ue627", Color: 74}, //
|
||||
"go.sum": {Icon: "\ue627", Color: 74}, //
|
||||
"gradle": {Icon: "\ue256", Color: 168}, //
|
||||
"gruntfile.coffee": {Icon: "\ue611", Color: 166}, //
|
||||
"gruntfile.js": {Icon: "\ue611", Color: 166}, //
|
||||
"gruntfile.ls": {Icon: "\ue611", Color: 166}, //
|
||||
"gulpfile.coffee": {Icon: "\ue610", Color: 167}, //
|
||||
"gulpfile.js": {Icon: "\ue610", Color: 167}, //
|
||||
"gulpfile.ls": {Icon: "\ue610", Color: 168}, //
|
||||
"hidden": {Icon: "\uf023", Color: 241}, //
|
||||
"include": {Icon: "\ue5fc", Color: 241}, //
|
||||
"lib": {Icon: "\uf121", Color: 241}, //
|
||||
"localized": {Icon: "\uf179", Color: 15}, //
|
||||
"Makefile": {Icon: "\ue975", Color: 241}, //
|
||||
"node_modules": {Icon: "\ue718", Color: 197}, //
|
||||
"npmignore": {Icon: "\ue71e", Color: 197}, //
|
||||
"PKGBUILD": {Icon: "\uf303", Color: 38}, //
|
||||
"rubydoc": {Icon: "\ue73b", Color: 160}, //
|
||||
"yarn.lock": {Icon: "\ue6a7", Color: 74}, //
|
||||
var nameIconMap = map[string]string{
|
||||
".Trash": "\uf1f8", //
|
||||
".atom": "\ue764", //
|
||||
".bashprofile": "\ue615", //
|
||||
".bashrc": "\uf489", //
|
||||
".idea": "\ue7b5", //
|
||||
".git": "\uf1d3", //
|
||||
".gitattributes": "\uf1d3", //
|
||||
".gitconfig": "\uf1d3", //
|
||||
".github": "\uf408", //
|
||||
".gitignore": "\uf1d3", //
|
||||
".gitmodules": "\uf1d3", //
|
||||
".rvm": "\ue21e", //
|
||||
".vimrc": "\ue62b", //
|
||||
".vscode": "\ue70c", //
|
||||
".zshrc": "\uf489", //
|
||||
"Cargo.lock": "\ue7a8", //
|
||||
"Cargo.toml": "\ue7a8", //
|
||||
"bin": "\ue5fc", //
|
||||
"config": "\ue5fc", //
|
||||
"docker-compose.yml": "\uf308", //
|
||||
"Dockerfile": "\uf308", //
|
||||
"ds_store": "\uf179", //
|
||||
"gitignore_global": "\uf1d3", //
|
||||
"go.mod": "\ue626", //
|
||||
"go.sum": "\ue626", //
|
||||
"gradle": "\ue256", //
|
||||
"gruntfile.coffee": "\ue611", //
|
||||
"gruntfile.js": "\ue611", //
|
||||
"gruntfile.ls": "\ue611", //
|
||||
"gulpfile.coffee": "\ue610", //
|
||||
"gulpfile.js": "\ue610", //
|
||||
"gulpfile.ls": "\ue610", //
|
||||
"hidden": "\uf023", //
|
||||
"include": "\ue5fc", //
|
||||
"lib": "\uf121", //
|
||||
"localized": "\uf179", //
|
||||
"Makefile": "\uf489", //
|
||||
"node_modules": "\ue718", //
|
||||
"npmignore": "\ue71e", //
|
||||
"PKGBUILD": "\uf303", //
|
||||
"rubydoc": "\ue73b", //
|
||||
"yarn.lock": "\ue718", //
|
||||
}
|
||||
|
||||
var extIconMap = map[string]IconProperties{
|
||||
".ai": {Icon: "\ue7b4", Color: 185}, //
|
||||
".android": {Icon: "\ue70e", Color: 70}, //
|
||||
".apk": {Icon: "\ue70e", Color: 70}, //
|
||||
".apple": {Icon: "\uf179", Color: 15}, //
|
||||
".avi": {Icon: "\uf03d", Color: 140}, //
|
||||
".avif": {Icon: "\uf1c5", Color: 140}, //
|
||||
".avro": {Icon: "\ue60b", Color: 130}, //
|
||||
".awk": {Icon: "\ue795", Color: 140}, //
|
||||
".bash": {Icon: "\ue795", Color: 113}, //
|
||||
".bash_history": {Icon: "\ue795", Color: 113}, //
|
||||
".bash_profile": {Icon: "\ue795", Color: 113}, //
|
||||
".bashrc": {Icon: "\ue795", Color: 113}, //
|
||||
".bat": {Icon: "\uf17a", Color: 81}, //
|
||||
".bats": {Icon: "\ue795", Color: 241}, //
|
||||
".bmp": {Icon: "\uf1c5", Color: 149}, //
|
||||
".bz": {Icon: "\uf410", Color: 239}, //
|
||||
".bz2": {Icon: "\uf410", Color: 239}, //
|
||||
".c": {Icon: "\ue61e", Color: 111}, //
|
||||
".c++": {Icon: "\ue61d", Color: 204}, //
|
||||
".cab": {Icon: "\ue70f", Color: 241}, //
|
||||
".cc": {Icon: "\ue61d", Color: 204}, //
|
||||
".cfg": {Icon: "\ue615", Color: 255}, //
|
||||
".class": {Icon: "\ue256", Color: 168}, //
|
||||
".clj": {Icon: "\ue768", Color: 113}, //
|
||||
".cljs": {Icon: "\ue76a", Color: 74}, //
|
||||
".cls": {Icon: "\uf034", Color: 239}, //
|
||||
".cmd": {Icon: "\ue70f", Color: 239}, //
|
||||
".coffee": {Icon: "\uf0f4", Color: 185}, //
|
||||
".conf": {Icon: "\ue615", Color: 66}, //
|
||||
".cp": {Icon: "\ue61d", Color: 74}, //
|
||||
".cpio": {Icon: "\uf410", Color: 239}, //
|
||||
".cpp": {Icon: "\ue61d", Color: 74}, //
|
||||
".cs": {Icon: "\U000f031b", Color: 58}, //
|
||||
".csh": {Icon: "\ue795", Color: 240}, //
|
||||
".cshtml": {Icon: "\uf1fa", Color: 239}, //
|
||||
".csproj": {Icon: "\U000f031b", Color: 58}, //
|
||||
".css": {Icon: "\ue749", Color: 75}, //
|
||||
".csv": {Icon: "\uf1c3", Color: 113}, //
|
||||
".csx": {Icon: "\U000f031b", Color: 58}, //
|
||||
".cxx": {Icon: "\ue61d", Color: 74}, //
|
||||
".d": {Icon: "\ue7af", Color: 28}, //
|
||||
".dart": {Icon: "\ue798", Color: 25}, //
|
||||
".db": {Icon: "\uf1c0", Color: 188}, //
|
||||
".deb": {Icon: "\ue77d", Color: 88}, //
|
||||
".diff": {Icon: "\uf440", Color: 241}, //
|
||||
".djvu": {Icon: "\uf02d", Color: 241}, //
|
||||
".dll": {Icon: "\ue70f", Color: 241}, //
|
||||
".doc": {Icon: "\uf0219", Color: 26}, //
|
||||
".docx": {Icon: "\uf0219", Color: 26}, //
|
||||
".ds_store": {Icon: "\uf179", Color: 15}, //
|
||||
".DS_store": {Icon: "\uf179", Color: 15}, //
|
||||
".dump": {Icon: "\uf1c0", Color: 188}, //
|
||||
".ebook": {Icon: "\ue28b", Color: 241}, //
|
||||
".ebuild": {Icon: "\uf30d", Color: 56}, //
|
||||
".editorconfig": {Icon: "\ue615", Color: 241}, //
|
||||
".ejs": {Icon: "\ue618", Color: 185}, //
|
||||
".elm": {Icon: "\ue62c", Color: 74}, //
|
||||
".env": {Icon: "\uf462", Color: 227}, //
|
||||
".eot": {Icon: "\uf031", Color: 124}, //
|
||||
".epub": {Icon: "\ue28a", Color: 241}, //
|
||||
".erb": {Icon: "\ue73b", Color: 160}, //
|
||||
".erl": {Icon: "\ue7b1", Color: 163}, //
|
||||
".ex": {Icon: "\ue62d", Color: 140}, //
|
||||
".exe": {Icon: "\uf17a", Color: 81}, //
|
||||
".exs": {Icon: "\ue62d", Color: 140}, //
|
||||
".fish": {Icon: "\ue795", Color: 249}, //
|
||||
".flac": {Icon: "\uf001", Color: 241}, //
|
||||
".flv": {Icon: "\uf03d", Color: 241}, //
|
||||
".font": {Icon: "\uf031", Color: 241}, //
|
||||
".fs": {Icon: "\ue7a7", Color: 74}, //
|
||||
".fsi": {Icon: "\ue7a7", Color: 74}, //
|
||||
".fsx": {Icon: "\ue7a7", Color: 74}, //
|
||||
".gdoc": {Icon: "\uf1c2", Color: 40}, //
|
||||
".gem": {Icon: "\ue21e", Color: 160}, //
|
||||
".gemfile": {Icon: "\ue21e", Color: 160}, //
|
||||
".gemspec": {Icon: "\ue21e", Color: 160}, //
|
||||
".gform": {Icon: "\uf298", Color: 40}, //
|
||||
".gif": {Icon: "\uf1c5", Color: 140}, //
|
||||
".git": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitattributes": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitignore": {Icon: "\uf1d3", Color: 202}, //
|
||||
".gitmodules": {Icon: "\uf1d3", Color: 202}, //
|
||||
".go": {Icon: "\ue627", Color: 74}, //
|
||||
".gradle": {Icon: "\ue256", Color: 168}, //
|
||||
".groovy": {Icon: "\ue775", Color: 24}, //
|
||||
".gsheet": {Icon: "\uf1c3", Color: 10}, //
|
||||
".gslides": {Icon: "\uf1c4", Color: 226}, //
|
||||
".guardfile": {Icon: "\ue21e", Color: 241}, //
|
||||
".gz": {Icon: "\uf410", Color: 241}, //
|
||||
".h": {Icon: "\uf0fd", Color: 140}, //
|
||||
".hbs": {Icon: "\ue60f", Color: 202}, //
|
||||
".hpp": {Icon: "\uf0fd", Color: 140}, //
|
||||
".hs": {Icon: "\ue777", Color: 140}, //
|
||||
".htm": {Icon: "\uf13b", Color: 196}, //
|
||||
".html": {Icon: "\uf13b", Color: 196}, //
|
||||
".hxx": {Icon: "\uf0fd", Color: 140}, //
|
||||
".ico": {Icon: "\uf1c5", Color: 185}, //
|
||||
".image": {Icon: "\uf1c5", Color: 185}, //
|
||||
".iml": {Icon: "\ue7b5", Color: 239}, //
|
||||
".ini": {Icon: "\uf17a", Color: 81}, //
|
||||
".ipynb": {Icon: "\ue606", Color: 214}, //
|
||||
".iso": {Icon: "\ue271", Color: 239}, //
|
||||
".j2c": {Icon: "\uf1c5", Color: 239}, //
|
||||
".j2k": {Icon: "\uf1c5", Color: 239}, //
|
||||
".jad": {Icon: "\ue256", Color: 168}, //
|
||||
".jar": {Icon: "\ue256", Color: 168}, //
|
||||
".java": {Icon: "\ue256", Color: 168}, //
|
||||
".jfi": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jfif": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jif": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jl": {Icon: "\ue624", Color: 241}, //
|
||||
".jmd": {Icon: "\uf48a", Color: 74}, //
|
||||
".jp2": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jpe": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jpeg": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jpg": {Icon: "\uf1c5", Color: 241}, //
|
||||
".jpx": {Icon: "\uf1c5", Color: 241}, //
|
||||
".js": {Icon: "\ue74e", Color: 185}, //
|
||||
".json": {Icon: "\ue60b", Color: 185}, //
|
||||
".jsx": {Icon: "\ue7ba", Color: 45}, //
|
||||
".jxl": {Icon: "\uf1c5", Color: 241}, //
|
||||
".ksh": {Icon: "\ue795", Color: 241}, //
|
||||
".kt": {Icon: "\ue634", Color: 99}, //
|
||||
".kts": {Icon: "\ue634", Color: 99}, //
|
||||
".latex": {Icon: "\uf034", Color: 241}, //
|
||||
".less": {Icon: "\ue758", Color: 54}, //
|
||||
".lhs": {Icon: "\ue777", Color: 140}, //
|
||||
".license": {Icon: "\U000f0219", Color: 185}, //
|
||||
".localized": {Icon: "\uf179", Color: 15}, //
|
||||
".lock": {Icon: "\uf023", Color: 241}, //
|
||||
".log": {Icon: "\uf18d", Color: 188}, //
|
||||
".lua": {Icon: "\ue620", Color: 74}, //
|
||||
".lz": {Icon: "\uf410", Color: 241}, //
|
||||
".lz4": {Icon: "\uf410", Color: 241}, //
|
||||
".lzh": {Icon: "\uf410", Color: 241}, //
|
||||
".lzma": {Icon: "\uf410", Color: 241}, //
|
||||
".lzo": {Icon: "\uf410", Color: 241}, //
|
||||
".m": {Icon: "\ue61e", Color: 111}, //
|
||||
".mm": {Icon: "\ue61d", Color: 111}, //
|
||||
".m4a": {Icon: "\uf001", Color: 239}, //
|
||||
".markdown": {Icon: "\uf48a", Color: 74}, //
|
||||
".md": {Icon: "\uf48a", Color: 74}, //
|
||||
".mdx": {Icon: "\uf48a", Color: 74}, //
|
||||
".mjs": {Icon: "\ue74e", Color: 185}, //
|
||||
".mk": {Icon: "\ue795", Color: 241}, //
|
||||
".mkd": {Icon: "\uf48a", Color: 74}, //
|
||||
".mkv": {Icon: "\uf03d", Color: 241}, //
|
||||
".mobi": {Icon: "\ue28b", Color: 241}, //
|
||||
".mov": {Icon: "\uf03d", Color: 241}, //
|
||||
".mp3": {Icon: "\uf001", Color: 241}, //
|
||||
".mp4": {Icon: "\uf03d", Color: 241}, //
|
||||
".msi": {Icon: "\ue70f", Color: 241}, //
|
||||
".mustache": {Icon: "\ue60f", Color: 241}, //
|
||||
".nix": {Icon: "\uf313", Color: 111}, //
|
||||
".node": {Icon: "\U000f0399", Color: 197}, //
|
||||
".npmignore": {Icon: "\ue71e", Color: 197}, //
|
||||
".odp": {Icon: "\uf1c4", Color: 241}, //
|
||||
".ods": {Icon: "\uf1c3", Color: 241}, //
|
||||
".odt": {Icon: "\uf1c2", Color: 241}, //
|
||||
".ogg": {Icon: "\uf001", Color: 241}, //
|
||||
".ogv": {Icon: "\uf03d", Color: 241}, //
|
||||
".otf": {Icon: "\uf031", Color: 241}, //
|
||||
".part": {Icon: "\uf43a", Color: 241}, //
|
||||
".patch": {Icon: "\uf440", Color: 241}, //
|
||||
".pdf": {Icon: "\uf1c1", Color: 124}, //
|
||||
".php": {Icon: "\ue73d", Color: 61}, //
|
||||
".pl": {Icon: "\ue769", Color: 74}, //
|
||||
".png": {Icon: "\uf1c5", Color: 241}, //
|
||||
".ppt": {Icon: "\uf1c4", Color: 241}, //
|
||||
".pptx": {Icon: "\uf1c4", Color: 241}, //
|
||||
".procfile": {Icon: "\ue21e", Color: 241}, //
|
||||
".properties": {Icon: "\ue60b", Color: 185}, //
|
||||
".ps1": {Icon: "\ue795", Color: 241}, //
|
||||
".psd": {Icon: "\ue7b8", Color: 241}, //
|
||||
".pxm": {Icon: "\uf1c5", Color: 241}, //
|
||||
".py": {Icon: "\ue606", Color: 214}, //
|
||||
".pyc": {Icon: "\ue606", Color: 214}, //
|
||||
".r": {Icon: "\uf25d", Color: 68}, //
|
||||
".rakefile": {Icon: "\ue21e", Color: 160}, //
|
||||
".rar": {Icon: "\uf410", Color: 241}, //
|
||||
".razor": {Icon: "\uf1fa", Color: 81}, //
|
||||
".rb": {Icon: "\ue21e", Color: 160}, //
|
||||
".rdata": {Icon: "\uf25d", Color: 68}, //
|
||||
".rdb": {Icon: "\ue76d", Color: 160}, //
|
||||
".rdoc": {Icon: "\uf48a", Color: 74}, //
|
||||
".rds": {Icon: "\uf25d", Color: 68}, //
|
||||
".readme": {Icon: "\uf48a", Color: 74}, //
|
||||
".rlib": {Icon: "\ue7a8", Color: 216}, //
|
||||
".rmd": {Icon: "\uf48a", Color: 74}, //
|
||||
".rpm": {Icon: "\ue7bb", Color: 52}, //
|
||||
".rs": {Icon: "\ue7a8", Color: 216}, //
|
||||
".rspec": {Icon: "\ue21e", Color: 160}, //
|
||||
".rspec_parallel": {Icon: "\ue21e", Color: 160}, //
|
||||
".rspec_status": {Icon: "\ue21e", Color: 160}, //
|
||||
".rss": {Icon: "\uf09e", Color: 130}, //
|
||||
".rtf": {Icon: "\U000f0219", Color: 241}, //
|
||||
".ru": {Icon: "\ue21e", Color: 160}, //
|
||||
".rubydoc": {Icon: "\ue73b", Color: 160}, //
|
||||
".sass": {Icon: "\ue603", Color: 169}, //
|
||||
".scala": {Icon: "\ue737", Color: 74}, //
|
||||
".scss": {Icon: "\ue749", Color: 204}, //
|
||||
".sh": {Icon: "\ue795", Color: 239}, //
|
||||
".shell": {Icon: "\ue795", Color: 239}, //
|
||||
".slim": {Icon: "\ue73b", Color: 160}, //
|
||||
".sln": {Icon: "\ue70c", Color: 39}, //
|
||||
".so": {Icon: "\uf17c", Color: 241}, //
|
||||
".sql": {Icon: "\uf1c0", Color: 188}, //
|
||||
".sqlite3": {Icon: "\ue7c4", Color: 25}, //
|
||||
".sty": {Icon: "\uf034", Color: 239}, //
|
||||
".styl": {Icon: "\ue600", Color: 148}, //
|
||||
".stylus": {Icon: "\ue600", Color: 148}, //
|
||||
".svelte": {Icon: "\ue697", Color: 208}, //
|
||||
".svg": {Icon: "\uf1c5", Color: 241}, //
|
||||
".swift": {Icon: "\ue755", Color: 208}, //
|
||||
".tar": {Icon: "\uf410", Color: 241}, //
|
||||
".taz": {Icon: "\uf410", Color: 241}, //
|
||||
".tbz": {Icon: "\uf410", Color: 241}, //
|
||||
".tbz2": {Icon: "\uf410", Color: 241}, //
|
||||
".tex": {Icon: "\uf034", Color: 79}, //
|
||||
".tgz": {Icon: "\uf410", Color: 241}, //
|
||||
".tiff": {Icon: "\uf1c5", Color: 241}, //
|
||||
".tlz": {Icon: "\uf410", Color: 241}, //
|
||||
".toml": {Icon: "\ue615", Color: 241}, //
|
||||
".torrent": {Icon: "\ue275", Color: 76}, //
|
||||
".ts": {Icon: "\ue628", Color: 74}, //
|
||||
".tsv": {Icon: "\uf1c3", Color: 241}, //
|
||||
".tsx": {Icon: "\ue7ba", Color: 74}, //
|
||||
".ttf": {Icon: "\uf031", Color: 241}, //
|
||||
".twig": {Icon: "\ue61c", Color: 241}, //
|
||||
".txt": {Icon: "\uf15c", Color: 241}, //
|
||||
".txz": {Icon: "\uf410", Color: 241}, //
|
||||
".tz": {Icon: "\uf410", Color: 241}, //
|
||||
".tzo": {Icon: "\uf410", Color: 241}, //
|
||||
".video": {Icon: "\uf03d", Color: 241}, //
|
||||
".vim": {Icon: "\ue62b", Color: 28}, //
|
||||
".vue": {Icon: "\U000f0844", Color: 113}, //
|
||||
".war": {Icon: "\ue256", Color: 168}, //
|
||||
".wav": {Icon: "\uf001", Color: 241}, //
|
||||
".webm": {Icon: "\uf03d", Color: 241}, //
|
||||
".webp": {Icon: "\uf1c5", Color: 241}, //
|
||||
".windows": {Icon: "\uf17a", Color: 81}, //
|
||||
".woff": {Icon: "\uf031", Color: 241}, //
|
||||
".woff2": {Icon: "\uf031", Color: 241}, //
|
||||
".xhtml": {Icon: "\uf13b", Color: 196}, //
|
||||
".xls": {Icon: "\uf1c3", Color: 34}, //
|
||||
".xlsx": {Icon: "\uf1c3", Color: 34}, //
|
||||
".xml": {Icon: "\uf121", Color: 160}, //
|
||||
".xul": {Icon: "\uf121", Color: 166}, //
|
||||
".xz": {Icon: "\uf410", Color: 241}, //
|
||||
".yaml": {Icon: "\uf481", Color: 160}, //
|
||||
".yml": {Icon: "\uf481", Color: 160}, //
|
||||
".zip": {Icon: "\uf410", Color: 241}, //
|
||||
".zsh": {Icon: "\ue795", Color: 241}, //
|
||||
".zsh-theme": {Icon: "\ue795", Color: 241}, //
|
||||
".zshrc": {Icon: "\ue795", Color: 241}, //
|
||||
".zst": {Icon: "\uf410", Color: 241}, //
|
||||
var extIconMap = map[string]string{
|
||||
".ai": "\ue7b4", //
|
||||
".android": "\ue70e", //
|
||||
".apk": "\ue70e", //
|
||||
".apple": "\uf179", //
|
||||
".avi": "\uf03d", //
|
||||
".avif": "\uf1c5", //
|
||||
".avro": "\ue60b", //
|
||||
".awk": "\uf489", //
|
||||
".bash": "\uf489", //
|
||||
".bash_history": "\uf489", //
|
||||
".bash_profile": "\uf489", //
|
||||
".bashrc": "\uf489", //
|
||||
".bat": "\uf17a", //
|
||||
".bats": "\uf489", //
|
||||
".bmp": "\uf1c5", //
|
||||
".bz": "\uf410", //
|
||||
".bz2": "\uf410", //
|
||||
".c": "\ue61e", //
|
||||
".c++": "\ue61d", //
|
||||
".cab": "\ue70f", //
|
||||
".cc": "\ue61d", //
|
||||
".cfg": "\ue615", //
|
||||
".class": "\ue256", //
|
||||
".clj": "\ue768", //
|
||||
".cljs": "\ue76a", //
|
||||
".cls": "\uf034", //
|
||||
".cmd": "\ue70f", //
|
||||
".coffee": "\uf0f4", //
|
||||
".conf": "\ue615", //
|
||||
".cp": "\ue61d", //
|
||||
".cpio": "\uf410", //
|
||||
".cpp": "\ue61d", //
|
||||
".cs": "\U000f031b", //
|
||||
".csh": "\uf489", //
|
||||
".cshtml": "\uf1fa", //
|
||||
".csproj": "\U000f031b", //
|
||||
".css": "\ue749", //
|
||||
".csv": "\uf1c3", //
|
||||
".csx": "\U000f031b", //
|
||||
".cxx": "\ue61d", //
|
||||
".d": "\ue7af", //
|
||||
".dart": "\ue798", //
|
||||
".db": "\uf1c0", //
|
||||
".deb": "\ue77d", //
|
||||
".diff": "\uf440", //
|
||||
".djvu": "\uf02d", //
|
||||
".dll": "\ue70f", //
|
||||
".doc": "\uf1c2", //
|
||||
".docx": "\uf1c2", //
|
||||
".ds_store": "\uf179", //
|
||||
".DS_store": "\uf179", //
|
||||
".dump": "\uf1c0", //
|
||||
".ebook": "\ue28b", //
|
||||
".ebuild": "\uf30d", //
|
||||
".editorconfig": "\ue615", //
|
||||
".ejs": "\ue618", //
|
||||
".elm": "\ue62c", //
|
||||
".env": "\uf462", //
|
||||
".eot": "\uf031", //
|
||||
".epub": "\ue28a", //
|
||||
".erb": "\ue73b", //
|
||||
".erl": "\ue7b1", //
|
||||
".ex": "\ue62d", //
|
||||
".exe": "\uf17a", //
|
||||
".exs": "\ue62d", //
|
||||
".fish": "\uf489", //
|
||||
".flac": "\uf001", //
|
||||
".flv": "\uf03d", //
|
||||
".font": "\uf031", //
|
||||
".fs": "\ue7a7", //
|
||||
".fsi": "\ue7a7", //
|
||||
".fsx": "\ue7a7", //
|
||||
".gdoc": "\uf1c2", //
|
||||
".gem": "\ue21e", //
|
||||
".gemfile": "\ue21e", //
|
||||
".gemspec": "\ue21e", //
|
||||
".gform": "\uf298", //
|
||||
".gif": "\uf1c5", //
|
||||
".git": "\uf1d3", //
|
||||
".gitattributes": "\uf1d3", //
|
||||
".gitignore": "\uf1d3", //
|
||||
".gitmodules": "\uf1d3", //
|
||||
".go": "\ue626", //
|
||||
".gradle": "\ue256", //
|
||||
".groovy": "\ue775", //
|
||||
".gsheet": "\uf1c3", //
|
||||
".gslides": "\uf1c4", //
|
||||
".guardfile": "\ue21e", //
|
||||
".gz": "\uf410", //
|
||||
".h": "\uf0fd", //
|
||||
".hbs": "\ue60f", //
|
||||
".hpp": "\uf0fd", //
|
||||
".hs": "\ue777", //
|
||||
".htm": "\uf13b", //
|
||||
".html": "\uf13b", //
|
||||
".hxx": "\uf0fd", //
|
||||
".ico": "\uf1c5", //
|
||||
".image": "\uf1c5", //
|
||||
".iml": "\ue7b5", //
|
||||
".ini": "\uf17a", //
|
||||
".ipynb": "\ue606", //
|
||||
".iso": "\ue271", //
|
||||
".j2c": "\uf1c5", //
|
||||
".j2k": "\uf1c5", //
|
||||
".jad": "\ue256", //
|
||||
".jar": "\ue256", //
|
||||
".java": "\ue256", //
|
||||
".jfi": "\uf1c5", //
|
||||
".jfif": "\uf1c5", //
|
||||
".jif": "\uf1c5", //
|
||||
".jl": "\ue624", //
|
||||
".jmd": "\uf48a", //
|
||||
".jp2": "\uf1c5", //
|
||||
".jpe": "\uf1c5", //
|
||||
".jpeg": "\uf1c5", //
|
||||
".jpg": "\uf1c5", //
|
||||
".jpx": "\uf1c5", //
|
||||
".js": "\ue74e", //
|
||||
".json": "\ue60b", //
|
||||
".jsx": "\ue7ba", //
|
||||
".jxl": "\uf1c5", //
|
||||
".ksh": "\uf489", //
|
||||
".kt": "\ue634", //
|
||||
".kts": "\ue634", //
|
||||
".latex": "\uf034", //
|
||||
".less": "\ue758", //
|
||||
".lhs": "\ue777", //
|
||||
".license": "\U000f0219", //
|
||||
".localized": "\uf179", //
|
||||
".lock": "\uf023", //
|
||||
".log": "\uf18d", //
|
||||
".lua": "\ue620", //
|
||||
".lz": "\uf410", //
|
||||
".lz4": "\uf410", //
|
||||
".lzh": "\uf410", //
|
||||
".lzma": "\uf410", //
|
||||
".lzo": "\uf410", //
|
||||
".m": "\ue61e", //
|
||||
".mm": "\ue61d", //
|
||||
".m4a": "\uf001", //
|
||||
".markdown": "\uf48a", //
|
||||
".md": "\uf48a", //
|
||||
".mdx": "\uf48a", //
|
||||
".mjs": "\ue74e", //
|
||||
".mk": "\uf489", //
|
||||
".mkd": "\uf48a", //
|
||||
".mkv": "\uf03d", //
|
||||
".mobi": "\ue28b", //
|
||||
".mov": "\uf03d", //
|
||||
".mp3": "\uf001", //
|
||||
".mp4": "\uf03d", //
|
||||
".msi": "\ue70f", //
|
||||
".mustache": "\ue60f", //
|
||||
".nix": "\uf313", //
|
||||
".node": "\U000f0399", //
|
||||
".npmignore": "\ue71e", //
|
||||
".odp": "\uf1c4", //
|
||||
".ods": "\uf1c3", //
|
||||
".odt": "\uf1c2", //
|
||||
".ogg": "\uf001", //
|
||||
".ogv": "\uf03d", //
|
||||
".otf": "\uf031", //
|
||||
".part": "\uf43a", //
|
||||
".patch": "\uf440", //
|
||||
".pdf": "\uf1c1", //
|
||||
".php": "\ue73d", //
|
||||
".pl": "\ue769", //
|
||||
".png": "\uf1c5", //
|
||||
".ppt": "\uf1c4", //
|
||||
".pptx": "\uf1c4", //
|
||||
".procfile": "\ue21e", //
|
||||
".properties": "\ue60b", //
|
||||
".ps1": "\uf489", //
|
||||
".psd": "\ue7b8", //
|
||||
".pxm": "\uf1c5", //
|
||||
".py": "\ue606", //
|
||||
".pyc": "\ue606", //
|
||||
".r": "\uf25d", //
|
||||
".rakefile": "\ue21e", //
|
||||
".rar": "\uf410", //
|
||||
".razor": "\uf1fa", //
|
||||
".rb": "\ue21e", //
|
||||
".rdata": "\uf25d", //
|
||||
".rdb": "\ue76d", //
|
||||
".rdoc": "\uf48a", //
|
||||
".rds": "\uf25d", //
|
||||
".readme": "\uf48a", //
|
||||
".rlib": "\ue7a8", //
|
||||
".rmd": "\uf48a", //
|
||||
".rpm": "\ue7bb", //
|
||||
".rs": "\ue7a8", //
|
||||
".rspec": "\ue21e", //
|
||||
".rspec_parallel": "\ue21e", //
|
||||
".rspec_status": "\ue21e", //
|
||||
".rss": "\uf09e", //
|
||||
".rtf": "\U000f0219", //
|
||||
".ru": "\ue21e", //
|
||||
".rubydoc": "\ue73b", //
|
||||
".sass": "\ue603", //
|
||||
".scala": "\ue737", //
|
||||
".scss": "\ue749", //
|
||||
".sh": "\uf489", //
|
||||
".shell": "\uf489", //
|
||||
".slim": "\ue73b", //
|
||||
".sln": "\ue70c", //
|
||||
".so": "\uf17c", //
|
||||
".sql": "\uf1c0", //
|
||||
".sqlite3": "\ue7c4", //
|
||||
".sty": "\uf034", //
|
||||
".styl": "\ue600", //
|
||||
".stylus": "\ue600", //
|
||||
".svelte": "\ue697", //
|
||||
".svg": "\uf1c5", //
|
||||
".swift": "\ue755", //
|
||||
".tar": "\uf410", //
|
||||
".taz": "\uf410", //
|
||||
".tbz": "\uf410", //
|
||||
".tbz2": "\uf410", //
|
||||
".tex": "\uf034", //
|
||||
".tgz": "\uf410", //
|
||||
".tiff": "\uf1c5", //
|
||||
".tlz": "\uf410", //
|
||||
".toml": "\ue615", //
|
||||
".torrent": "\ue275", //
|
||||
".ts": "\ue628", //
|
||||
".tsv": "\uf1c3", //
|
||||
".tsx": "\ue7ba", //
|
||||
".ttf": "\uf031", //
|
||||
".twig": "\ue61c", //
|
||||
".txt": "\uf15c", //
|
||||
".txz": "\uf410", //
|
||||
".tz": "\uf410", //
|
||||
".tzo": "\uf410", //
|
||||
".video": "\uf03d", //
|
||||
".vim": "\ue62b", //
|
||||
".vue": "\U000f0844", //
|
||||
".war": "\ue256", //
|
||||
".wav": "\uf001", //
|
||||
".webm": "\uf03d", //
|
||||
".webp": "\uf1c5", //
|
||||
".windows": "\uf17a", //
|
||||
".woff": "\uf031", //
|
||||
".woff2": "\uf031", //
|
||||
".xhtml": "\uf13b", //
|
||||
".xls": "\uf1c3", //
|
||||
".xlsx": "\uf1c3", //
|
||||
".xml": "\uf121", //
|
||||
".xul": "\uf121", //
|
||||
".xz": "\uf410", //
|
||||
".yaml": "\uf481", //
|
||||
".yml": "\uf481", //
|
||||
".zip": "\uf410", //
|
||||
".zsh": "\uf489", //
|
||||
".zsh-theme": "\uf489", //
|
||||
".zshrc": "\uf489", //
|
||||
".zst": "\uf410", //
|
||||
}
|
||||
|
||||
func patchFileIconsForNerdFontsV2() {
|
||||
extIconMap[".cs"] = IconProperties{Icon: "\uf81a", Color: 58} //
|
||||
extIconMap[".csproj"] = IconProperties{Icon: "\uf81a", Color: 58} //
|
||||
extIconMap[".csx"] = IconProperties{Icon: "\uf81a", Color: 58} //
|
||||
extIconMap[".license"] = IconProperties{Icon: "\uf718", Color: 241} //
|
||||
extIconMap[".node"] = IconProperties{Icon: "\uf898", Color: 197} //
|
||||
extIconMap[".rtf"] = IconProperties{Icon: "\uf718", Color: 241} //
|
||||
extIconMap[".vue"] = IconProperties{Icon: "\ufd42", Color: 113} // ﵂
|
||||
extIconMap[".cs"] = "\uf81a" //
|
||||
extIconMap[".csproj"] = "\uf81a" //
|
||||
extIconMap[".csx"] = "\uf81a" //
|
||||
extIconMap[".license"] = "\uf718" //
|
||||
extIconMap[".node"] = "\uf898" //
|
||||
extIconMap[".rtf"] = "\uf718" //
|
||||
extIconMap[".vue"] = "\ufd42" // ﵂
|
||||
}
|
||||
|
||||
func IconForFile(name string, isSubmodule bool, isLinkedWorktree bool, isDirectory bool) IconProperties {
|
||||
func IconForFile(name string, isSubmodule bool, isLinkedWorktree bool, isDirectory bool) string {
|
||||
base := filepath.Base(name)
|
||||
if icon, ok := nameIconMap[base]; ok {
|
||||
return icon
|
||||
@@ -340,7 +339,7 @@ func IconForFile(name string, isSubmodule bool, isLinkedWorktree bool, isDirecto
|
||||
if isSubmodule {
|
||||
return DEFAULT_SUBMODULE_ICON
|
||||
} else if isLinkedWorktree {
|
||||
return IconProperties{LINKED_WORKTREE_ICON, 239}
|
||||
return LINKED_WORKTREE_ICON
|
||||
} else if isDirectory {
|
||||
return DEFAULT_DIRECTORY_ICON
|
||||
}
|
||||
|
||||
@@ -6,11 +6,6 @@ import (
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type IconProperties struct {
|
||||
Icon string
|
||||
Color uint8
|
||||
}
|
||||
|
||||
var isIconEnabled = false
|
||||
|
||||
func IsIconEnabled() bool {
|
||||
@@ -18,18 +13,14 @@ func IsIconEnabled() bool {
|
||||
}
|
||||
|
||||
func SetNerdFontsVersion(version string) {
|
||||
if version == "" {
|
||||
isIconEnabled = false
|
||||
} else {
|
||||
if !lo.Contains([]string{"2", "3"}, version) {
|
||||
log.Fatalf("Unsupported nerdFontVersion %s", version)
|
||||
}
|
||||
|
||||
if version == "2" {
|
||||
patchGitIconsForNerdFontsV2()
|
||||
patchFileIconsForNerdFontsV2()
|
||||
}
|
||||
|
||||
isIconEnabled = true
|
||||
if !lo.Contains([]string{"2", "3"}, version) {
|
||||
log.Fatalf("Unsupported nerdFontVersion %s", version)
|
||||
}
|
||||
|
||||
if version == "2" {
|
||||
patchGitIconsForNerdFontsV2()
|
||||
patchFileIconsForNerdFontsV2()
|
||||
}
|
||||
|
||||
isIconEnabled = true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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"
|
||||
@@ -39,7 +37,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()) + " " + 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(time.Now())
|
||||
return topStatus.message + " " + utils.Loader()
|
||||
}
|
||||
return topStatus.message
|
||||
}
|
||||
|
||||
+12
-9
@@ -1,6 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/set"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
@@ -223,15 +224,17 @@ type MenuItem struct {
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
Submodules []*models.SubmoduleConfig
|
||||
Branches []*models.Branch
|
||||
// set of branch names of the form 'refs/heads/mybranch'
|
||||
MergedBranches *set.Set[string]
|
||||
Commits []*models.Commit
|
||||
StashEntries []*models.StashEntry
|
||||
SubCommits []*models.Commit
|
||||
Remotes []*models.Remote
|
||||
Worktrees []*models.Worktree
|
||||
|
||||
// FilteredReflogCommits are the ones that appear in the reflog panel.
|
||||
// when in filtering mode we only include the ones that match the given path
|
||||
|
||||
@@ -60,9 +60,6 @@ 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 = 150
|
||||
defaultWidth = 100
|
||||
defaultHeight = 100
|
||||
)
|
||||
|
||||
|
||||
+26
-1
@@ -1,6 +1,10 @@
|
||||
package utils
|
||||
|
||||
import "golang.org/x/exp/slices"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// NextIndex returns the index of the element that comes after the given number
|
||||
func NextIndex(numbers []int, currentNumber int) int {
|
||||
@@ -179,3 +183,24 @@ func Shift[T any](slice []T) (T, []T) {
|
||||
slice = slice[1:]
|
||||
return value, slice
|
||||
}
|
||||
|
||||
// Map function which handles each element in its own goroutine
|
||||
func ConcurrentMap[T any, V any](items []T, fn func(T) V) []V {
|
||||
results := make([]V, len(items))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i, item := range items {
|
||||
i := i
|
||||
item := item
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
results[i] = fn(item)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
@@ -315,3 +315,13 @@ func TestMoveElement(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestConcurrentMap(t *testing.T) {
|
||||
in := []int{1, 2, 3}
|
||||
|
||||
out := ConcurrentMap(in, func(i int) int {
|
||||
return i * 2
|
||||
})
|
||||
|
||||
assert.EqualValues(t, []int{2, 4, 6}, out)
|
||||
}
|
||||
|
||||
+2
-1
@@ -28,8 +28,9 @@ func GetProjectRoot() string {
|
||||
const LoaderAnimationInterval = 50
|
||||
|
||||
// Loader dumps a string to be displayed as a loader
|
||||
func Loader(now time.Time) string {
|
||||
func Loader() string {
|
||||
characters := "|/-\\"
|
||||
now := time.Now()
|
||||
milliseconds := now.UnixMilli()
|
||||
index := milliseconds / LoaderAnimationInterval % int64(len(characters))
|
||||
return characters[index : index+1]
|
||||
|
||||
Reference in New Issue
Block a user