Skip to content

Commit

Permalink
Rerender fewer views when their width changes
Browse files Browse the repository at this point in the history
In d5b4f7b and 58a83b0 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 58a83b0, but in a slightly
different way.
  • Loading branch information
stefanhaller committed Jun 23, 2024
1 parent 8e1464f commit a67eda3
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 11 deletions.
6 changes: 3 additions & 3 deletions pkg/gui/context/base_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type BaseContext struct {
focusable bool
transient bool
hasControlledBounds bool
needsRerenderOnWidthChange bool
needsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
needsRerenderOnHeightChange bool
highlightOnFocus bool

Expand All @@ -46,7 +46,7 @@ type NewBaseContextOpts struct {
Transient bool
HasUncontrolledBounds bool // negating for the sake of making false the default
HighlightOnFocus bool
NeedsRerenderOnWidthChange bool
NeedsRerenderOnWidthChange types.NeedsRerenderOnWidthChangeLevel
NeedsRerenderOnHeightChange bool

OnGetOptionsMap func() map[string]string
Expand Down Expand Up @@ -201,7 +201,7 @@ func (self *BaseContext) HasControlledBounds() bool {
return self.hasControlledBounds
}

func (self *BaseContext) NeedsRerenderOnWidthChange() bool {
func (self *BaseContext) NeedsRerenderOnWidthChange() types.NeedsRerenderOnWidthChangeLevel {
return self.needsRerenderOnWidthChange
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/context/branches_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext {
Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
NeedsRerenderOnWidthChange: true,
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_WIDTH_CHANGES,
})),
ListRenderer: ListRenderer{
list: viewModel,
Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/context/local_commits_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
Key: LOCAL_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
NeedsRerenderOnWidthChange: true,
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
NeedsRerenderOnHeightChange: true,
})),
ListRenderer: ListRenderer{
Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/context/reflog_commits_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
Key: REFLOG_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
NeedsRerenderOnWidthChange: true,
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
})),
ListRenderer: ListRenderer{
list: viewModel,
Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/context/sub_commits_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func NewSubCommitsContext(
Kind: types.SIDE_CONTEXT,
Focusable: true,
Transient: true,
NeedsRerenderOnWidthChange: true,
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
NeedsRerenderOnHeightChange: true,
})),
ListRenderer: ListRenderer{
Expand Down
27 changes: 26 additions & 1 deletion pkg/gui/controllers/screen_mode_actions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controllers

import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)

Expand All @@ -16,7 +17,7 @@ func (self *ScreenModeActions) Next() error {
),
)

return nil
return self.rerenderViewsWithScreenModeDependentContent()
}

func (self *ScreenModeActions) Prev() error {
Expand All @@ -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
}

func (self *ScreenModeActions) rerenderView(view *gocui.View) error {
context, ok := self.c.Helpers().View.ContextForView(view.Name())
if !ok {
self.c.Log.Errorf("no context found for view %s", view.Name())
return nil
}

return context.HandleRender()
}

func nextIntInCycle(sl []types.WindowMaximisation, current types.WindowMaximisation) types.WindowMaximisation {
for i, val := range sl {
if val == current {
Expand Down
2 changes: 1 addition & 1 deletion pkg/gui/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}

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
oldWidth := view.Width() + 1
newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset
Expand Down
16 changes: 14 additions & 2 deletions pkg/gui/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ type ParentContexter interface {
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 {
HasKeybindings
ParentContexter
Expand All @@ -60,8 +72,8 @@ type IBaseContext interface {
// determined independently.
HasControlledBounds() bool

// true if the view needs to be rerendered when its width changes
NeedsRerenderOnWidthChange() bool
// to what extent the view needs to be rerendered when its width changes
NeedsRerenderOnWidthChange() NeedsRerenderOnWidthChangeLevel

// true if the view needs to be rerendered when its height changes
NeedsRerenderOnHeightChange() bool
Expand Down

0 comments on commit a67eda3

Please sign in to comment.