diff --git a/docs/Config.md b/docs/Config.md index 650c3c969a7..e8e31e3d9a3 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -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' diff --git a/go.mod b/go.mod index 249fe4d46c8..5ec732e5906 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 643db264bc4..5664ea2a865 100644 --- a/go.sum +++ b/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= diff --git a/pkg/commands/patch/patch.go b/pkg/commands/patch/patch.go index 55dd8f80d54..04933472747 100644 --- a/pkg/commands/patch/patch.go +++ b/pkg/commands/patch/patch.go @@ -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) +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 9eb8e028af5..a217d8099ab 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -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, diff --git a/pkg/gui/context/patch_explorer_context.go b/pkg/gui/context/patch_explorer_context.go index 17ecae4ae93..54a9356ac11 100644 --- a/pkg/gui/context/patch_explorer_context.go +++ b/pkg/gui/context/patch_explorer_context.go @@ -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) diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index fb6d8736af2..cdaea413aa8 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -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{}) } diff --git a/pkg/gui/controllers/patch_explorer_controller.go b/pkg/gui/controllers/patch_explorer_controller.go index dd19d08dbd6..83c8633a749 100644 --- a/pkg/gui/controllers/patch_explorer_controller.go +++ b/pkg/gui/controllers/patch_explorer_controller.go @@ -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 } diff --git a/pkg/gui/controllers/scroll_off_margin.go b/pkg/gui/controllers/scroll_off_margin.go new file mode 100644 index 00000000000..119f300908c --- /dev/null +++ b/pkg/gui/controllers/scroll_off_margin.go @@ -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 +} diff --git a/pkg/gui/controllers/scroll_off_margin_test.go b/pkg/gui/controllers/scroll_off_margin_test.go new file mode 100644 index 00000000000..099da299f4c --- /dev/null +++ b/pkg/gui/controllers/scroll_off_margin_test.go @@ -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) + }) + } +} diff --git a/pkg/gui/patch_exploring/focus.go b/pkg/gui/patch_exploring/focus.go index cf0908999c9..02fed7b28f9 100644 --- a/pkg/gui/patch_exploring/focus.go +++ b/pkg/gui/patch_exploring/focus.go @@ -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 diff --git a/pkg/gui/patch_exploring/focus_test.go b/pkg/gui/patch_exploring/focus_test.go index eb3ed7c6659..bbe4b234198 100644 --- a/pkg/gui/patch_exploring/focus_test.go +++ b/pkg/gui/patch_exploring/focus_test.go @@ -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)) }) } } diff --git a/pkg/gui/patch_exploring/state.go b/pkg/gui/patch_exploring/state.go index ad5426805c4..1c82d59cb26 100644 --- a/pkg/gui/patch_exploring/state.go +++ b/pkg/gui/patch_exploring/state.go @@ -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) } diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 939ceaa29aa..5c6fed903ff 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -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) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 9561f09eb7d..539b8a48885 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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