mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-03 13:21:56 +02:00
Jump to middle of the view when selection leaves the visible area (#2915)
This commit is contained in:
commit
7402be98b6
@ -35,6 +35,7 @@ gui:
|
||||
windowSize: 'normal' # one of 'normal' | 'half' | 'full' default is 'normal'
|
||||
scrollHeight: 2 # how many lines you scroll by
|
||||
scrollPastBottom: true # enable scrolling past the bottom
|
||||
scrollOffMargin: 2 # how many lines to keep before/after the cursor when it reaches the top/bottom of the view
|
||||
sidePanelWidth: 0.3333 # number from 0 to 1
|
||||
expandFocusedSidePanel: false
|
||||
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
|
||||
|
2
go.mod
2
go.mod
@ -15,7 +15,7 @@ require (
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230807090044-83a7161c8727
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230815093813-9f3df4a6da3b
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
|
4
go.sum
4
go.sum
@ -179,8 +179,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230807090044-83a7161c8727 h1:cLq698s96uDMm0n5379doAjIKoip3/8ioWIM8pySRLY=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230807090044-83a7161c8727/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230815093813-9f3df4a6da3b h1:D2Qgpvo+i7bIIBbi/UtzrpyTuUj020lJeGxMa0J1jGs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20230815093813-9f3df4a6da3b/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
|
||||
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
|
@ -149,3 +149,8 @@ func (self *Patch) LineCount() int {
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Returns the number of hunks of the patch
|
||||
func (self *Patch) HunkCount() int {
|
||||
return len(self.hunks)
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ type GuiConfig struct {
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
ScrollOffMargin int `yaml:"scrollOffMargin"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipDiscardChangeWarning bool `yaml:"skipDiscardChangeWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
@ -418,6 +419,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
Gui: GuiConfig{
|
||||
ScrollHeight: 2,
|
||||
ScrollPastBottom: true,
|
||||
ScrollOffMargin: 2,
|
||||
MouseEvents: true,
|
||||
SkipDiscardChangeWarning: false,
|
||||
SkipStashWarning: false,
|
||||
|
@ -109,8 +109,9 @@ func (self *PatchExplorerContext) FocusSelection() {
|
||||
_, viewHeight := view.Size()
|
||||
bufferHeight := viewHeight - 1
|
||||
_, origin := view.Origin()
|
||||
numLines := view.LinesHeight()
|
||||
|
||||
newOriginY := state.CalculateOrigin(origin, bufferHeight)
|
||||
newOriginY := state.CalculateOrigin(origin, bufferHeight, numLines)
|
||||
|
||||
_ = view.SetOriginY(newOriginY)
|
||||
|
||||
|
@ -82,6 +82,12 @@ func (self *ListController) handleLineChange(change int) error {
|
||||
// doing this check so that if we're holding the up key at the start of the list
|
||||
// we're not constantly re-rendering the main view.
|
||||
if before != after {
|
||||
if change == -1 {
|
||||
checkScrollUp(self.context.GetViewTrait(), self.c.UserConfig.Gui.ScrollOffMargin, before, after)
|
||||
} else if change == 1 {
|
||||
checkScrollDown(self.context.GetViewTrait(), self.c.UserConfig.Gui.ScrollOffMargin, before, after)
|
||||
}
|
||||
|
||||
return self.context.HandleFocus(types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
|
@ -159,13 +159,25 @@ func (self *PatchExplorerController) GetMouseKeybindings(opts types.KeybindingsO
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandlePrevLine() error {
|
||||
before := self.context.GetState().GetSelectedLineIdx()
|
||||
self.context.GetState().CycleSelection(false)
|
||||
after := self.context.GetState().GetSelectedLineIdx()
|
||||
|
||||
if self.context.GetState().SelectingLine() {
|
||||
checkScrollUp(self.context.GetViewTrait(), self.c.UserConfig.Gui.ScrollOffMargin, before, after)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PatchExplorerController) HandleNextLine() error {
|
||||
before := self.context.GetState().GetSelectedLineIdx()
|
||||
self.context.GetState().CycleSelection(true)
|
||||
after := self.context.GetState().GetSelectedLineIdx()
|
||||
|
||||
if self.context.GetState().SelectingLine() {
|
||||
checkScrollDown(self.context.GetViewTrait(), self.c.UserConfig.Gui.ScrollOffMargin, before, after)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
70
pkg/gui/controllers/scroll_off_margin.go
Normal file
70
pkg/gui/controllers/scroll_off_margin.go
Normal file
@ -0,0 +1,70 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// To be called after pressing up-arrow; checks whether the cursor entered the
|
||||
// top scroll-off margin, and so the view needs to be scrolled up one line
|
||||
func checkScrollUp(view types.IViewTrait, scrollOffMargin int, lineIdxBefore int, lineIdxAfter int) {
|
||||
viewPortStart, viewPortHeight := view.ViewPortYBounds()
|
||||
|
||||
linesToScroll := calculateLinesToScrollUp(
|
||||
viewPortStart, viewPortHeight, scrollOffMargin, lineIdxBefore, lineIdxAfter)
|
||||
if linesToScroll != 0 {
|
||||
view.ScrollUp(linesToScroll)
|
||||
}
|
||||
}
|
||||
|
||||
// To be called after pressing down-arrow; checks whether the cursor entered the
|
||||
// bottom scroll-off margin, and so the view needs to be scrolled down one line
|
||||
func checkScrollDown(view types.IViewTrait, scrollOffMargin int, lineIdxBefore int, lineIdxAfter int) {
|
||||
viewPortStart, viewPortHeight := view.ViewPortYBounds()
|
||||
|
||||
linesToScroll := calculateLinesToScrollDown(
|
||||
viewPortStart, viewPortHeight, scrollOffMargin, lineIdxBefore, lineIdxAfter)
|
||||
if linesToScroll != 0 {
|
||||
view.ScrollDown(linesToScroll)
|
||||
}
|
||||
}
|
||||
|
||||
func calculateLinesToScrollUp(viewPortStart int, viewPortHeight int, scrollOffMargin int, lineIdxBefore int, lineIdxAfter int) int {
|
||||
// Cap the margin to half the view height. This allows setting the config to
|
||||
// a very large value to keep the cursor always in the middle of the screen.
|
||||
// Use +.5 so that if the height is even, the top margin is one line higher
|
||||
// than the bottom margin.
|
||||
scrollOffMargin = utils.Min(scrollOffMargin, int((float64(viewPortHeight)+.5)/2))
|
||||
|
||||
// Scroll only if the "before" position was visible (this could be false if
|
||||
// the scroll wheel was used to scroll the selected line out of view) ...
|
||||
if lineIdxBefore >= viewPortStart && lineIdxBefore < viewPortStart+viewPortHeight {
|
||||
marginEnd := viewPortStart + scrollOffMargin
|
||||
// ... and the "after" position is within the top margin (or before it)
|
||||
if lineIdxAfter < marginEnd {
|
||||
return marginEnd - lineIdxAfter
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func calculateLinesToScrollDown(viewPortStart int, viewPortHeight int, scrollOffMargin int, lineIdxBefore int, lineIdxAfter int) int {
|
||||
// Cap the margin to half the view height. This allows setting the config to
|
||||
// a very large value to keep the cursor always in the middle of the screen.
|
||||
// Use -.5 so that if the height is even, the bottom margin is one line lower
|
||||
// than the top margin.
|
||||
scrollOffMargin = utils.Min(scrollOffMargin, int((float64(viewPortHeight)-.5)/2))
|
||||
|
||||
// Scroll only if the "before" position was visible (this could be false if
|
||||
// the scroll wheel was used to scroll the selected line out of view) ...
|
||||
if lineIdxBefore >= viewPortStart && lineIdxBefore < viewPortStart+viewPortHeight {
|
||||
marginStart := viewPortStart + viewPortHeight - scrollOffMargin - 1
|
||||
// ... and the "after" position is within the bottom margin (or after it)
|
||||
if lineIdxAfter > marginStart {
|
||||
return lineIdxAfter - marginStart
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
171
pkg/gui/controllers/scroll_off_margin_test.go
Normal file
171
pkg/gui/controllers/scroll_off_margin_test.go
Normal file
@ -0,0 +1,171 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_calculateLinesToScrollUp(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
viewPortStart int
|
||||
viewPortHeight int
|
||||
scrollOffMargin int
|
||||
lineIdxBefore int
|
||||
lineIdxAfter int
|
||||
expectedLinesToScroll int
|
||||
}{
|
||||
{
|
||||
name: "before position is above viewport - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 9,
|
||||
lineIdxAfter: 8,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before position is below viewport - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 20,
|
||||
lineIdxAfter: 19,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before and after positions are outside scroll-off margin - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 14,
|
||||
lineIdxAfter: 13,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before outside, after inside scroll-off margin - scroll by 1",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 13,
|
||||
lineIdxAfter: 12,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
{
|
||||
name: "before inside scroll-off margin - scroll by more than 1",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 11,
|
||||
lineIdxAfter: 10,
|
||||
expectedLinesToScroll: 3,
|
||||
},
|
||||
{
|
||||
name: "very large scroll-off margin - keep view centered (even viewport height)",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 999,
|
||||
lineIdxBefore: 15,
|
||||
lineIdxAfter: 14,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
{
|
||||
name: "very large scroll-off margin - keep view centered (odd viewport height)",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 9,
|
||||
scrollOffMargin: 999,
|
||||
lineIdxBefore: 14,
|
||||
lineIdxAfter: 13,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
linesToScroll := calculateLinesToScrollUp(scenario.viewPortStart, scenario.viewPortHeight, scenario.scrollOffMargin, scenario.lineIdxBefore, scenario.lineIdxAfter)
|
||||
assert.Equal(t, scenario.expectedLinesToScroll, linesToScroll)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_calculateLinesToScrollDown(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
viewPortStart int
|
||||
viewPortHeight int
|
||||
scrollOffMargin int
|
||||
lineIdxBefore int
|
||||
lineIdxAfter int
|
||||
expectedLinesToScroll int
|
||||
}{
|
||||
{
|
||||
name: "before position is above viewport - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 9,
|
||||
lineIdxAfter: 10,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before position is below viewport - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 20,
|
||||
lineIdxAfter: 21,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before and after positions are outside scroll-off margin - don't scroll",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 15,
|
||||
lineIdxAfter: 16,
|
||||
expectedLinesToScroll: 0,
|
||||
},
|
||||
{
|
||||
name: "before outside, after inside scroll-off margin - scroll by 1",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 16,
|
||||
lineIdxAfter: 17,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
{
|
||||
name: "before inside scroll-off margin - scroll by more than 1",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 3,
|
||||
lineIdxBefore: 18,
|
||||
lineIdxAfter: 19,
|
||||
expectedLinesToScroll: 3,
|
||||
},
|
||||
{
|
||||
name: "very large scroll-off margin - keep view centered (even viewport height)",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 10,
|
||||
scrollOffMargin: 999,
|
||||
lineIdxBefore: 15,
|
||||
lineIdxAfter: 16,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
{
|
||||
name: "very large scroll-off margin - keep view centered (odd viewport height)",
|
||||
viewPortStart: 10,
|
||||
viewPortHeight: 9,
|
||||
scrollOffMargin: 999,
|
||||
lineIdxBefore: 14,
|
||||
lineIdxAfter: 15,
|
||||
expectedLinesToScroll: 1,
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
linesToScroll := calculateLinesToScrollDown(scenario.viewPortStart, scenario.viewPortHeight, scenario.scrollOffMargin, scenario.lineIdxBefore, scenario.lineIdxAfter)
|
||||
assert.Equal(t, scenario.expectedLinesToScroll, linesToScroll)
|
||||
})
|
||||
}
|
||||
}
|
@ -2,21 +2,19 @@ package patch_exploring
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/utils"
|
||||
|
||||
func calculateOrigin(currentOrigin int, bufferHeight int, firstLineIdx int, lastLineIdx int, selectedLineIdx int, mode selectMode) int {
|
||||
func calculateOrigin(currentOrigin int, bufferHeight int, numLines int, firstLineIdx int, lastLineIdx int, selectedLineIdx int, mode selectMode) int {
|
||||
needToSeeIdx, wantToSeeIdx := getNeedAndWantLineIdx(firstLineIdx, lastLineIdx, selectedLineIdx, mode)
|
||||
|
||||
return calculateNewOriginWithNeededAndWantedIdx(currentOrigin, bufferHeight, needToSeeIdx, wantToSeeIdx)
|
||||
return calculateNewOriginWithNeededAndWantedIdx(currentOrigin, bufferHeight, numLines, needToSeeIdx, wantToSeeIdx)
|
||||
}
|
||||
|
||||
// we want to scroll our origin so that the index we need to see is in view
|
||||
// and the other index we want to see (e.g. the other side of a line range)
|
||||
// is in as close to being in view as possible.
|
||||
func calculateNewOriginWithNeededAndWantedIdx(currentOrigin int, bufferHeight int, needToSeeIdx int, wantToSeeIdx int) int {
|
||||
// is as close to being in view as possible.
|
||||
func calculateNewOriginWithNeededAndWantedIdx(currentOrigin int, bufferHeight int, numLines int, needToSeeIdx int, wantToSeeIdx int) int {
|
||||
origin := currentOrigin
|
||||
if needToSeeIdx < currentOrigin {
|
||||
origin = needToSeeIdx
|
||||
} else if needToSeeIdx > currentOrigin+bufferHeight {
|
||||
origin = needToSeeIdx - bufferHeight
|
||||
if needToSeeIdx < currentOrigin || needToSeeIdx > currentOrigin+bufferHeight {
|
||||
origin = utils.Max(utils.Min(needToSeeIdx-bufferHeight/2, numLines-bufferHeight-1), 0)
|
||||
}
|
||||
|
||||
bottom := origin + bufferHeight
|
||||
|
@ -11,6 +11,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
name string
|
||||
origin int
|
||||
bufferHeight int
|
||||
numLines int
|
||||
firstLineIdx int
|
||||
lastLineIdx int
|
||||
selectedLineIdx int
|
||||
@ -20,29 +21,54 @@ func TestNewOrigin(t *testing.T) {
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
name: "selection above scroll window",
|
||||
name: "selection above scroll window, enough room to put it in the middle",
|
||||
origin: 250,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 210,
|
||||
lastLineIdx: 210,
|
||||
selectedLineIdx: 210,
|
||||
selectMode: LINE,
|
||||
expected: 160,
|
||||
},
|
||||
{
|
||||
name: "selection above scroll window, not enough room to put it in the middle",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 10,
|
||||
lastLineIdx: 10,
|
||||
selectedLineIdx: 10,
|
||||
selectMode: LINE,
|
||||
expected: 10,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "selection below scroll window",
|
||||
name: "selection below scroll window, enough room to put it in the middle",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 150,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 150,
|
||||
selectMode: LINE,
|
||||
expected: 50,
|
||||
expected: 100,
|
||||
},
|
||||
{
|
||||
name: "selection below scroll window, not enough room to put it in the middle",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
numLines: 200,
|
||||
firstLineIdx: 199,
|
||||
lastLineIdx: 199,
|
||||
selectedLineIdx: 199,
|
||||
selectMode: LINE,
|
||||
expected: 99,
|
||||
},
|
||||
{
|
||||
name: "selection within scroll window",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 50,
|
||||
lastLineIdx: 50,
|
||||
selectedLineIdx: 50,
|
||||
@ -53,6 +79,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
name: "range ending below scroll window with selection at end of range",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 150,
|
||||
@ -63,6 +90,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
name: "range ending below scroll window with selection at beginning of range",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 40,
|
||||
@ -73,6 +101,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
name: "range starting above scroll window with selection at beginning of range",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 40,
|
||||
@ -83,6 +112,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
name: "hunk extending beyond both bounds of scroll window",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
numLines: 500,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 200,
|
||||
selectedLineIdx: 70,
|
||||
@ -94,7 +124,7 @@ func TestNewOrigin(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
assert.EqualValues(t, s.expected, calculateOrigin(s.origin, s.bufferHeight, s.firstLineIdx, s.lastLineIdx, s.selectedLineIdx, s.selectMode))
|
||||
assert.EqualValues(t, s.expected, calculateOrigin(s.origin, s.bufferHeight, s.numLines, s.firstLineIdx, s.lastLineIdx, s.selectedLineIdx, s.selectMode))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +143,13 @@ func (s *State) CycleHunk(forward bool) {
|
||||
}
|
||||
|
||||
hunkIdx := s.patch.HunkContainingLine(s.selectedLineIdx)
|
||||
start := s.patch.HunkStartIdx(hunkIdx + change)
|
||||
s.selectedLineIdx = s.patch.GetNextChangeIdx(start)
|
||||
if hunkIdx != -1 {
|
||||
newHunkIdx := hunkIdx + change
|
||||
if newHunkIdx >= 0 && newHunkIdx < s.patch.HunkCount() {
|
||||
start := s.patch.HunkStartIdx(newHunkIdx)
|
||||
s.selectedLineIdx = s.patch.GetNextChangeIdx(start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) CycleLine(forward bool) {
|
||||
@ -216,8 +221,8 @@ func (s *State) SelectTop() {
|
||||
s.SelectLine(0)
|
||||
}
|
||||
|
||||
func (s *State) CalculateOrigin(currentOrigin int, bufferHeight int) int {
|
||||
func (s *State) CalculateOrigin(currentOrigin int, bufferHeight int, numLines int) int {
|
||||
firstLineIdx, lastLineIdx := s.SelectedRange()
|
||||
|
||||
return calculateOrigin(currentOrigin, bufferHeight, firstLineIdx, lastLineIdx, s.GetSelectedLineIdx(), s.selectMode)
|
||||
return calculateOrigin(currentOrigin, bufferHeight, numLines, firstLineIdx, lastLineIdx, s.GetSelectedLineIdx(), s.selectMode)
|
||||
}
|
||||
|
44
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
44
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@ -273,25 +273,33 @@ func (v *View) FocusPoint(cx int, cy int) {
|
||||
ly = 0
|
||||
}
|
||||
|
||||
// if line is above origin, move origin and set cursor to zero
|
||||
// if line is below origin + height, move origin and set cursor to max
|
||||
// otherwise set cursor to value - origin
|
||||
if ly > lineCount {
|
||||
v.cx = cx
|
||||
v.cy = cy
|
||||
v.oy = 0
|
||||
} else if cy < v.oy {
|
||||
v.cx = cx
|
||||
v.cy = 0
|
||||
v.oy = cy
|
||||
} else if cy > v.oy+ly {
|
||||
v.cx = cx
|
||||
v.cy = ly
|
||||
v.oy = cy - ly
|
||||
} else {
|
||||
v.cx = cx
|
||||
v.cy = cy - v.oy
|
||||
v.oy = calculateNewOrigin(cy, v.oy, lineCount, ly)
|
||||
v.cx = cx
|
||||
v.cy = cy - v.oy
|
||||
}
|
||||
|
||||
func calculateNewOrigin(selectedLine int, oldOrigin int, lineCount int, viewHeight int) int {
|
||||
if viewHeight > lineCount {
|
||||
return 0
|
||||
} else if selectedLine < oldOrigin || selectedLine > oldOrigin+viewHeight {
|
||||
// If the selected line is outside the visible area, scroll the view so
|
||||
// that the selected line is in the middle.
|
||||
newOrigin := selectedLine - viewHeight/2
|
||||
|
||||
// However, take care not to overflow if the total line count is less
|
||||
// than the view height.
|
||||
maxOrigin := lineCount - viewHeight - 1
|
||||
if newOrigin > maxOrigin {
|
||||
newOrigin = maxOrigin
|
||||
}
|
||||
if newOrigin < 0 {
|
||||
newOrigin = 0
|
||||
}
|
||||
|
||||
return newOrigin
|
||||
}
|
||||
|
||||
return oldOrigin
|
||||
}
|
||||
|
||||
func (s *searcher) search(str string) {
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -124,7 +124,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20230807090044-83a7161c8727
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20230815093813-9f3df4a6da3b
|
||||
## explicit; go 1.12
|
||||
github.com/jesseduffield/gocui
|
||||
# github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
|
||||
|
Loading…
x
Reference in New Issue
Block a user