1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-03 13:21:56 +02:00

stop resetting scroll all the time

This commit is contained in:
Jesse Duffield 2021-10-17 19:01:02 +11:00
parent e7c27b6f4a
commit d672b7342f
7 changed files with 131 additions and 142 deletions

View File

@ -57,13 +57,6 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
}
if !alreadySelected {
// TODO: pull into update task interface
if err := gui.resetOrigin(gui.Views.Main); err != nil {
return err
}
if err := gui.resetOrigin(gui.Views.Secondary); err != nil {
return err
}
gui.takeOverMergeConflictScrolling()
}

View File

@ -11,8 +11,7 @@ type ListContext struct {
SelectedItem func() (ListItem, bool)
GetPanelState func() IListPanelState
Gui *Gui
ResetMainViewOriginOnFocus bool
Gui *Gui
*BasicContext
}
@ -79,15 +78,6 @@ func (lc *ListContext) HandleFocus() error {
view.FocusPoint(0, lc.GetPanelState().GetSelectedLineIdx())
if lc.ResetMainViewOriginOnFocus {
if err := lc.Gui.resetOrigin(lc.Gui.Views.Main); err != nil {
return err
}
if err := lc.Gui.resetOrigin(lc.Gui.Views.Secondary); err != nil {
return err
}
}
if lc.Gui.State.Modes.Diffing.Active() {
return lc.Gui.renderDiff()
}

View File

@ -14,12 +14,11 @@ func (gui *Gui) menuListContext() *ListContext {
Kind: PERSISTENT_POPUP,
OnGetOptionsMap: gui.getMenuOptions,
},
GetItemsLength: func() int { return gui.Views.Menu.LinesHeight() },
GetPanelState: func() IListPanelState { return gui.State.Panels.Menu },
OnFocus: gui.handleMenuSelect,
OnClickSelectedItem: gui.onMenuPress,
Gui: gui,
ResetMainViewOriginOnFocus: false,
GetItemsLength: func() int { return gui.Views.Menu.LinesHeight() },
GetPanelState: func() IListPanelState { return gui.State.Panels.Menu },
OnFocus: gui.handleMenuSelect,
OnClickSelectedItem: gui.onMenuPress,
Gui: gui,
// no GetDisplayStrings field because we do a custom render on menu creation
}
@ -33,12 +32,11 @@ func (gui *Gui) filesListContext() *ListContext {
Key: FILES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return gui.State.FileManager.GetItemsLength() },
GetPanelState: func() IListPanelState { return gui.State.Panels.Files },
OnFocus: gui.focusAndSelectFile,
OnClickSelectedItem: gui.handleFilePress,
Gui: gui,
ResetMainViewOriginOnFocus: false,
GetItemsLength: func() int { return gui.State.FileManager.GetItemsLength() },
GetPanelState: func() IListPanelState { return gui.State.Panels.Files },
OnFocus: gui.focusAndSelectFile,
OnClickSelectedItem: gui.handleFilePress,
Gui: gui,
GetDisplayStrings: func() [][]string {
lines := gui.State.FileManager.Render(gui.State.Modes.Diffing.Ref, gui.State.Submodules)
mappedLines := make([][]string, len(lines))
@ -63,11 +61,10 @@ func (gui *Gui) branchesListContext() *ListContext {
Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.Branches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Branches },
OnFocus: gui.handleBranchSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.Branches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Branches },
OnFocus: gui.handleBranchSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref)
},
@ -86,12 +83,11 @@ func (gui *Gui) remotesListContext() *ListContext {
Key: REMOTES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.Remotes) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Remotes },
OnFocus: gui.handleRemoteSelect,
OnClickSelectedItem: gui.handleRemoteEnter,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.Remotes) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Remotes },
OnFocus: gui.handleRemoteSelect,
OnClickSelectedItem: gui.handleRemoteEnter,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetRemoteListDisplayStrings(gui.State.Remotes, gui.State.Modes.Diffing.Ref)
},
@ -110,11 +106,10 @@ func (gui *Gui) remoteBranchesListContext() *ListContext {
Key: REMOTE_BRANCHES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches },
OnFocus: gui.handleRemoteBranchSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches },
OnFocus: gui.handleRemoteBranchSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches, gui.State.Modes.Diffing.Ref)
},
@ -133,11 +128,10 @@ func (gui *Gui) tagsListContext() *ListContext {
Key: TAGS_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.Tags) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Tags },
OnFocus: gui.handleTagSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.Tags) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Tags },
OnFocus: gui.handleTagSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetTagListDisplayStrings(gui.State.Tags, gui.State.Modes.Diffing.Ref)
},
@ -157,12 +151,11 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
Key: BRANCH_COMMITS_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.Commits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Commits },
OnFocus: gui.handleCommitSelect,
OnClickSelectedItem: gui.handleViewCommitFiles,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.Commits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Commits },
OnFocus: gui.handleCommitSelect,
OnClickSelectedItem: gui.handleViewCommitFiles,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetCommitListDisplayStrings(
gui.State.Commits,
@ -188,11 +181,10 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
Key: REFLOG_COMMITS_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits },
OnFocus: gui.handleReflogCommitSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits },
OnFocus: gui.handleReflogCommitSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetReflogCommitListDisplayStrings(
gui.State.FilteredReflogCommits,
@ -218,11 +210,10 @@ func (gui *Gui) subCommitsListContext() *ListContext {
Key: SUB_COMMITS_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.SubCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits },
OnFocus: gui.handleSubCommitSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.SubCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits },
OnFocus: gui.handleSubCommitSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetCommitListDisplayStrings(
gui.State.SubCommits,
@ -247,11 +238,10 @@ func (gui *Gui) stashListContext() *ListContext {
Key: STASH_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.StashEntries) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Stash },
OnFocus: gui.handleStashEntrySelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.StashEntries) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Stash },
OnFocus: gui.handleStashEntrySelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries, gui.State.Modes.Diffing.Ref)
},
@ -270,11 +260,10 @@ func (gui *Gui) commitFilesListContext() *ListContext {
Key: COMMIT_FILES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() },
GetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles },
OnFocus: gui.handleCommitFileSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() },
GetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles },
OnFocus: gui.handleCommitFileSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
if gui.State.CommitFileManager.GetItemsLength() == 0 {
return [][]string{{style.FgRed.Sprint("(none)")}}
@ -303,11 +292,10 @@ func (gui *Gui) submodulesListContext() *ListContext {
Key: SUBMODULES_CONTEXT_KEY,
Kind: SIDE_CONTEXT,
},
GetItemsLength: func() int { return len(gui.State.Submodules) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
OnFocus: gui.handleSubmoduleSelect,
Gui: gui,
ResetMainViewOriginOnFocus: true,
GetItemsLength: func() int { return len(gui.State.Submodules) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
OnFocus: gui.handleSubmoduleSelect,
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetSubmoduleListDisplayStrings(gui.State.Submodules)
},
@ -326,11 +314,10 @@ func (gui *Gui) suggestionsListContext() *ListContext {
Key: SUGGESTIONS_CONTEXT_KEY,
Kind: PERSISTENT_POPUP,
},
GetItemsLength: func() int { return len(gui.State.Suggestions) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions },
OnFocus: func() error { return nil },
Gui: gui,
ResetMainViewOriginOnFocus: false,
GetItemsLength: func() int { return len(gui.State.Suggestions) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions },
OnFocus: func() error { return nil },
Gui: gui,
GetDisplayStrings: func() [][]string {
return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions)
},

View File

@ -29,7 +29,6 @@ type TaskKind int
const (
RENDER_STRING TaskKind = iota
RENDER_STRING_WITHOUT_SCROLL
RUN_FUNCTION
RUN_COMMAND
RUN_PTY
)
@ -97,19 +96,6 @@ func NewRunPtyTask(cmd *exec.Cmd) *runPtyTask {
// return &runPtyTask{cmd: cmd, prefix: prefix}
// }
type runFunctionTask struct {
f func(chan struct{}) error
}
func (t *runFunctionTask) GetKind() TaskKind {
return RUN_FUNCTION
}
// currently unused
// func (gui *Gui) createRunFunctionTask(f func(chan struct{}) error) *runFunctionTask {
// return &runFunctionTask{f: f}
// }
func (gui *Gui) runTaskForView(view *gocui.View, task updateTask) error {
switch task.GetKind() {
case RENDER_STRING:
@ -120,10 +106,6 @@ func (gui *Gui) runTaskForView(view *gocui.View, task updateTask) error {
specificTask := task.(*renderStringWithoutScrollTask)
return gui.newStringTaskWithoutScroll(view, specificTask.str)
case RUN_FUNCTION:
specificTask := task.(*runFunctionTask)
return gui.newTask(view, specificTask.f)
case RUN_COMMAND:
specificTask := task.(*runCommandTask)
return gui.newCmdTask(view, specificTask.cmd, specificTask.prefix)

View File

@ -5,6 +5,7 @@ package gui
import (
"os/exec"
"strings"
"github.com/creack/pty"
"github.com/jesseduffield/gocui"
@ -40,6 +41,8 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
return gui.newCmdTask(view, cmd, prefix)
}
cmdStr := strings.Join(cmd.Args, " ")
cmd.Env = append(cmd.Env, "GIT_PAGER="+pager)
_, height := view.Size()
@ -62,7 +65,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)); err != nil {
if err := manager.NewTask(manager.NewCmdTask(ptmx, cmd, prefix, height+oy+10, onClose), cmdStr); err != nil {
return err
}

View File

@ -9,9 +9,10 @@ import (
)
func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error {
cmdStr := strings.Join(cmd.Args, " ")
gui.Log.WithField(
"command",
strings.Join(cmd.Args, " "),
cmdStr,
).Debug("RunCommand")
_, height := view.Size()
@ -29,17 +30,7 @@ func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
return err
}
if err := manager.NewTask(manager.NewCmdTask(r, cmd, prefix, height+oy+10, nil)); err != nil {
return err
}
return nil
}
func (gui *Gui) newTask(view *gocui.View, f func(chan struct{}) error) error {
manager := gui.getManager(view)
if err := manager.NewTask(f); err != nil {
if err := manager.NewTask(manager.NewCmdTask(r, cmd, prefix, height+oy+10, nil), cmdStr); err != nil {
return err
}
@ -47,6 +38,16 @@ func (gui *Gui) newTask(view *gocui.View, f func(chan struct{}) error) error {
}
func (gui *Gui) newStringTask(view *gocui.View, str string) error {
// using str so that if rendering the exact same thing we don't reset the origin
return gui.newStringTaskWithKey(view, str, str)
}
func (gui *Gui) newStringTaskWithoutScroll(view *gocui.View, str string) error {
// using empty key so that on subsequent calls we won't reset the view's origin
return gui.newStringTaskWithKey(view, str, "")
}
func (gui *Gui) newStringTaskWithKey(view *gocui.View, str string, key string) error {
manager := gui.getManager(view)
f := func(stop chan struct{}) error {
@ -54,22 +55,7 @@ func (gui *Gui) newStringTask(view *gocui.View, str string) error {
return nil
}
if err := manager.NewTask(f); err != nil {
return err
}
return nil
}
func (gui *Gui) newStringTaskWithoutScroll(view *gocui.View, str string) error {
manager := gui.getManager(view)
f := func(stop chan struct{}) error {
gui.setViewContent(view, str)
return nil
}
if err := manager.NewTask(f); err != nil {
if err := manager.NewTask(f, key); err != nil {
return err
}
@ -97,8 +83,29 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
})
},
func() {
// Need to check if the content of the view is well past the origin.
// It would be better to use .ViewLinesHeight here (given it considers
// wrapping) but when this function is called they haven't been written to yet.
linesHeight := view.LinesHeight()
_, height := view.Size()
_, originY := view.Origin()
if linesHeight < originY {
newOriginY := linesHeight - height
if newOriginY < 0 {
newOriginY = 0
}
err := view.SetOrigin(0, newOriginY)
if err != nil {
panic(err)
}
}
view.FlushStaleCells()
},
func() {
_ = view.SetOrigin(0, 0)
},
)
gui.viewBufferManagerMap[view.Name()] = manager
}

View File

@ -31,15 +31,36 @@ type ViewBufferManager struct {
Log *logrus.Entry
newTaskId int
readLines chan int
taskKey string
onNewKey func()
// beforeStart is the function that is called before starting a new task
beforeStart func()
refreshView func()
flushStaleCells func()
beforeStart func()
refreshView func()
onEndOfInput func()
}
func NewViewBufferManager(log *logrus.Entry, writer io.Writer, beforeStart func(), refreshView func(), flushStaleCells func()) *ViewBufferManager {
return &ViewBufferManager{Log: log, writer: writer, beforeStart: beforeStart, refreshView: refreshView, flushStaleCells: flushStaleCells, readLines: make(chan int, 1024)}
func (m *ViewBufferManager) GetTaskKey() string {
return m.taskKey
}
func NewViewBufferManager(
log *logrus.Entry,
writer io.Writer,
beforeStart func(),
refreshView func(),
onEndOfInput func(),
onNewKey func(),
) *ViewBufferManager {
return &ViewBufferManager{
Log: log,
writer: writer,
beforeStart: beforeStart,
refreshView: refreshView,
onEndOfInput: onEndOfInput,
readLines: make(chan int, 1024),
onNewKey: onNewKey,
}
}
func (m *ViewBufferManager) ReadLines(n int) {
@ -117,11 +138,11 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string
if !ok {
// if we're here then there's nothing left to scan from the source
// so we're at the EOF and can flush the stale content
m.flushStaleCells()
m.onEndOfInput()
m.refreshView()
break outer
}
_, _ = m.writer.Write(append(scanner.Bytes(), []byte("\n")...))
_, _ = m.writer.Write(append(scanner.Bytes(), '\n'))
}
m.refreshView()
case <-stop:
@ -179,11 +200,17 @@ func (t *ViewBufferManager) Close() {
// 1) command based, where the manager can be asked to read more lines, but the command can be killed
// 2) string based, where the manager can also be asked to read more lines
func (m *ViewBufferManager) NewTask(f func(stop chan struct{}) error) error {
func (m *ViewBufferManager) NewTask(f func(stop chan struct{}) error, key string) error {
go utils.Safe(func() {
m.taskIDMutex.Lock()
m.newTaskId++
taskID := m.newTaskId
if m.GetTaskKey() != key && m.onNewKey != nil {
m.onNewKey()
}
m.taskKey = key
m.taskIDMutex.Unlock()
m.waitingMutex.Lock()