1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-08 23:56:15 +02:00

Rerender fewer views when their width changes

In d5b4f7bb3e and 58a83b0862 we introduced a combined mechanism for rerendering
views when either their width changes (needed for the branches view which
truncates long branch names), or the screen mode (needed for those views that
display more information in half or full screen mode, e.g. the commits view).

This was a bad idea, because it unnecessarily rerenders too many views when just
their width changes, which causes a noticable lag. This is a problem, for
example, when selecting a file in the files panel that has only unstaged
changes, and then going to one that has both staged and unstaged changes; this
splits the main view, causing the side panels to become a bit narrower, and
rerendering all those views took almost 500ms on my machine. Another similar
example is entering or leaving staging mode.

Fix this by being more specific about which views need rerendering under what
conditions; this improves the time it takes to rerender in the above scenarios
from 450-500s down to about 20ms.

This reintroduces the code that was removed in 58a83b0862, but in a slightly
different way.
This commit is contained in:
Stefan Haller 2024-06-22 18:00:31 +02:00
parent 8e1464f720
commit a67eda39a5
8 changed files with 48 additions and 11 deletions

View File

@ -23,7 +23,7 @@ type BaseContext struct {
focusable bool focusable bool
transient bool transient bool
hasControlledBounds bool hasControlledBounds bool
needsRerenderOnWidthChange bool needsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
needsRerenderOnHeightChange bool needsRerenderOnHeightChange bool
highlightOnFocus bool highlightOnFocus bool
@ -46,7 +46,7 @@ type NewBaseContextOpts struct {
Transient bool Transient bool
HasUncontrolledBounds bool // negating for the sake of making false the default HasUncontrolledBounds bool // negating for the sake of making false the default
HighlightOnFocus bool HighlightOnFocus bool
NeedsRerenderOnWidthChange bool NeedsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
NeedsRerenderOnHeightChange bool NeedsRerenderOnHeightChange bool
OnGetOptionsMap func() map[string]string OnGetOptionsMap func() map[string]string
@ -201,7 +201,7 @@ func (self *BaseContext) HasControlledBounds() bool {
return self.hasControlledBounds return self.hasControlledBounds
} }
func (self *BaseContext) NeedsRerenderOnWidthChange() bool { func (self *BaseContext) NeedsRerenderOnWidthChange() types.NeedsRerenderOnWidthChangeLevel {
return self.needsRerenderOnWidthChange return self.needsRerenderOnWidthChange
} }

View File

@ -46,7 +46,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
Key: LOCAL_BRANCHES_CONTEXT_KEY, Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true, NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -77,7 +77,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
Key: LOCAL_COMMITS_CONTEXT_KEY, Key: LOCAL_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true, NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
NeedsRerenderOnHeightChange: true, NeedsRerenderOnHeightChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{

View File

@ -48,7 +48,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
Key: REFLOG_COMMITS_CONTEXT_KEY, Key: REFLOG_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
NeedsRerenderOnWidthChange: true, NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{
list: viewModel, list: viewModel,

View File

@ -121,7 +121,7 @@ func NewSubCommitsContext(
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
Focusable: true, Focusable: true,
Transient: true, Transient: true,
NeedsRerenderOnWidthChange: true, NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
NeedsRerenderOnHeightChange: true, NeedsRerenderOnHeightChange: true,
})), })),
ListRenderer: ListRenderer{ ListRenderer: ListRenderer{

View File

@ -1,6 +1,7 @@
package controllers package controllers
import ( import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -16,7 +17,7 @@ func (self *ScreenModeActions) Next() error {
), ),
) )
return nil return self.rerenderViewsWithScreenModeDependentContent()
} }
func (self *ScreenModeActions) Prev() error { func (self *ScreenModeActions) Prev() error {
@ -27,9 +28,33 @@ func (self *ScreenModeActions) Prev() error {
), ),
) )
return self.rerenderViewsWithScreenModeDependentContent()
}
// these views need to be re-rendered when the screen mode changes. The commits view,
// for example, will show authorship information in half and full screen mode.
func (self *ScreenModeActions) rerenderViewsWithScreenModeDependentContent() error {
for _, context := range self.c.Context().AllList() {
if context.NeedsRerenderOnWidthChange() == types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES {
if err := self.rerenderView(context.GetView()); err != nil {
return err
}
}
}
return nil return nil
} }
func (self *ScreenModeActions) rerenderView(view *gocui.View) error {
context, ok := self.c.Helpers().View.ContextForView(view.Name())
if !ok {
self.c.Log.Errorf("no context found for view %s", view.Name())
return nil
}
return context.HandleRender()
}
func nextIntInCycle(sl []types.WindowMaximisation, current types.WindowMaximisation) types.WindowMaximisation { func nextIntInCycle(sl []types.WindowMaximisation, current types.WindowMaximisation) types.WindowMaximisation {
for i, val := range sl { for i, val := range sl {
if val == current { if val == current {

View File

@ -73,7 +73,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
mustRerender := false mustRerender := false
if context.NeedsRerenderOnWidthChange() { if context.NeedsRerenderOnWidthChange() == types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES {
// view.Width() returns the width -1 for some reason // view.Width() returns the width -1 for some reason
oldWidth := view.Width() + 1 oldWidth := view.Width() + 1
newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset

View File

@ -39,6 +39,18 @@ type ParentContexter interface {
GetParentContext() (Context, bool) GetParentContext() (Context, bool)
} }
type NeedsRerenderOnWidthChangeLevel int
const (
// view doesn't render differently when its width changes
NEEDS_RERENDER_ON_WIDTH_CHANGE_NONE NeedsRerenderOnWidthChangeLevel = iota
// view renders differently when its width changes. An example is a view
// that truncates long lines to the view width, e.g. the branches view
NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES
// view renders differently only when the screen mode changes
NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES
)
type IBaseContext interface { type IBaseContext interface {
HasKeybindings HasKeybindings
ParentContexter ParentContexter
@ -60,8 +72,8 @@ type IBaseContext interface {
// determined independently. // determined independently.
HasControlledBounds() bool HasControlledBounds() bool
// true if the view needs to be rerendered when its width changes // to what extent the view needs to be rerendered when its width changes
NeedsRerenderOnWidthChange() bool NeedsRerenderOnWidthChange() NeedsRerenderOnWidthChangeLevel
// true if the view needs to be rerendered when its height changes // true if the view needs to be rerendered when its height changes
NeedsRerenderOnHeightChange() bool NeedsRerenderOnHeightChange() bool