Repository: jesseduffield/lazygit
File: pkg/gui/controllers/list_controller.go (Go)

Code before:
	}

	if cursorMoved || rangeBefore != rangeAfter {
		self.context.HandleFocus(types.OnFocusOpts{})
	}

	return nil
}

func (self *ListController) HandlePrevPage() error {
	return self.handlePageChange(-self.context.GetViewTrait().PageDelta())
}

func (self *ListController) HandleNextPage() error {
	return self.handlePageChange(self.context.GetViewTrait().PageDelta())
}

func (self *ListController) handlePageChange(delta int) error {
	list := self.context.GetList()
	view := self.context.GetViewTrait()

	before := list.GetSelectedLineIdx()
	list.MoveSelectedLine(delta)
	after := list.GetSelectedLineIdx()

	if err := self.pushContextIfNotFocused(); err != nil {
		return err
	}

	cursorMoved := before != after
	if cursorMoved {
		// For page navigation, we want to keep the selection at the top (for prev page)
		// or bottom (for next page) of the viewport, rather than centering it.
		viewPortStart, viewPortHeight := view.ViewPortYBounds()
		afterViewIdx := self.context.ModelIndexToViewIndex(after)

		if delta < 0 {
			// Previous page: keep selection at top of viewport
			linesToScroll := afterViewIdx - viewPortStart
			if linesToScroll < 0 {
				view.ScrollUp(-linesToScroll)
			}
		} else {
			// Next page: keep selection at bottom of viewport
			linesToScroll := afterViewIdx - (viewPortStart + viewPortHeight - 1)
			if linesToScroll > 0 {
				view.ScrollDown(linesToScroll)
			}
		}

		self.context.HandleFocus(types.OnFocusOpts{})

Review comment (suggestion):
I don't think this is needed. The method is only called from keybinding handlers, so the view must be focused already, otherwise the binding wouldn't have been dispatched to it.

I realize that this is copied from handleLineChangeAux, but it doesn't seem to be needed there, either (for the same reason), so it might be nice to add a cleanup commit to remove it there, too.

Code after:
	}

	return nil
}

func (self *ListController) HandlePrevPage() error {
	return self.handlePageChange(-self.context.GetViewTrait().PageDelta())
}

func (self *ListController) HandleNextPage() error {
	return self.handlePageChange(self.context.GetViewTrait().PageDelta())
}

func (self *ListController) handlePageChange(delta int) error {
	list := self.context.GetList()
	view := self.context.GetViewTrait()

	before := list.GetSelectedLineIdx()

	viewPortStart, viewPortHeight := view.ViewPortYBounds()
	beforeViewIdx := self.context.ModelIndexToViewIndex(before)
	afterViewIdx := beforeViewIdx + delta
	newModelIndex := self.context.ViewIndexToModelIndex(afterViewIdx)

	if delta < 0 {
		// Previous page: keep selection at top of viewport
		indexAtTopOfPage := self.context.ViewIndexToModelIndex(viewPortStart)
		if before != indexAtTopOfPage {
			// If the selection isn't already at the top of the page, move it there without scrolling
			list.MoveSelectedLine(indexAtTopOfPage - before)
		} else {
			// Otherwise, move the selection by one page and scroll
			list.MoveSelectedLine(newModelIndex - before)

			linesToScroll := afterViewIdx - viewPortStart
			if linesToScroll < 0 {
				view.ScrollUp(-linesToScroll)
			}
		}
	} else {
		// Next page: keep selection at bottom of viewport
		indexAtBottomOfPage := self.context.ViewIndexToModelIndex(viewPortStart + viewPortHeight - 1)
		if before != indexAtBottomOfPage {
			// If the selection isn't already at the bottom of the page, move it there without scrolling
			list.MoveSelectedLine(indexAtBottomOfPage - before)
		} else {
			// Otherwise, move the selection by one page and scroll
			list.MoveSelectedLine(newModelIndex - before)

			linesToScroll := afterViewIdx - (viewPortStart + viewPortHeight - 1)
			if linesToScroll > 0 {