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,120 +2,152 @@ 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 - } - - 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) + " " - } - - name := c.Name - if parseEmoji { - name = emoji.Sprint(name) - } - - return []string{ - shaColor.Sprint(c.ShortSha()), - secondColumnString, - authors.LongAuthor(c.Author), - tagString + theme.DefaultTextColor.Sprint(name), - } -} - -func getDisplayStringsForCommit(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 - } - - 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 := "" - 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, " ")) + " " + if commit.Action != "" { + actionString = actionColorMap(commit.Action).Sprint(commit.Action) + " " } - name := c.Name + tagString := "" + 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 := commit.Name if parseEmoji { name = emoji.Sprint(name) } - return []string{ - shaColor.Sprint(c.ShortSha()), - authors.ShortAuthor(c.Author), - actionString + 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 getShaColor(commit *models.Commit, diffName string, cherryPickedCommitShaMap map[string]bool) style.TextStyle { + diffed := commit.Sha == diffName + shaColor := theme.DefaultTextColor + switch commit.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 + } + + if diffed { + shaColor = theme.DiffTerminalColor + } else if cherryPickedCommitShaMap[commit.Sha] { + shaColor = theme.CherryPickedCommitTextStyle + } + + 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 + rgbCacheMutex.Unlock() + return value + case color.Basic: + return style.Sprint(str) + case color.Style: + value := style.Sprint(str) + return value + } + return style.Sprint(str) } func (cell *Cell) reset() { @@ -102,39 +139,39 @@ func (cell *Cell) setType(cellType cellType) *Cell { return cell } -func getBoxDrawingChars(up, down, left, right bool) (rune, rune) { +func getBoxDrawingChars(up, down, left, right bool) (string, string) { if up && down && left && right { - return '│', '─' + return "│", "─" } else if up && down && left && !right { - return '│', ' ' + return "│", " " } else if up && down && !left && right { - return '│', '─' + return "│", "─" } else if up && down && !left && !right { - return '│', ' ' + return "│", " " } else if up && !down && left && right { - return '┴', '─' + return "┴", "─" } else if up && !down && left && !right { - return '╯', ' ' + return "╯", " " } else if up && !down && !left && right { - return '╰', '─' + return "╰", "─" } else if up && !down && !left && !right { - return '╵', ' ' + return "╵", " " } else if !up && down && left && right { - return '┬', '─' + return "┬", "─" } else if !up && down && left && !right { - return '╮', ' ' + return "╮", " " } else if !up && down && !left && right { - return '╭', '─' + return "╭", "─" } else if !up && down && !left && !right { - return '╷', ' ' + return "╷", " " } else if !up && !down && left && right { - return '─', '─' + return "─", "─" } else if !up && !down && left && !right { - return '─', ' ' + return "─", " " } else if !up && !down && !left && right { - return '╶', '─' + return "╶", "─" } else if !up && !down && !left && !right { - return ' ', ' ' + return " ", " " } else { panic("should not be possible") } diff --git a/pkg/gui/presentation/graph/graph.go b/pkg/gui/presentation/graph/graph.go index 16d7a8ab1..0e193cba8 100644 --- a/pkg/gui/presentation/graph/graph.go +++ b/pkg/gui/presentation/graph/graph.go @@ -1,6 +1,7 @@ package graph import ( + "runtime" "sort" "strings" "sync" @@ -29,7 +30,7 @@ type Pipe struct { var highlightStyle = style.FgLightWhite.SetBold() -func ContainsCommitSha(pipes []Pipe, sha string) bool { +func ContainsCommitSha(pipes []*Pipe, sha string) bool { for _, pipe := range pipes { if equalHashes(pipe.fromSha, sha) { return true @@ -57,14 +58,14 @@ func RenderCommitGraph(commits []*models.Commit, selectedCommitSha string, getSt return lines } -func GetPipeSets(commits []*models.Commit, getStyle func(c *models.Commit) style.TextStyle) [][]Pipe { +func GetPipeSets(commits []*models.Commit, getStyle func(c *models.Commit) style.TextStyle) [][]*Pipe { if len(commits) == 0 { return nil } - pipes := []Pipe{{fromPos: 0, toPos: 0, fromSha: "START", toSha: commits[0].Sha, kind: STARTS, style: style.FgDefault}} + pipes := []*Pipe{{fromPos: 0, toPos: 0, fromSha: "START", toSha: commits[0].Sha, kind: STARTS, style: style.FgDefault}} - pipeSets := [][]Pipe{} + pipeSets := [][]*Pipe{} for _, commit := range commits { pipes = getNextPipes(pipes, commit, getStyle) pipeSets = append(pipeSets, pipes) @@ -73,29 +74,51 @@ func GetPipeSets(commits []*models.Commit, getStyle func(c *models.Commit) style return pipeSets } -func RenderAux(pipeSets [][]Pipe, commits []*models.Commit, selectedCommitSha string) []string { - lines := make([]string, len(pipeSets)) +func RenderAux(pipeSets [][]*Pipe, commits []*models.Commit, selectedCommitSha string) []string { + maxProcs := runtime.GOMAXPROCS(0) + + lines := make([]string, 0, len(pipeSets)) + // splitting up the rendering of the graph into multiple goroutines allows us to render the graph in parallel + chunks := make([][]string, maxProcs) + perProc := len(pipeSets) / maxProcs + wg := sync.WaitGroup{} - wg.Add(len(pipeSets)) - for i, pipeSet := range pipeSets { + wg.Add(maxProcs) + + for i := 0; i < maxProcs; i++ { i := i - pipeSet := pipeSet go func() { - defer wg.Done() - var prevCommit *models.Commit - if i > 0 { - prevCommit = commits[i-1] + from := i * perProc + to := (i + 1) * perProc + if i == maxProcs-1 { + to = len(pipeSets) } - line := renderPipeSet(pipeSet, selectedCommitSha, prevCommit) - lines[i] = line + innerLines := make([]string, 0, to-from) + for j, pipeSet := range pipeSets[from:to] { + k := from + j + var prevCommit *models.Commit + if k > 0 { + prevCommit = commits[k-1] + } + line := renderPipeSet(pipeSet, selectedCommitSha, prevCommit) + innerLines = append(innerLines, line) + } + chunks[i] = innerLines + wg.Done() }() } + wg.Wait() + + for _, chunk := range chunks { + lines = append(lines, chunk...) + } + return lines } -func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *models.Commit) style.TextStyle) []Pipe { - currentPipes := make([]Pipe, 0, len(prevPipes)) +func getNextPipes(prevPipes []*Pipe, commit *models.Commit, getStyle func(c *models.Commit) style.TextStyle) []*Pipe { + currentPipes := make([]*Pipe, 0, len(prevPipes)) maxPos := 0 for _, pipe := range prevPipes { // a pipe that terminated in the previous line has no bearing on the current line @@ -106,7 +129,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode maxPos = utils.Max(maxPos, pipe.toPos) } - newPipes := make([]Pipe, 0, len(currentPipes)+len(commit.Parents)) + newPipes := make([]*Pipe, 0, len(currentPipes)+len(commit.Parents)) // start by assuming that we've got a brand new commit not related to any preceding commit. // (this only happens when we're doing `git log --all`). These will be tacked onto the far end. pos := maxPos + 1 @@ -124,7 +147,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode traversedSpots := make(map[int]bool) if len(commit.Parents) > 0 { - newPipes = append(newPipes, Pipe{ + newPipes = append(newPipes, &Pipe{ fromPos: pos, toPos: pos, fromSha: commit.Sha, @@ -177,7 +200,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode for _, pipe := range currentPipes { if equalHashes(pipe.toSha, commit.Sha) { // terminating here - newPipes = append(newPipes, Pipe{ + newPipes = append(newPipes, &Pipe{ fromPos: pipe.toPos, toPos: pos, fromSha: pipe.fromSha, @@ -189,7 +212,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode } else if pipe.toPos < pos { // continuing here availablePos := getNextAvailablePosForContinuingPipe() - newPipes = append(newPipes, Pipe{ + newPipes = append(newPipes, &Pipe{ fromPos: pipe.toPos, toPos: availablePos, fromSha: pipe.fromSha, @@ -205,7 +228,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode for _, parent := range commit.Parents[1:] { availablePos := getNextAvailablePosForNewPipe() // need to act as if continuing pipes are going to continue on the same line. - newPipes = append(newPipes, Pipe{ + newPipes = append(newPipes, &Pipe{ fromPos: pos, toPos: availablePos, fromSha: commit.Sha, @@ -229,7 +252,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode last = i } } - newPipes = append(newPipes, Pipe{ + newPipes = append(newPipes, &Pipe{ fromPos: pipe.toPos, toPos: last, fromSha: pipe.fromSha, @@ -253,7 +276,7 @@ func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *mode } func renderPipeSet( - pipes []Pipe, + pipes []*Pipe, selectedCommitSha string, prevCommit *models.Commit, ) string { @@ -279,7 +302,7 @@ func renderPipeSet( cells[i] = &Cell{cellType: CONNECTION, style: style.FgDefault} } - renderPipe := func(pipe Pipe, style style.TextStyle, overrideRightStyle bool) { + renderPipe := func(pipe *Pipe, style style.TextStyle, overrideRightStyle bool) { left := pipe.left() right := pipe.right() @@ -313,9 +336,9 @@ func renderPipeSet( // so we have our commit pos again, now it's time to build the cells. // we'll handle the one that's sourced from our selected commit last so that it can override the other cells. - selectedPipes := []Pipe{} + selectedPipes := []*Pipe{} // pre-allocating this one because most of the time we'll only have non-selected pipes - nonSelectedPipes := make([]Pipe, 0, len(pipes)) + nonSelectedPipes := make([]*Pipe, 0, len(pipes)) for _, pipe := range pipes { if highlight && equalHashes(pipe.fromSha, selectedCommitSha) { @@ -356,11 +379,13 @@ func renderPipeSet( cells[commitPos].setType(cType) - renderedCells := make([]string, len(cells)) - for i, cell := range cells { - renderedCells[i] = cell.render() + // using a string builder here for the sake of performance + writer := &strings.Builder{} + writer.Grow(len(cells) * 2) + for _, cell := range cells { + cell.render(writer) } - return strings.Join(renderedCells, "") + return writer.String() } func equalHashes(a, b string) bool { diff --git a/pkg/gui/presentation/graph/graph_test.go b/pkg/gui/presentation/graph/graph_test.go index 180f5f2b7..416409b9f 100644 --- a/pkg/gui/presentation/graph/graph_test.go +++ b/pkg/gui/presentation/graph/graph_test.go @@ -1,11 +1,14 @@ package graph import ( + "fmt" + "math/rand" "strings" "testing" "github.com/gookit/color" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gui/presentation/authors" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/stretchr/testify/assert" @@ -253,7 +256,7 @@ func TestRenderPipeSet(t *testing.T) { tests := []struct { name string - pipes []Pipe + pipes []*Pipe commit *models.Commit prevCommit *models.Commit expectedStr string @@ -261,7 +264,7 @@ func TestRenderPipeSet(t *testing.T) { }{ { name: "single cell", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: cyan}, {fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: green}, }, @@ -271,7 +274,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "single cell, selected", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "selected", kind: TERMINATES, style: cyan}, {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "c", kind: STARTS, style: green}, }, @@ -281,7 +284,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "terminating hook and starting hook, selected", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "selected", kind: TERMINATES, style: cyan}, {fromPos: 1, toPos: 0, fromSha: "c", toSha: "selected", kind: TERMINATES, style: yellow}, {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "d", kind: STARTS, style: green}, @@ -294,8 +297,8 @@ func TestRenderPipeSet(t *testing.T) { }, }, { - name: "terminating hook and starting hook, prioritise the starting one", - pipes: []Pipe{ + name: "terminating hook and starting hook, prioritise the terminating one", + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: red}, {fromPos: 1, toPos: 0, fromSha: "c", toSha: "b", kind: TERMINATES, style: magenta}, {fromPos: 0, toPos: 0, fromSha: "b", toSha: "d", kind: STARTS, style: green}, @@ -309,7 +312,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "starting and terminating pipe sharing some space", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 1, toPos: 1, fromSha: "b1", toSha: "b2", kind: CONTINUES, style: magenta}, @@ -324,7 +327,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "starting and terminating pipe sharing some space, with selection", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "selected", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 1, toPos: 1, fromSha: "b1", toSha: "b2", kind: CONTINUES, style: magenta}, @@ -339,7 +342,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "many terminating pipes", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 1, toPos: 0, fromSha: "b1", toSha: "a2", kind: TERMINATES, style: magenta}, @@ -353,7 +356,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "starting pipe passing through", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 0, toPos: 3, fromSha: "a2", toSha: "d3", kind: STARTS, style: yellow}, @@ -368,7 +371,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "starting and terminating path crossing continuing path", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: yellow}, @@ -383,7 +386,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "another clash of starting and terminating paths", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, {fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: yellow}, @@ -398,7 +401,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "commit whose previous commit is selected", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow}, }, @@ -410,7 +413,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "commit whose previous commit is selected and is a merge commit", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 1, toPos: 1, fromSha: "selected", toSha: "b3", kind: CONTINUES, style: red}, }, @@ -422,7 +425,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "commit whose previous commit is selected and is a merge commit, with continuing pipe inbetween", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 1, toPos: 1, fromSha: "z1", toSha: "z3", kind: CONTINUES, style: green}, {fromPos: 2, toPos: 2, fromSha: "selected", toSha: "b3", kind: CONTINUES, style: red}, @@ -435,7 +438,7 @@ func TestRenderPipeSet(t *testing.T) { }, { name: "when previous commit is selected, not a merge commit, and spawns a continuing pipe", - pipes: []Pipe{ + pipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red}, {fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: green}, {fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: green}, @@ -471,57 +474,27 @@ func TestRenderPipeSet(t *testing.T) { } } -func TestCellRender(t *testing.T) { - tests := []struct { - cell *Cell - expectedString string - }{ - { - cell: &Cell{ - up: true, - down: true, - cellType: CONNECTION, - style: style.FgCyan, - }, - expectedString: "\x1b[36m│\x1b[0m ", - }, - { - cell: &Cell{ - up: true, - down: true, - cellType: COMMIT, - style: style.FgCyan, - }, - expectedString: "\x1b[36m⎔\x1b[0m ", - }, - } - - for _, test := range tests { - assert.EqualValues(t, test.expectedString, test.cell.render()) - } -} - func TestGetNextPipes(t *testing.T) { tests := []struct { - prevPipes []Pipe + prevPipes []*Pipe commit *models.Commit - expected []Pipe + expected []*Pipe }{ { - prevPipes: []Pipe{ + prevPipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: STARTS, style: style.FgDefault}, }, commit: &models.Commit{ Sha: "b", Parents: []string{"c"}, }, - expected: []Pipe{ + expected: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: style.FgDefault}, {fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: style.FgDefault}, }, }, { - prevPipes: []Pipe{ + prevPipes: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: style.FgDefault}, {fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: style.FgDefault}, {fromPos: 0, toPos: 1, fromSha: "b", toSha: "d", kind: STARTS, style: style.FgDefault}, @@ -530,7 +503,7 @@ func TestGetNextPipes(t *testing.T) { Sha: "d", Parents: []string{"e"}, }, - expected: []Pipe{ + expected: []*Pipe{ {fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: CONTINUES, style: style.FgDefault}, {fromPos: 1, toPos: 1, fromSha: "b", toSha: "d", kind: TERMINATES, style: style.FgDefault}, {fromPos: 1, toPos: 1, fromSha: "d", toSha: "e", kind: STARTS, style: style.FgDefault}, @@ -551,3 +524,47 @@ func TestGetNextPipes(t *testing.T) { assert.EqualValues(t, test.expected, pipes) } } + +func BenchmarkRenderCommitGraph(b *testing.B) { + commits := generateCommits(50) + getStyle := func(commit *models.Commit) style.TextStyle { + return authors.AuthorStyle(commit.Author) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + RenderCommitGraph(commits, "selected", getStyle) + } +} + +func generateCommits(count int) []*models.Commit { + rand.Seed(1234) + pool := []*models.Commit{{Sha: "a", Author: "A"}} + commits := make([]*models.Commit, 0, count) + authorPool := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"} + for len(commits) < count { + currentCommitIdx := rand.Intn(len(pool)) + currentCommit := pool[currentCommitIdx] + pool = append(pool[0:currentCommitIdx], pool[currentCommitIdx+1:]...) + // I need to pick a random number of parents to add + parentCount := rand.Intn(2) + 1 + + for j := 0; j < parentCount; j++ { + reuseParent := rand.Intn(6) != 1 && j <= len(pool)-1 && j != 0 + var newParent *models.Commit + if reuseParent { + newParent = pool[j] + } else { + newParent = &models.Commit{ + Sha: fmt.Sprintf("%s%d", currentCommit.Sha, j), + Author: authorPool[rand.Intn(len(authorPool))], + } + pool = append(pool, newParent) + } + currentCommit.Parents = append(currentCommit.Parents, newParent.Sha) + } + + commits = append(commits, currentCommit) + } + + return commits +} diff --git a/pkg/gui/pty.go b/pkg/gui/pty.go index ff6892d73..56956c399 100644 --- a/pkg/gui/pty.go +++ b/pkg/gui/pty.go @@ -4,6 +4,7 @@ package gui import ( + "io" "os/exec" "strings" @@ -50,14 +51,19 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error manager := gui.getManager(view) - ptmx, err := pty.Start(cmd) - if err != nil { - return err + start := func() (*exec.Cmd, io.Reader) { + ptmx, err := pty.Start(cmd) + if err != nil { + gui.Log.Error(err) + } + + gui.State.Ptmx = ptmx + + return cmd, ptmx } - gui.State.Ptmx = ptmx onClose := func() { - ptmx.Close() + gui.State.Ptmx.Close() gui.State.Ptmx = nil } @@ -65,7 +71,7 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error return err } - if err := manager.NewTask(manager.NewCmdTask(ptmx, cmd, prefix, height+oy+10, onClose), cmdStr); err != nil { + if err := manager.NewTask(manager.NewCmdTask(start, prefix, height+oy+10, onClose), cmdStr); err != nil { return err } diff --git a/pkg/gui/style/style_test.go b/pkg/gui/style/style_test.go index ecf316705..360ad00e6 100644 --- a/pkg/gui/style/style_test.go +++ b/pkg/gui/style/style_test.go @@ -39,19 +39,19 @@ func TestMerge(t *testing.T) { { "no color", nil, - TextStyle{style: color.Style{}}, + TextStyle{Style: color.Style{}}, "foo", }, { "only fg color", []TextStyle{FgRed}, - TextStyle{fg: &Color{basic: &fgRed}, style: color.Style{fgRed}}, + TextStyle{fg: &Color{basic: &fgRed}, Style: color.Style{fgRed}}, "\x1b[31mfoo\x1b[0m", }, { "only bg color", []TextStyle{BgRed}, - TextStyle{bg: &Color{basic: &bgRed}, style: color.Style{bgRed}}, + TextStyle{bg: &Color{basic: &bgRed}, Style: color.Style{bgRed}}, "\x1b[41mfoo\x1b[0m", }, { @@ -60,7 +60,7 @@ func TestMerge(t *testing.T) { TextStyle{ fg: &Color{basic: &fgBlue}, bg: &Color{basic: &bgRed}, - style: color.Style{fgBlue, bgRed}, + Style: color.Style{fgBlue, bgRed}, }, "\x1b[34;41mfoo\x1b[0m", }, @@ -69,7 +69,7 @@ func TestMerge(t *testing.T) { []TextStyle{AttrBold}, TextStyle{ decoration: Decoration{bold: true}, - style: color.Style{color.OpBold}, + Style: color.Style{color.OpBold}, }, "\x1b[1mfoo\x1b[0m", }, @@ -81,7 +81,7 @@ func TestMerge(t *testing.T) { bold: true, underline: true, }, - style: color.Style{color.OpBold, color.OpUnderscore}, + Style: color.Style{color.OpBold, color.OpUnderscore}, }, "\x1b[1;4mfoo\x1b[0m", }, @@ -95,7 +95,7 @@ func TestMerge(t *testing.T) { bold: true, underline: true, }, - style: color.Style{fgBlue, bgRed, color.OpBold, color.OpUnderscore}, + Style: color.Style{fgBlue, bgRed, color.OpBold, color.OpUnderscore}, }, "\x1b[34;41;1;4mfoo\x1b[0m", }, @@ -104,7 +104,7 @@ func TestMerge(t *testing.T) { []TextStyle{New().SetFg(rgbPink)}, TextStyle{ fg: &rgbPink, - style: color.NewRGBStyle(rgbPinkLib).SetOpts(color.Opts{}), + Style: color.NewRGBStyle(rgbPinkLib).SetOpts(color.Opts{}), }, // '38;2' qualifies an RGB foreground color "\x1b[38;2;255;0;255mfoo\x1b[0m", @@ -115,7 +115,7 @@ func TestMerge(t *testing.T) { TextStyle{ fg: &rgbPink, bg: &rgbYellow, - style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{}), + Style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{}), }, // '48;2' qualifies an RGB background color "\x1b[38;2;255;0;255;48;2;255;255;0mfoo\x1b[0m", @@ -130,7 +130,7 @@ func TestMerge(t *testing.T) { bold: true, underline: true, }, - style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{color.OpBold, color.OpUnderscore}), + Style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{color.OpBold, color.OpUnderscore}), }, "\x1b[38;2;255;0;255;48;2;255;255;0;1;4mfoo\x1b[0m", }, @@ -140,7 +140,7 @@ func TestMerge(t *testing.T) { TextStyle{ fg: &rgbYellow, bg: &Color{basic: &bgRed}, - style: color.NewRGBStyle( + Style: color.NewRGBStyle( rgbYellowLib, fgRed.RGB(), // We need to use FG here, https://github.com/gookit/color/issues/39 ).SetOpts(color.Opts{}), diff --git a/pkg/gui/style/text_style.go b/pkg/gui/style/text_style.go index 4fbb7f1f4..30fa74035 100644 --- a/pkg/gui/style/text_style.go +++ b/pkg/gui/style/text_style.go @@ -30,7 +30,9 @@ type TextStyle struct { bg *Color decoration Decoration - style Sprinter + // making this public so that we can use a type switch to get to the underlying + // value so we can cache styles. This is very much a hack. + Style Sprinter } type Sprinter interface { @@ -40,16 +42,16 @@ type Sprinter interface { func New() TextStyle { s := TextStyle{} - s.style = s.deriveStyle() + s.Style = s.deriveStyle() return s } func (b TextStyle) Sprint(a ...interface{}) string { - return b.style.Sprint(a...) + return b.Style.Sprint(a...) } func (b TextStyle) Sprintf(format string, a ...interface{}) string { - return b.style.Sprintf(format, a...) + return b.Style.Sprintf(format, a...) } // note that our receiver here is not a pointer which means we're receiving a @@ -57,31 +59,31 @@ func (b TextStyle) Sprintf(format string, a ...interface{}) string { // TextStyle receiver without actually modifying the original. func (b TextStyle) SetBold() TextStyle { b.decoration.SetBold() - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } func (b TextStyle) SetUnderline() TextStyle { b.decoration.SetUnderline() - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } func (b TextStyle) SetReverse() TextStyle { b.decoration.SetReverse() - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } func (b TextStyle) SetBg(color Color) TextStyle { b.bg = &color - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } func (b TextStyle) SetFg(color Color) TextStyle { b.fg = &color - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } @@ -96,7 +98,7 @@ func (b TextStyle) MergeStyle(other TextStyle) TextStyle { b.bg = other.bg } - b.style = b.deriveStyle() + b.Style = b.deriveStyle() return b } diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go index 7e5089f6a..fad3e6284 100644 --- a/pkg/gui/tasks_adapter.go +++ b/pkg/gui/tasks_adapter.go @@ -1,6 +1,7 @@ package gui import ( + "io" "os/exec" "strings" @@ -20,18 +21,22 @@ func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error manager := gui.getManager(view) - r, err := cmd.StdoutPipe() - if err != nil { - return err - } - cmd.Stderr = cmd.Stdout + start := func() (*exec.Cmd, io.Reader) { + r, err := cmd.StdoutPipe() + if err != nil { + gui.Log.Warn(err) + } + cmd.Stderr = cmd.Stdout - if err := cmd.Start(); err != nil { - return err + if err := cmd.Start(); err != nil { + gui.Log.Warn(err) + } + + return cmd, r } - if err := manager.NewTask(manager.NewCmdTask(r, cmd, prefix, height+oy+10, nil), cmdStr); err != nil { - return err + if err := manager.NewTask(manager.NewCmdTask(start, prefix, height+oy+10, nil), cmdStr); err != nil { + gui.Log.Warn(err) } return nil @@ -90,7 +95,6 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager { view.Reset() }, func() { - // gui.g.Draw(view) // doing this causes an issue when there's a popup panel in front of the main view. gui.render() }, func() { diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 0ef2f2aa1..f853f2942 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -308,6 +308,11 @@ func (gui *Gui) renderDisplayStrings(v *gocui.View, displayStrings [][]string) { v.SetContent(list) } +func (gui *Gui) renderDisplayStringsAtPos(v *gocui.View, y int, displayStrings [][]string) { + list := utils.RenderDisplayStrings(displayStrings) + v.OverwriteLines(y, list) +} + func (gui *Gui) globalOptionsMap() map[string]string { keybindingConfig := gui.Config.GetUserConfig().Keybinding diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go index e394f18f7..62237f22f 100644 --- a/pkg/tasks/tasks.go +++ b/pkg/tasks/tasks.go @@ -14,6 +14,8 @@ import ( "github.com/sirupsen/logrus" ) +const THROTTLE_TIME = time.Millisecond * 30 + type Task struct { stop chan struct{} stopped bool @@ -38,6 +40,12 @@ type ViewBufferManager struct { beforeStart func() refreshView func() onEndOfInput func() + + // if the user flicks through a heap of items, with each one + // spawning a process to render something to the main view, + // it can slow things down quite a bit. In these situations we + // want to throttle the spawning of processes. + throttle bool } func (m *ViewBufferManager) GetTaskKey() string { @@ -69,18 +77,31 @@ func (m *ViewBufferManager) ReadLines(n int) { }) } -func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string, linesToRead int, onDone func()) func(chan struct{}) error { +func (m *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), prefix string, linesToRead int, onDone func()) func(chan struct{}) error { return func(stop chan struct{}) error { + if m.throttle { + m.Log.Info("throttling task") + time.Sleep(THROTTLE_TIME) + } + + select { + case <-stop: + return nil + default: + } + + startTime := time.Now() + + cmd, r := start() + go utils.Safe(func() { <-stop + m.throttle = time.Since(startTime) < THROTTLE_TIME if err := oscommands.Kill(cmd); err != nil { if !strings.Contains(err.Error(), "process already finished") { m.Log.Errorf("error when running cmd task: %v", err) } } - if onDone != nil { - onDone() - } }) loadingMutex := sync.Mutex{} diff --git a/pkg/utils/color.go b/pkg/utils/color.go index 18b04db1f..76e4c41c1 100644 --- a/pkg/utils/color.go +++ b/pkg/utils/color.go @@ -6,21 +6,24 @@ import ( ) var decoloriseCache = make(map[string]string) -var decoloriseMutex sync.Mutex +var decoloriseMutex sync.RWMutex // Decolorise strips a string of color func Decolorise(str string) string { - decoloriseMutex.Lock() - defer decoloriseMutex.Unlock() + decoloriseMutex.RLock() + val := decoloriseCache[str] + decoloriseMutex.RUnlock() - if decoloriseCache[str] != "" { - return decoloriseCache[str] + if val != "" { + return val } re := regexp.MustCompile(`\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]`) ret := re.ReplaceAllString(str, "") + decoloriseMutex.Lock() decoloriseCache[str] = ret + decoloriseMutex.Unlock() return ret } diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index c2306c194..86d1393bd 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -75,8 +75,6 @@ type GuiMutexes struct { tickingMutex sync.Mutex ViewsMutex sync.Mutex - - drawMutex sync.Mutex } type PlayMode int @@ -936,8 +934,6 @@ func (g *Gui) drawListFooter(v *View, fgColor, bgColor Attribute) error { // flush updates the gui, re-drawing frames and buffers. func (g *Gui) flush() error { - g.Mutexes.drawMutex.Lock() - defer g.Mutexes.drawMutex.Unlock() // pretty sure we don't need this, but keeping it here in case we get weird visual artifacts // g.clear(g.FgColor, g.BgColor) @@ -966,18 +962,6 @@ func (g *Gui) flush() error { return nil } -func (g *Gui) Draw(v *View) error { - g.Mutexes.drawMutex.Lock() - defer g.Mutexes.drawMutex.Unlock() - - if err := g.draw(v); err != nil { - return err - } - - Screen.Show() - return nil -} - // draw manages the cursor and calls the draw function of a view. func (g *Gui) draw(v *View) error { if g.suspended { diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index df9b910f2..9fc923800 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -879,11 +879,18 @@ func (v *View) draw() error { if v.Autoscroll && visibleViewLinesHeight > maxY { v.oy = visibleViewLinesHeight - maxY } + + if len(v.viewLines) == 0 { + return nil + } + + start := v.oy + if start > len(v.viewLines)-1 { + start = len(v.viewLines) - 1 + } + y := 0 - for i, vline := range v.viewLines { - if i < v.oy { - continue - } + for _, vline := range v.viewLines[start:] { if y >= maxY { break } @@ -1112,14 +1119,6 @@ func (v *View) SetHighlight(y int, on bool) error { return nil } -func lineWidth(line []cell) (n int) { - for i := range line { - n += runewidth.RuneWidth(line[i].chr) - } - - return -} - func lineWrap(line []cell, columns int) [][]cell { if columns == 0 { return [][]cell{line} @@ -1227,3 +1226,16 @@ func (v *View) ClearTextArea() { _ = v.SetOrigin(0, 0) _ = v.SetCursor(0, 0) } + +// only call this function if you don't care where v.wx and v.wy end up +func (v *View) OverwriteLines(y int, content string) { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + // break by newline, then for each line, write it, then add that erase command + v.wx = 0 + v.wy = y + + lines := strings.Replace(content, "\n", "\x1b[K\n", -1) + v.writeString(lines) +} diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index a8c13317d..8826f4143 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -430,8 +430,25 @@ func GetsockoptXucred(fd, level, opt int) (*Xucred, error) { return x, err } -func SysctlKinfoProcSlice(name string) ([]KinfoProc, error) { - mib, err := sysctlmib(name) +func SysctlKinfoProc(name string, args ...int) (*KinfoProc, error) { + mib, err := sysctlmib(name, args...) + if err != nil { + return nil, err + } + + var kinfo KinfoProc + n := uintptr(SizeofKinfoProc) + if err := sysctl(mib, (*byte)(unsafe.Pointer(&kinfo)), &n, nil, 0); err != nil { + return nil, err + } + if n != SizeofKinfoProc { + return nil, EIO + } + return &kinfo, nil +} + +func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { + mib, err := sysctlmib(name, args...) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 78d4b85ec..3bbc52751 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -116,6 +116,7 @@ const ( ARPHRD_LAPB = 0x204 ARPHRD_LOCALTLK = 0x305 ARPHRD_LOOPBACK = 0x304 + ARPHRD_MCTP = 0x122 ARPHRD_METRICOM = 0x17 ARPHRD_NETLINK = 0x338 ARPHRD_NETROM = 0x0 @@ -472,6 +473,7 @@ const ( DM_DEV_WAIT = 0xc138fd08 DM_DIR = "mapper" DM_GET_TARGET_VERSION = 0xc138fd11 + DM_IMA_MEASUREMENT_FLAG = 0x80000 DM_INACTIVE_PRESENT_FLAG = 0x40 DM_INTERNAL_SUSPEND_FLAG = 0x40000 DM_IOCTL = 0xfd @@ -716,6 +718,7 @@ const ( ETH_P_LOOPBACK = 0x9000 ETH_P_MACSEC = 0x88e5 ETH_P_MAP = 0xf9 + ETH_P_MCTP = 0xfa ETH_P_MOBITEX = 0x15 ETH_P_MPLS_MC = 0x8848 ETH_P_MPLS_UC = 0x8847 @@ -751,6 +754,21 @@ const ( ETH_P_WCCP = 0x883e ETH_P_X25 = 0x805 ETH_P_XDSA = 0xf8 + EV_ABS = 0x3 + EV_CNT = 0x20 + EV_FF = 0x15 + EV_FF_STATUS = 0x17 + EV_KEY = 0x1 + EV_LED = 0x11 + EV_MAX = 0x1f + EV_MSC = 0x4 + EV_PWR = 0x16 + EV_REL = 0x2 + EV_REP = 0x14 + EV_SND = 0x12 + EV_SW = 0x5 + EV_SYN = 0x0 + EV_VERSION = 0x10001 EXABYTE_ENABLE_NEST = 0xf0 EXT2_SUPER_MAGIC = 0xef53 EXT3_SUPER_MAGIC = 0xef53 @@ -789,9 +807,11 @@ const ( FAN_DELETE_SELF = 0x400 FAN_DENY = 0x2 FAN_ENABLE_AUDIT = 0x40 + FAN_EPIDFD = -0x2 FAN_EVENT_INFO_TYPE_DFID = 0x3 FAN_EVENT_INFO_TYPE_DFID_NAME = 0x2 FAN_EVENT_INFO_TYPE_FID = 0x1 + FAN_EVENT_INFO_TYPE_PIDFD = 0x4 FAN_EVENT_METADATA_LEN = 0x18 FAN_EVENT_ON_CHILD = 0x8000000 FAN_MARK_ADD = 0x1 @@ -811,6 +831,7 @@ const ( FAN_MOVE_SELF = 0x800 FAN_NOFD = -0x1 FAN_NONBLOCK = 0x2 + FAN_NOPIDFD = -0x1 FAN_ONDIR = 0x40000000 FAN_OPEN = 0x20 FAN_OPEN_EXEC = 0x1000 @@ -821,6 +842,7 @@ const ( FAN_REPORT_DIR_FID = 0x400 FAN_REPORT_FID = 0x200 FAN_REPORT_NAME = 0x800 + FAN_REPORT_PIDFD = 0x80 FAN_REPORT_TID = 0x100 FAN_UNLIMITED_MARKS = 0x20 FAN_UNLIMITED_QUEUE = 0x10 @@ -1997,6 +2019,7 @@ const ( PR_SPEC_ENABLE = 0x2 PR_SPEC_FORCE_DISABLE = 0x8 PR_SPEC_INDIRECT_BRANCH = 0x1 + PR_SPEC_L1D_FLUSH = 0x2 PR_SPEC_NOT_AFFECTED = 0x0 PR_SPEC_PRCTL = 0x1 PR_SPEC_STORE_BYPASS = 0x0 @@ -2432,12 +2455,15 @@ const ( SMART_WRITE_THRESHOLDS = 0xd7 SMB_SUPER_MAGIC = 0x517b SOCKFS_MAGIC = 0x534f434b + SOCK_BUF_LOCK_MASK = 0x3 SOCK_DCCP = 0x6 SOCK_IOC_TYPE = 0x89 SOCK_PACKET = 0xa SOCK_RAW = 0x3 + SOCK_RCVBUF_LOCK = 0x2 SOCK_RDM = 0x4 SOCK_SEQPACKET = 0x5 + SOCK_SNDBUF_LOCK = 0x1 SOL_AAL = 0x109 SOL_ALG = 0x117 SOL_ATM = 0x108 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index 697811a46..80c790840 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -293,6 +293,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index 7d8d93bfc..da55638a4 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -294,6 +294,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index f707d5089..c3da063c7 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -300,6 +300,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index 3a67a9c85..fd9f0d1db 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -290,6 +290,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index a7ccef56c..c358ada0d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -293,6 +293,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x20 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index f7b7cec91..1dc1ee16b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -293,6 +293,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x20 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index 4fcacf958..3ae187dd9 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -293,6 +293,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x20 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 6f6c223a2..39895f0dd 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -293,6 +293,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x20 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 59e522bcf..a98a45537 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -348,6 +348,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index d4264a0f7..0a8fbbffa 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -352,6 +352,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index 21cbec1dd..cb835a144 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -352,6 +352,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 9b05bf12f..73cf6554b 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -281,6 +281,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index bd82ace09..04b6dfaf5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -356,6 +356,7 @@ const ( SO_BPF_EXTENSIONS = 0x30 SO_BROADCAST = 0x6 SO_BSDCOMPAT = 0xe + SO_BUF_LOCK = 0x48 SO_BUSY_POLL = 0x2e SO_BUSY_POLL_BUDGET = 0x46 SO_CNX_ADVICE = 0x35 diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index 1f8bded56..8c87d979d 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -347,6 +347,7 @@ const ( SO_BPF_EXTENSIONS = 0x32 SO_BROADCAST = 0x20 SO_BSDCOMPAT = 0x400 + SO_BUF_LOCK = 0x51 SO_BUSY_POLL = 0x30 SO_BUSY_POLL_BUDGET = 0x49 SO_CNX_ADVICE = 0x37 diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index aa7ce85d1..31847d230 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -444,4 +444,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index b83032638..3503cbbde 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -366,4 +366,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index d75f65a0a..5ecd24bf6 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -7,6 +7,7 @@ package unix const ( + SYS_SYSCALL_MASK = 0 SYS_RESTART_SYSCALL = 0 SYS_EXIT = 1 SYS_FORK = 2 @@ -407,4 +408,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 8b02f09e9..7e5c94cc7 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -311,4 +311,5 @@ const ( SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 SYS_MEMFD_SECRET = 447 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 026695abb..e1e2a2bf5 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -428,4 +428,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 4444 SYS_LANDLOCK_ADD_RULE = 4445 SYS_LANDLOCK_RESTRICT_SELF = 4446 + SYS_PROCESS_MRELEASE = 4448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 7320ba958..7651915a3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -358,4 +358,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 5444 SYS_LANDLOCK_ADD_RULE = 5445 SYS_LANDLOCK_RESTRICT_SELF = 5446 + SYS_PROCESS_MRELEASE = 5448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index 45082dd67..a26a2c050 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -358,4 +358,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 5444 SYS_LANDLOCK_ADD_RULE = 5445 SYS_LANDLOCK_RESTRICT_SELF = 5446 + SYS_PROCESS_MRELEASE = 5448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index 570a857a5..fda9a6a99 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -428,4 +428,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 4444 SYS_LANDLOCK_ADD_RULE = 4445 SYS_LANDLOCK_RESTRICT_SELF = 4446 + SYS_PROCESS_MRELEASE = 4448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index 638498d62..e8496150d 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -435,4 +435,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 702beebfe..5ee0678a3 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -407,4 +407,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index bfc87ea44..29c0f9a39 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -407,4 +407,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index a390e147d..5c9a9a3b6 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -309,4 +309,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 3e791e6cd..913f50f98 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -372,4 +372,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 78802a5cf..0de03a722 100644 --- a/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -386,4 +386,5 @@ const ( SYS_LANDLOCK_CREATE_RULESET = 444 SYS_LANDLOCK_ADD_RULE = 445 SYS_LANDLOCK_RESTRICT_SELF = 446 + SYS_PROCESS_MRELEASE = 448 ) diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 249ecfcd4..620a6702f 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -3264,7 +3264,8 @@ const ( LWTUNNEL_ENCAP_BPF = 0x6 LWTUNNEL_ENCAP_SEG6_LOCAL = 0x7 LWTUNNEL_ENCAP_RPL = 0x8 - LWTUNNEL_ENCAP_MAX = 0x8 + LWTUNNEL_ENCAP_IOAM6 = 0x9 + LWTUNNEL_ENCAP_MAX = 0x9 MPLS_IPTUNNEL_UNSPEC = 0x0 MPLS_IPTUNNEL_DST = 0x1 @@ -3617,7 +3618,9 @@ const ( ETHTOOL_A_COALESCE_TX_USECS_HIGH = 0x15 ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH = 0x16 ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL = 0x17 - ETHTOOL_A_COALESCE_MAX = 0x17 + ETHTOOL_A_COALESCE_USE_CQE_MODE_TX = 0x18 + ETHTOOL_A_COALESCE_USE_CQE_MODE_RX = 0x19 + ETHTOOL_A_COALESCE_MAX = 0x19 ETHTOOL_A_PAUSE_UNSPEC = 0x0 ETHTOOL_A_PAUSE_HEADER = 0x1 ETHTOOL_A_PAUSE_AUTONEG = 0x2 diff --git a/vendor/modules.txt b/vendor/modules.txt index 3ceae5bb7..ee620b0f6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -161,7 +161,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/noder -# github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75 +# github.com/jesseduffield/gocui v0.3.1-0.20211102081536-e4eee64f4d13 ## explicit github.com/jesseduffield/gocui # github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e @@ -253,7 +253,7 @@ golang.org/x/crypto/ssh/knownhosts golang.org/x/net/context golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/sys v0.0.0-20211031064116-611d5d643895 +# golang.org/x/sys v0.0.0-20211102061401-a2f17f7b995c ## explicit golang.org/x/sys/cpu golang.org/x/sys/internal/unsafeheader