Skip to content

Commit 32207d2

Browse files
authored
Fix annoying UI stalls after refresh (e.g. background fetch) when the reflog is very long (#5135)
Draw only the visible lines of the reflogs panel, like we do for commits. Since the reflog can get very long, this saves some memory but especially some UI thread lag. In one of my repos I had over 11'000 reflog entries (I guess I should prune them more regularly...), and rendering them took ~600ms; since this happens on the UI thread, there was an annoying stall for half a second after every background fetch, for example (even if the reflog panel is not visible).
2 parents e1a8327 + 84be082 commit 32207d2

File tree

9 files changed

+46
-27
lines changed

9 files changed

+46
-27
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ require (
1818
github.com/integrii/flaggy v1.4.0
1919
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c
2020
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd
21-
github.com/jesseduffield/gocui v0.3.1-0.20251223143206-950739ccd44a
21+
github.com/jesseduffield/gocui v0.3.1-0.20251223144240-29fe12e8d53f
2222
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
2323
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
2424
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c h1:tC2Paiis
194194
github.com/jesseduffield/generics v0.0.0-20250517122708-b0b4a53a6f5c/go.mod h1:F2fEBk0ddf6ixrBrJjY7phfQ3hL9rXG0uSjvwYe50bE=
195195
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd h1:ViKj6qth8FgcIWizn9KiACWwPemWSymx62OPN0tHT+Q=
196196
github.com/jesseduffield/go-git/v5 v5.14.1-0.20250407170251-e1a013310ccd/go.mod h1:lRhCiBr6XjQrvcQVa+UYsy/99d3wMXn/a0nSQlhnhlA=
197-
github.com/jesseduffield/gocui v0.3.1-0.20251223143206-950739ccd44a h1:XRsyqrSljes4TlaPczQViIAA4xqdnB0fKEEpZdqWWTA=
198-
github.com/jesseduffield/gocui v0.3.1-0.20251223143206-950739ccd44a/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
197+
github.com/jesseduffield/gocui v0.3.1-0.20251223144240-29fe12e8d53f h1:5ArylWehV98WTxJM7AcSa53YNskEFpHHv+VePONQn58=
198+
github.com/jesseduffield/gocui v0.3.1-0.20251223144240-29fe12e8d53f/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
199199
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
200200
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
201201
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=

pkg/gui/context/list_context_trait.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ type ListContextTrait struct {
2121
// If this is true, we only render the visible lines of the list. Useful for lists that can
2222
// get very long, because it can save a lot of memory
2323
renderOnlyVisibleLines bool
24+
// If renderOnlyVisibleLines is true, needRerenderVisibleLines indicates whether we need to
25+
// rerender the visible lines e.g. because the scroll position changed
26+
needRerenderVisibleLines bool
2427
}
2528

2629
func (self *ListContextTrait) IsListContext() {}
@@ -50,8 +53,8 @@ func (self *ListContextTrait) FocusLine(scrollIntoView bool) {
5053
self.refreshViewport()
5154
} else if self.renderOnlyVisibleLines {
5255
newOrigin, _ := self.GetViewTrait().ViewPortYBounds()
53-
if oldOrigin != newOrigin {
54-
self.HandleRender()
56+
if oldOrigin != newOrigin || self.needRerenderVisibleLines {
57+
self.refreshViewport()
5558
}
5659
}
5760
return nil
@@ -102,10 +105,10 @@ func (self *ListContextTrait) HandleRender() {
102105
if self.getNonModelItems != nil {
103106
totalLength += len(self.getNonModelItems())
104107
}
105-
self.GetViewTrait().SetContentLineCount(totalLength)
106108
startIdx, length := self.GetViewTrait().ViewPortYBounds()
107109
content := self.renderLines(startIdx, startIdx+length)
108-
self.GetViewTrait().SetViewPortContentAndClearEverythingElse(content)
110+
self.GetViewTrait().SetViewPortContentAndClearEverythingElse(totalLength, content)
111+
self.needRerenderVisibleLines = false
109112
} else {
110113
content := self.renderLines(-1, -1)
111114
self.GetViewTrait().SetContent(content)
@@ -142,6 +145,10 @@ func (self *ListContextTrait) RenderOnlyVisibleLines() bool {
142145
return self.renderOnlyVisibleLines
143146
}
144147

148+
func (self *ListContextTrait) SetNeedRerenderVisibleLines() {
149+
self.needRerenderVisibleLines = true
150+
}
151+
145152
func (self *ListContextTrait) TotalContentHeight() int {
146153
result := self.list.Len()
147154
if self.getNonModelItems != nil {

pkg/gui/context/reflog_commits_context.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
2626
},
2727
)
2828

29-
getDisplayStrings := func(_ int, _ int) [][]string {
29+
getDisplayStrings := func(startIdx int, endIdx int) [][]string {
30+
commits := viewModel.GetItems()
31+
if startIdx >= len(commits) {
32+
return nil
33+
}
34+
3035
return presentation.GetReflogCommitListDisplayStrings(
31-
viewModel.GetItems(),
36+
commits[startIdx:endIdx],
3237
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
3338
c.Modes().CherryPicking.SelectedHashSet(),
3439
c.Modes().Diffing.Ref,
@@ -43,18 +48,20 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
4348
FilteredListViewModel: viewModel,
4449
ListContextTrait: &ListContextTrait{
4550
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
46-
View: c.Views().ReflogCommits,
47-
WindowName: "commits",
48-
Key: REFLOG_COMMITS_CONTEXT_KEY,
49-
Kind: types.SIDE_CONTEXT,
50-
Focusable: true,
51-
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
51+
View: c.Views().ReflogCommits,
52+
WindowName: "commits",
53+
Key: REFLOG_COMMITS_CONTEXT_KEY,
54+
Kind: types.SIDE_CONTEXT,
55+
Focusable: true,
56+
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
57+
NeedsRerenderOnHeightChange: true,
5258
})),
5359
ListRenderer: ListRenderer{
5460
list: viewModel,
5561
getDisplayStrings: getDisplayStrings,
5662
},
57-
c: c,
63+
c: c,
64+
renderOnlyVisibleLines: true,
5865
},
5966
}
6067
}

pkg/gui/context/view_trait.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,9 @@ func (self *ViewTrait) SetViewPortContent(content string) {
3434
self.view.OverwriteLines(y, content)
3535
}
3636

37-
func (self *ViewTrait) SetViewPortContentAndClearEverythingElse(content string) {
37+
func (self *ViewTrait) SetViewPortContentAndClearEverythingElse(lineCount int, content string) {
3838
_, y := self.view.Origin()
39-
self.view.OverwriteLinesAndClearEverythingElse(y, content)
40-
}
41-
42-
func (self *ViewTrait) SetContentLineCount(lineCount int) {
43-
self.view.SetContentLineCount(lineCount)
39+
self.view.OverwriteLinesAndClearEverythingElse(lineCount, y, content)
4440
}
4541

4642
func (self *ViewTrait) SetContent(content string) {

pkg/gui/controllers/list_controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ func (self *ListController) handlePageChange(delta int) error {
178178
}
179179
}
180180

181+
// Since we already scrolled the view above, the normal mechanism that
182+
// ListContextTrait.FocusLine uses for deciding whether rerendering is needed won't work. It is
183+
// based on checking whether the origin was changed by the call to FocusPoint in that function,
184+
// but since we scrolled the view directly above, the origin has already been updated. So we
185+
// must tell it explicitly to rerender.
186+
self.context.SetNeedRerenderVisibleLines()
187+
181188
// Since we are maintaining the scroll position ourselves above, there's no point in passing
182189
// ScrollSelectionIntoView=true here.
183190
self.context.HandleFocus(types.OnFocusOpts{})

pkg/gui/types/context.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ type IListContext interface {
182182
IsListContext() // used for type switch
183183
RangeSelectEnabled() bool
184184
RenderOnlyVisibleLines() bool
185+
SetNeedRerenderVisibleLines()
185186

186187
IndexForGotoBottom() int
187188
}
@@ -205,8 +206,7 @@ type IViewTrait interface {
205206
SetRangeSelectStart(yIdx int)
206207
CancelRangeSelect()
207208
SetViewPortContent(content string)
208-
SetViewPortContentAndClearEverythingElse(content string)
209-
SetContentLineCount(lineCount int)
209+
SetViewPortContentAndClearEverythingElse(lineCount int, content string)
210210
SetContent(content string)
211211
SetFooter(value string)
212212
SetOriginX(value int)

vendor/github.com/jesseduffield/gocui/view.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/modules.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
221221
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
222222
github.com/jesseduffield/go-git/v5/utils/sync
223223
github.com/jesseduffield/go-git/v5/utils/trace
224-
# github.com/jesseduffield/gocui v0.3.1-0.20251223143206-950739ccd44a
224+
# github.com/jesseduffield/gocui v0.3.1-0.20251223144240-29fe12e8d53f
225225
## explicit; go 1.12
226226
github.com/jesseduffield/gocui
227227
# github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5

0 commit comments

Comments
 (0)