Keep scrolling while commits are dragged at an edge

Reuse the drag autoscroller for commit drags. Scrolling stops once the
insertion point reaches the end of the allowed range in the scroll
direction, so during a rebase the view doesn't keep scrolling once the
last insertion position among the todos has been reached.
This commit is contained in:
Stefan Haller
2026-07-31 08:37:28 +02:00
parent eda4ad1192
commit 0738d55551
3 changed files with 87 additions and 7 deletions
@@ -29,8 +29,9 @@ type LocalCommitsController struct {
*ListControllerTrait[*models.Commit]
c *ControllerCommon
pullFiles PullFilesFn
commitDrag *commitDragState
pullFiles PullFilesFn
commitDrag *commitDragState
dragAutoscroller *helpers.DragAutoscroller
}
// commitDragState tracks a mouse drag that moves the selected commits. It is
@@ -77,7 +78,7 @@ func NewLocalCommitsController(
c *ControllerCommon,
pullFiles PullFilesFn,
) *LocalCommitsController {
return &LocalCommitsController{
controller := &LocalCommitsController{
baseController: baseController{},
c: c,
pullFiles: pullFiles,
@@ -88,6 +89,13 @@ func NewLocalCommitsController(
c.Contexts().LocalCommits.GetSelectedItems,
),
}
controller.dragAutoscroller = helpers.NewDragAutoscroller(
c.HelperCommon,
c.Contexts().LocalCommits,
controller.canCommitDragAutoscroll,
controller.handleCommitDragAutoscroll,
)
return controller
}
func (self *LocalCommitsController) GetMouseKeybindings(types.KeybindingsOpts) []*gocui.ViewMouseBinding {
@@ -176,13 +184,22 @@ func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBinding
}
self.commitDrag.hasMoved = true
if self.updateCommitDragInsertion(opts.Y) {
self.c.PostRefreshUpdate(self.context())
}
originY := self.context().GetView().OriginY()
self.dragAutoscroller.Update(opts.Y - originY)
self.restoreCommitDragHighlight()
insertionIndex := self.commitDragInsertionIndex(opts.Y)
return nil
}
func (self *LocalCommitsController) updateCommitDragInsertion(viewIndex int) bool {
insertionIndex := self.commitDragInsertionIndex(viewIndex)
if insertionIndex >= self.commitDrag.startIndex && insertionIndex <= self.commitDrag.endIndex+1 {
insertionIndex = -1
}
if insertionIndex == self.commitDrag.insertionIndex {
return nil
return false
}
self.commitDrag.insertionIndex = insertionIndex
@@ -191,8 +208,7 @@ func (self *LocalCommitsController) handleCommitDrag(opts gocui.ViewMouseBinding
} else {
self.context().SetDropInsertionIndex(insertionIndex)
}
self.c.PostRefreshUpdate(self.context())
return nil
return true
}
// gocui moves the view cursor to the pointer position before invoking our
@@ -235,6 +251,7 @@ func (self *LocalCommitsController) handleCommitDragRelease(gocui.ViewMouseBindi
}
state := self.commitDrag
self.dragAutoscroller.Cancel()
self.commitDrag = nil
self.context().ClearDropInsertionIndex()
@@ -312,6 +329,7 @@ func (self *LocalCommitsController) GetOnFocusLost() func(types.OnFocusLostOpts)
return
}
self.dragAutoscroller.Cancel()
self.commitDrag = nil
self.c.GocuiGui().CancelMouseCapture()
self.context().ClearDropInsertionIndex()
@@ -319,6 +337,32 @@ func (self *LocalCommitsController) GetOnFocusLost() func(types.OnFocusLostOpts)
}
}
// Stop autoscrolling once the insertion point has reached the end of the
// allowed range in the scroll direction; e.g. during a rebase there is no
// point in scrolling on into the section of real commits.
func (self *LocalCommitsController) canCommitDragAutoscroll(direction int) bool {
state := self.commitDrag
if state == nil {
return false
}
if direction < 0 {
return state.insertionIndex != state.minInsertion
}
return state.insertionIndex != state.maxInsertion
}
func (self *LocalCommitsController) handleCommitDragAutoscroll(viewIndex int) bool {
if self.commitDrag == nil {
return false
}
self.updateCommitDragInsertion(viewIndex)
self.context().SetNeedRerenderVisibleLines()
self.context().HandleRender()
self.restoreCommitDragHighlight()
return self.canCommitDragAutoscroll(self.dragAutoscroller.Direction())
}
func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
editCommitKey := opts.Config.Universal.Edit
@@ -0,0 +1,35 @@
package interactive_rebase
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DragToReorderWithAutoscroll = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Keep scrolling commits while a dragged commit is held at the panel edge",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.CreateNCommits(40)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
TopLines(
Contains("commit-40").IsSelected(),
).
ClickAndHold(1, 0).
MouseMoveToBottom(1).
OriginYAtLeast(3).
MouseRelease().
SelectedLines(
Contains("commit-40"),
).
SelectedLineIdxAtLeast(3).
GotoTop().
TopLines(
Contains("commit-39").IsSelected(),
)
},
})
+1
View File
@@ -293,6 +293,7 @@ var tests = []*components.IntegrationTest{
interactive_rebase.DragKeepsSelectionHighlighted,
interactive_rebase.DragToReorder,
interactive_rebase.DragToReorderInRebase,
interactive_rebase.DragToReorderWithAutoscroll,
interactive_rebase.DropCommitInCopiedBranchWithUpdateRef,
interactive_rebase.DropMergeCommit,
interactive_rebase.DropTodoCommitWithUpdateRef,