summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-11-02 16:39:15 +1100
committerJesse Duffield <jessedduffield@gmail.com>2021-11-05 07:58:21 +1100
commit802cfb1a0436568c72fc998249f10f8150b352a3 (patch)
tree599f8a8bd52b786312a11f3b3cac2a2d5b7c597e
parent2fc1498517523a20a3080816ec50ee9e7fbe533d (diff)
render commit graph
-rwxr-xr-xbump_gocui.sh2
-rw-r--r--go.mod4
-rw-r--r--go.sum8
-rw-r--r--pkg/commands/loading_commits.go2
-rw-r--r--pkg/gui/commits_panel.go5
-rw-r--r--pkg/gui/gui.go2
-rw-r--r--pkg/gui/list_context.go11
-rw-r--r--pkg/gui/list_context_config.go22
-rw-r--r--pkg/gui/presentation/commits.go164
-rw-r--r--pkg/gui/presentation/graph/cell.go85
-rw-r--r--pkg/gui/presentation/graph/graph.go87
-rw-r--r--pkg/gui/presentation/graph/graph_test.go121
-rw-r--r--pkg/gui/pty.go18
-rw-r--r--pkg/gui/style/style_test.go22
-rw-r--r--pkg/gui/style/text_style.go22
-rw-r--r--pkg/gui/tasks_adapter.go24
-rw-r--r--pkg/gui/view_helpers.go5
-rw-r--r--pkg/tasks/tasks.go29
-rw-r--r--pkg/utils/color.go13
-rw-r--r--vendor/github.com/jesseduffield/gocui/gui.go16
-rw-r--r--vendor/github.com/jesseduffield/gocui/view.go36
-rw-r--r--vendor/golang.org/x/sys/unix/syscall_darwin.go21
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux.go26
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_386.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_arm.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_mips.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_386.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go2
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go1
-rw-r--r--vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go1
-rw-r--r--vendor/golang.org/x/sys/unix/ztypes_linux.go7
-rw-r--r--vendor/modules.txt4
53 files changed, 522 insertions, 263 deletions
diff --git a/bump_gocui.sh b/bump_gocui.sh
index 30e565375..3e0adebae 100755
--- a/bump_gocui.sh
+++ b/bump_gocui.sh
@@ -1,5 +1,5 @@
# Go's proxy servers are not very up-to-date so that's why we use `GOPROXY=direct`
# We specify the `awesome` branch to avoid the default behaviour of looking for a semver tag.
-GOPROXY=direct go get -u github.com/jesseduffield/gocui@awesome && go mod vendor
+GOPROXY=direct go get -u github.com/jesseduffield/gocui@awesome && go mod vendor && go mod tidy
# Note to self if you ever want to fork a repo be sure to use this same approach: it's important to use the branch name (e.g. master)
diff --git a/go.mod b/go.mod
index c48c8bebe..73704f54b 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
- github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75
+ github.com/jesseduffield/gocui v0.3.1-0.20211102081536-e4eee64f4d13
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
@@ -40,7 +40,7 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
- golang.org/x/sys v0.0.0-20211031064116-611d5d643895 // indirect
+ golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
diff --git a/go.sum b/go.sum
index 26a3dddfb..332984b7c 100644
--- a/go.sum
+++ b/go.sum
@@ -71,8 +71,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
-github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75 h1:zu+WBGwscCwu7GEuxANGl8E51HbW6ueqTF1XdAoqnZs=
-github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
+github.com/jesseduffield/gocui v0.3.1-0.20211102081536-e4eee64f4d13 h1:JB1nYX2l3s9aBtw4Ymc7KXp/Hk3IukO4u+APok6WWjo=
+github.com/jesseduffield/gocui v0.3.1-0.20211102081536-e4eee64f4d13/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
@@ -178,8 +178,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps=
-golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c h1:QOfDMdrf/UwlVR0UBq2Mpr58UzNtvgJRXA4BgPfFACs=
+golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
diff --git a/pkg/commands/loading_commits.go b/pkg/commands/loading_commits.go
index d22c803de..22f6db6d2 100644
--- a/pkg/commands/loading_commits.go
+++ b/pkg/commands/loading_commits.go
@@ -406,7 +406,7 @@ func (c *CommitListBuilder) getLogCmd(opts GetCommitsOptions) *exec.Cmd {
return c.OSCommand.ExecutableFromString(
fmt.Sprintf(
- "git log %s --oneline %s %s --abbrev=%d %s",
+ "git log --topo-order %s --oneline %s %s --abbrev=%d %s",
c.OSCommand.Quote(opts.RefName),
prettyFormat,
limitFlag,
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index 9c2914330..7185f1695 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -10,6 +10,9 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
+// after selecting the 200th commit, we'll load in all the rest
+const COMMIT_THRESHOLD = 200
+
// list panel functions
func (gui *Gui) getSelectedLocalCommit() *models.Commit {
@@ -23,7 +26,7 @@ func (gui *Gui) getSelectedLocalCommit() *models.Commit {
func (gui *Gui) handleCommitSelect() error {
state := gui.State.Panels.Commits
- if state.SelectedLineIdx > 290 && state.LimitCommits {
+ if state.SelectedLineIdx > COMMIT_THRESHOLD && state.LimitCommits {
state.LimitCommits = false
go utils.Safe(func() {
if err := gui.refreshCommitsWithLimit(); err != nil {
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index a07e1887b..76df52959 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -395,7 +395,7 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
Remotes: &remotePanelState{listPanelState{SelectedLineIdx: 0}},
RemoteBranches: &remoteBranchesState{listPanelState{SelectedLineIdx: -1}},
Tags: &tagsPanelState{listPanelState{SelectedLineIdx: -1}},
- Commits: &commitPanelState{listPanelState: listPanelState{SelectedLineIdx: -1}, LimitCommits: true},
+ Commits: &commitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, LimitCommits: true},
ReflogCommits: &reflogCommitPanelState{listPanelState{SelectedLineIdx: 0}},
SubCommits: &subCommitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, refName: ""},
CommitFiles: &commitFilesPanelState{listPanelState: listPanelState{SelectedLineIdx: -1}, refName: ""},
diff --git a/pkg/gui/list_context.go b/pkg/gui/list_context.go
index c8ef850a0..290ac3a37 100644
--- a/pkg/gui/list_context.go
+++ b/pkg/gui/list_context.go
@@ -14,6 +14,10 @@ type ListContext struct {
// the boolean here tells us whether the item is nil. This is needed because you can't work it out on the calling end once the pointer is wrapped in an interface (unless you want to use reflection)
SelectedItem func() (ListItem, bool)
OnGetPanelState func() IListPanelState
+ // if this is true, we'll call GetDisplayStrings for just the visible part of the
+ // view and re-render that. This is useful when you need to render different
+ // content based on the selection (e.g. for showing the selected commit)
+ RenderSelection bool
Gui *Gui
@@ -60,10 +64,17 @@ type ListItem interface {
func (self *ListContext) FocusLine() {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
+ // ignoring error for now
return
}
+ // we need a way of knowing whether we've rendered to the view yet.
view.FocusPoint(0, self.GetPanelState().GetSelectedLineIdx())
+ if self.RenderSelection {
+ _, originY := view.Origin()
+ displayStrings := self.GetDisplayStrings(originY, view.InnerHeight())
+ self.Gui.renderDisplayStringsAtPos(view, originY, displayStrings)
+ }
view.Footer = formatListFooter(self.GetPanelState().GetSelectedLineIdx(), self.GetItemsLength())
}
diff --git a/pkg/gui/list_context_config.go b/pkg/gui/list_context_config.go
index 6bd5a63c1..e9e585b37 100644
--- a/pkg/gui/list_context_config.go
+++ b/pkg/gui/list_context_config.go
@@ -157,18 +157,29 @@ func (gui *Gui) branchCommitsListContext() IListContext {
OnClickSelectedItem: gui.handleViewCommitFiles,
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
+ selectedCommitSha := ""
+ if gui.currentContext().GetKey() == BRANCH_COMMITS_CONTEXT_KEY {
+ selectedCommit := gui.getSelectedLocalCommit()
+ if selectedCommit != nil {
+ selectedCommitSha = selectedCommit.Sha
+ }
+ }
return presentation.GetCommitListDisplayStrings(
gui.State.Commits,
gui.State.ScreenMode != SCREEN_NORMAL,
gui.cherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref,
parseEmoji,
+ selectedCommitSha,
+ startIdx,
+ length,
)
},
SelectedItem: func() (ListItem, bool) {
item := gui.getSelectedLocalCommit()
return item, item != nil
},
+ RenderSelection: true,
}
}
@@ -215,18 +226,29 @@ func (gui *Gui) subCommitsListContext() IListContext {
OnFocus: gui.handleSubCommitSelect,
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
+ selectedCommitSha := ""
+ if gui.currentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
+ selectedCommit := gui.getSelectedSubCommit()
+ if selectedCommit != nil {
+ selectedCommitSha = selectedCommit.Sha
+ }
+ }
return presentation.GetCommitListDisplayStrings(
gui.State.SubCommits,
gui.State.ScreenMode != SCREEN_NORMAL,
gui.cherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref,
parseEmoji,
+ selectedCommitSha,
+ 0,
+ len(gui.State.SubCommits),
)
},
SelectedItem: func() (ListItem, bool) {
item := gui.getSelectedSubCommit()
return item, item != nil
},
+ RenderSelection: true,
}
}
diff --git a/pkg/gui/presentation/commits.go b/pkg/gui/presentation/commits.go
index 2474a283e..2a24437c9 100644
--- a/pkg/gui/presentation/commits.go
+++ b/pkg/gui/presentation/commits.go
@@ -2,81 +2,133 @@ package presentation
import (
"strings"
+ "sync"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation/authors"
+ "github.com/jesseduffield/lazygit/pkg/gui/presentation/graph"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/kyokomi/emoji/v2"
)
-func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
- lines := make([][]string, len(commits))
+type pipeSetCacheKey struct {
+ commitSha string
+ commitCount int
+}
- var displayFunc func(*models.Commit, map[string]bool, bool, bool) []string
- if fullDescription {
- displayFunc = getFullDescriptionDisplayStringsForCommit
- } else {
- displayFunc = getDisplayStringsForCommit
+var pipeSetCache = make(map[pipeSetCacheKey][][]*graph.Pipe)
+var mutex sync.Mutex
+
+func GetCommitListDisplayStrings(
+ commits []*models.Commit,
+ fullDescription bool,
+ cherryPickedCommitShaMap map[string]bool,
+ diffName string,
+ parseEmoji bool,
+ selectedCommitSha string,
+ startIdx int,
+ length int,
+) [][]string {
+ mutex.Lock()
+ defer mutex.Unlock()
+
+ if len(commits) == 0 {
+ return nil
}
- for i := range commits {
- diffed := commits[i].Sha == diffName
- lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed, parseEmoji)
+ // given that our cache key is a commit sha and a commit count, it's very important that we don't actually try to render pipes
+ // when dealing with things like filtered commits.
+ cacheKey := pipeSetCacheKey{
+ commitSha: commits[0].Sha,
+ commitCount: len(commits),
}
+ pipeSets, ok := pipeSetCache[cacheKey]
+ if !ok {
+ // pipe sets are unique to a commit head. and a commit count. Sometimes we haven't loaded everything for that.
+ // so let's just cache it based on that.
+ getStyle := func(commit *models.Commit) style.TextStyle {
+ return authors.AuthorStyle(commit.Author)
+ }
+ pipeSets = graph.GetPipeSets(commits, getStyle)
+ pipeSetCache[cacheKey] = pipeSets
+ }
+
+ end := startIdx + length
+ if end > len(commits)-1 {
+ end = len(commits) - 1
+ }
+
+ filteredPipeSets := pipeSets[startIdx : end+1]
+ filteredCommits := commits[startIdx : end+1]
+ graphLines := graph.RenderAux(filteredPipeSets, filteredCommits, selectedCommitSha)
+
+ lines := make([][]string, 0, len(graphLines))
+ for i, commit := range filteredCommits {
+ lines = append(lines, displayCommit(commit, cherryPickedCommitShaMap, diffName, parseEmoji, graphLines[i], fullDescription))
+ }
return lines
}
-func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
- shaColor := theme.DefaultTextColor
- switch c.Status {
- case "unpushed":
- shaColor = style.FgRed
- case "pushed":
- shaColor = style.FgYellow
- case "merged":
- shaColor = style.FgGreen
- case "rebasing":
- shaColor = style.FgBlue
- case "reflog":
- shaColor = style.FgBlue
- }
+func displayCommit(
+ commit *models.Commit,
+ cherryPickedCommitShaMap map[string]bool,
+ diffName string,
+ parseEmoji bool,
+ graphLine string,
+ fullDescription bool,
+) []string {
- if diffed {
- shaColor = theme.DiffTerminalColor
- } else if cherryPickedCommitShaMap[c.Sha] {
- // for some reason, setting the background to blue pads out the other commits
- // horizontally. For the sake of accessibility I'm considering this a feature,
- // not a bug
- shaColor = theme.CherryPickedCommitTextStyle
+ shaColor := getShaColor(commit, diffName, cherryPickedCommitShaMap)
+
+ actionString := ""
+ if commit.Action != "" {
+ actionString = actionColorMap(commit.Action).Sprint(commit.Action) + " "
}
tagString := ""
- secondColumnString := style.FgBlue.Sprint(utils.UnixToDate(c.UnixTimestamp))
- if c.Action != "" {
- secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
- } else if c.ExtraInfo != "" {
- tagString = style.FgMagenta.SetBold().Sprint(c.ExtraInfo) + " "
+ if fullDescription {
+ if commit.ExtraInfo != "" {
+ tagString = style.FgMagenta.SetBold().Sprint(commit.ExtraInfo) + " "
+ }
+ } else {
+ if len(commit.Tags) > 0 {
+ tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(commit.Tags, " ")) + " "
+ }
}
- name := c.Name
+ name := commit.Name
if parseEmoji {
name = emoji.Sprint(name)
}
- return []string{
- shaColor.Sprint(c.ShortSha()),
- secondColumnString,
- authors.LongAuthor(c.Author),
- tagString + theme.DefaultTextColor.Sprint(name),
+ authorFunc := authors.ShortAuthor
+ if fullDescription {
+ authorFunc = authors.LongAuthor
}
+
+ cols := make([]string, 0, 5)
+ cols = append(cols, shaColor.Sprint(commit.ShortSha()))
+ if fullDescription {
+ cols = append(cols, style.FgBlue.Sprint(utils.UnixToDate(commit.UnixTimestamp)))
+ }
+ cols = append(
+ cols,
+ actionString,
+ authorFunc(commit.Author),
+ graphLine+tagString+theme.DefaultTextColor.Sprint(name),
+ )
+
+ return cols
+
}
-func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
+func getShaColor(commit *models.Commit, diffName string, cherryPickedCommitShaMap map[string]bool) style.TextStyle {
+ diffed := commit.Sha == diffName
shaColor := theme.DefaultTextColor
- switch c.Status {
+ switch commit.Status {
case "unpushed":
shaColor = style.FgRed
case "pushed":
@@ -91,31 +143,11 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
if diffed {
shaColor = theme.DiffTerminalColor
- } else if cherryPickedCommitShaMap[c.Sha] {
- // for some reason, setting the background to blue pads out the other commits
- // horizontally. For the sake of accessibility I'm considering this a feature,
- // not a bug
+ } else if cherryPickedCommitShaMap[commit.Sha] {
shaColor = theme.CherryPickedCommitTextStyle
}
- actionString := ""
- tagString := ""
- if c.Action != "" {
- actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
- } else if len(c.Tags) > 0 {
- tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(c.Tags, " ")) + " "
- }
-
- name := c.Name
- if parseEmoji {
- name = emoji.Sprint(name)
- }
-
- return []string{
- shaColor.Sprint(c.ShortSha()),
- authors.ShortAuthor(c.Author),
- actionString + tagString + theme.DefaultTextColor.Sprint(name),
- }
+ return shaColor
}
func actionColorMap(str string) style.TextStyle {
diff --git a/pkg/gui/presentation/graph/cell.go b/pkg/gui/presentation/graph/cell.go
index e4f57bf80..84ff722ba 100644
--- a/pkg/gui/presentation/graph/cell.go
+++ b/pkg/gui/presentation/graph/cell.go
@@ -1,11 +1,15 @@
package graph
import (
+ "io"
+ "sync"
+
+ "github.com/gookit/color"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
-const mergeSymbol = '⏣'
-const commitSymbol = '⎔'
+const mergeSymbol = "⏣"
+const commitSymbol = "⎔"
type cellType int
@@ -22,11 +26,11 @@ type Cell struct {
style style.TextStyle
}
-func (cell *Cell) render() string {
+func (cell *Cell) render(writer io.StringWriter) {
up, down, left, right := cell.up, cell.down, cell.left, cell.right
first, second := getBoxDrawingChars(up, down, left, right)
- var adjustedFirst rune
+ var adjustedFirst string
switch cell.cellType {
case CONNECTION:
adjustedFirst = first
@@ -47,13 +51,46 @@ func (cell *Cell) render() string {
// assert on the style of a space given a space has no styling (assuming we
// stick to only using foreground styles)
var styledSecondChar string
- if second == ' ' {
+ if second == " " {
styledSecondChar = " "
} else {
- styledSecondChar = rightStyle.Sprint(string(second))
+ styledSecondChar = cachedSprint(*rightStyle, second)
}
- return cell.style.Sprint(string(adjustedFirst)) + styledSecondChar
+ _, _ = writer.WriteString(cachedSprint(cell.style, adjustedFirst))
+ _, _ = writer.WriteString(styledSecondChar)
+}
+
+type rgbCacheKey struct {
+ *color.RGBStyle
+ str string
+}
+
+var rgbCache = make(map[rgbCacheKey]string)
+var rgbCacheMutex sync.RWMutex
+
+func cachedSprint(style style.TextStyle, str string) string {
+ switch v := style.Style.(type) {
+ case *color.RGBStyle:
+ rgbCacheMutex.RLock()
+ key := rgbCacheKey{v, str}
+ value, ok := rgbCache[key]
+ rgbCacheMutex.RUnlock()
+ if ok {
+ return value
+ }
+ value = style.Sprint(str)
+ rgbCacheMutex.Lock()
+ rgbCache[key] = value