mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-28 18:24:17 -05:00
When scrolling a lazy-loaded view (a diff in the main view, the command log, etc.), we top up the view's line buffer by reading more lines from the still-running task. This was driven by asking the task to read a fixed number of *additional* lines on every scroll event, which had two problems: - It was decoupled from the scroll position. Scrolling down, back up, and down again re-read lines that had already been read, so the buffer crept towards the end of the input regardless of where the user actually scrolled. - A single wheel notch only bought a single notch worth of runway, so fast scrolling constantly outran the reader and had to wait for the next read (and re-render) on every notch. Make ReadLines take an absolute target total instead of a delta: the task tracks how many lines it has read and only reads the shortfall, so requests are idempotent. Callers now ask to fill the viewport at the current scroll position plus a few screenfuls of read-ahead, which gives scrolling enough runway to stay smooth. The four call sites all wanted the same "fill this view" computation, so consolidate them into a single ReadLinesToFillView helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
212 lines
5.5 KiB
Go
212 lines
5.5 KiB
Go
package gui
|
|
|
|
import (
|
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/jesseduffield/lazygit/pkg/gocui"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/jesseduffield/lazygit/pkg/tasks"
|
|
)
|
|
|
|
// hacking this by including the gui struct for now until we split more things out
|
|
type guiCommon struct {
|
|
gui *Gui
|
|
types.IPopupHandler
|
|
}
|
|
|
|
var _ types.IGuiCommon = &guiCommon{}
|
|
|
|
func (self *guiCommon) LogAction(msg string) {
|
|
self.gui.LogAction(msg)
|
|
}
|
|
|
|
func (self *guiCommon) LogCommand(cmdStr string, isCommandLine bool) {
|
|
self.gui.LogCommand(cmdStr, isCommandLine)
|
|
}
|
|
|
|
func (self *guiCommon) Refresh(opts types.RefreshOptions) {
|
|
self.gui.helpers.Refresh.Refresh(opts)
|
|
}
|
|
|
|
func (self *guiCommon) RefreshFromWorker(opts types.RefreshOptions) {
|
|
self.gui.helpers.Refresh.RefreshFromWorker(opts)
|
|
}
|
|
|
|
func (self *guiCommon) PostRefreshUpdate(context types.Context) {
|
|
self.gui.postRefreshUpdate(context)
|
|
}
|
|
|
|
func (self *guiCommon) RunSubprocessAndRefresh(cmdObj *oscommands.CmdObj) error {
|
|
return self.gui.runSubprocessWithSuspenseAndRefresh(cmdObj)
|
|
}
|
|
|
|
func (self *guiCommon) RunSubprocess(cmdObj *oscommands.CmdObj) (bool, error) {
|
|
return self.gui.runSubprocessWithSuspense(cmdObj)
|
|
}
|
|
|
|
func (self *guiCommon) Suspend() error {
|
|
return self.gui.suspend()
|
|
}
|
|
|
|
func (self *guiCommon) Resume() error {
|
|
return self.gui.resume()
|
|
}
|
|
|
|
func (self *guiCommon) PauseBackgroundRefreshes(pause bool) {
|
|
self.gui.BackgroundRoutineMgr.PauseBackgroundRefreshes(pause)
|
|
}
|
|
|
|
func (self *guiCommon) Context() types.IContextMgr {
|
|
return self.gui.State.ContextMgr
|
|
}
|
|
|
|
func (self *guiCommon) ContextForKey(key types.ContextKey) types.Context {
|
|
return self.gui.State.ContextMgr.ContextForKey(key)
|
|
}
|
|
|
|
func (self *guiCommon) GetAppState() *config.AppState {
|
|
return self.gui.Config.GetAppState()
|
|
}
|
|
|
|
func (self *guiCommon) SaveAppState() error {
|
|
return self.gui.Config.SaveAppState()
|
|
}
|
|
|
|
func (self *guiCommon) SaveAppStateAndLogError() {
|
|
if err := self.gui.Config.SaveAppState(); err != nil {
|
|
self.gui.Log.Errorf("error when saving app state: %v", err)
|
|
}
|
|
}
|
|
|
|
func (self *guiCommon) GetConfig() config.AppConfigurer {
|
|
return self.gui.Config
|
|
}
|
|
|
|
func (self *guiCommon) ResetViewOrigin(view *gocui.View) {
|
|
self.gui.resetViewOrigin(view)
|
|
}
|
|
|
|
func (self *guiCommon) SetViewContent(view *gocui.View, content string) {
|
|
self.gui.setViewContent(view, content)
|
|
}
|
|
|
|
func (self *guiCommon) Render() {
|
|
self.gui.render()
|
|
}
|
|
|
|
func (self *guiCommon) Views() types.Views {
|
|
return self.gui.Views
|
|
}
|
|
|
|
func (self *guiCommon) Git() *commands.GitCommand {
|
|
return self.gui.git
|
|
}
|
|
|
|
func (self *guiCommon) OS() *oscommands.OSCommand {
|
|
return self.gui.os
|
|
}
|
|
|
|
func (self *guiCommon) Modes() *types.Modes {
|
|
return self.gui.State.Modes
|
|
}
|
|
|
|
func (self *guiCommon) Model() *types.Model {
|
|
return self.gui.State.Model
|
|
}
|
|
|
|
func (self *guiCommon) Mutexes() *types.Mutexes {
|
|
return &self.gui.Mutexes
|
|
}
|
|
|
|
func (self *guiCommon) GocuiGui() *gocui.Gui {
|
|
return self.gui.g
|
|
}
|
|
|
|
func (self *guiCommon) OnUIThread(f func() error) {
|
|
self.gui.onUIThread(f)
|
|
}
|
|
|
|
func (self *guiCommon) OnUIThreadBackground(f func() error) {
|
|
self.gui.onUIThreadBackground(f)
|
|
}
|
|
|
|
func (self *guiCommon) OnUIThreadContentOnly(f func() error) {
|
|
self.gui.onUIThreadContentOnly(f)
|
|
}
|
|
|
|
func (self *guiCommon) OnUIThreadContentOnlyBackground(f func() error) {
|
|
self.gui.onUIThreadContentOnlyBackground(f)
|
|
}
|
|
|
|
func (self *guiCommon) OnWorker(f func(gocui.Task) error) {
|
|
self.gui.onWorker(f)
|
|
}
|
|
|
|
func (self *guiCommon) OnWorkerBackground(f func(gocui.Task) error) {
|
|
self.gui.onWorkerBackground(f)
|
|
}
|
|
|
|
func (self *guiCommon) RenderToMainViews(opts types.RefreshMainOpts) {
|
|
self.gui.refreshMainViews(opts)
|
|
}
|
|
|
|
func (self *guiCommon) MainViewPairs() types.MainViewPairs {
|
|
return types.MainViewPairs{
|
|
Normal: self.gui.normalMainContextPair(),
|
|
Staging: self.gui.stagingMainContextPair(),
|
|
PatchBuilding: self.gui.patchBuildingMainContextPair(),
|
|
MergeConflicts: self.gui.mergingMainContextPair(),
|
|
}
|
|
}
|
|
|
|
func (self *guiCommon) GetViewBufferManagerForView(view *gocui.View) *tasks.ViewBufferManager {
|
|
return self.gui.getViewBufferManagerForView(view)
|
|
}
|
|
|
|
func (self *guiCommon) ReadLinesToFillView(view *gocui.View) {
|
|
self.gui.readLinesToFillView(view)
|
|
}
|
|
|
|
func (self *guiCommon) State() types.IStateAccessor {
|
|
return self.gui.stateAccessor
|
|
}
|
|
|
|
func (self *guiCommon) KeybindingsOpts() types.KeybindingsOpts {
|
|
return self.gui.keybindingOpts()
|
|
}
|
|
|
|
func (self *guiCommon) CallKeybindingHandler(binding *types.Binding) error {
|
|
return self.gui.callKeybindingHandler(binding)
|
|
}
|
|
|
|
func (self *guiCommon) ResetKeybindings() error {
|
|
return self.gui.resetKeybindings()
|
|
}
|
|
|
|
func (self *guiCommon) IsAnyModeActive() bool {
|
|
return self.gui.helpers.Mode.IsAnyModeActive()
|
|
}
|
|
|
|
func (self *guiCommon) GetInitialKeybindingsWithCustomCommands() ([]*types.Binding, []*gocui.ViewMouseBinding) {
|
|
return self.gui.GetInitialKeybindingsWithCustomCommands()
|
|
}
|
|
|
|
func (self *guiCommon) AfterLayout(f func() error) {
|
|
self.gui.afterLayout(f)
|
|
}
|
|
|
|
func (self *guiCommon) RunningIntegrationTest() bool {
|
|
return self.gui.integrationTest != nil
|
|
}
|
|
|
|
func (self *guiCommon) InDemo() bool {
|
|
return self.gui.integrationTest != nil && self.gui.integrationTest.IsDemo()
|
|
}
|
|
|
|
func (self *guiCommon) WithInlineStatus(item types.HasUrn, operation types.ItemOperation, contextKey types.ContextKey, f func(gocui.Task) error) error {
|
|
self.gui.helpers.InlineStatus.WithInlineStatus(helpers.InlineStatusOpts{Item: item, Operation: operation, ContextKey: contextKey}, f)
|
|
return nil
|
|
}
|