refactor contexts

This commit is contained in:
Jesse Duffield 2022-02-05 17:04:10 +11:00
parent 145c69d9ae
commit d82f175e79
54 changed files with 1562 additions and 1248 deletions

View file

@ -13,22 +13,9 @@ import (
// list panel functions
func (gui *Gui) getSelectedBranch() *models.Branch {
if len(gui.State.Model.Branches) == 0 {
return nil
}
selectedLine := gui.State.Panels.Branches.SelectedLineIdx
if selectedLine == -1 {
return nil
}
return gui.State.Model.Branches[selectedLine]
}
func (gui *Gui) branchesRenderToMain() error {
var task updateTask
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
task = NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo)
} else {
@ -48,24 +35,26 @@ func (gui *Gui) branchesRenderToMain() error {
// specific functions
func (gui *Gui) handleBranchPress() error {
if gui.State.Panels.Branches.SelectedLineIdx == -1 {
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil
}
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
if branch == gui.getCheckedOutBranch() {
return gui.c.ErrorMsg(gui.c.Tr.AlreadyCheckedOutBranch)
}
branch := gui.getSelectedBranch()
gui.c.LogAction(gui.c.Tr.Actions.CheckoutBranch)
return gui.helpers.Refs.CheckoutRef(branch.Name, types.CheckoutRefOptions{})
}
func (gui *Gui) handleCreatePullRequestPress() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
return gui.createPullRequest(branch.Name, "")
}
func (gui *Gui) handleCreatePullRequestMenu() error {
selectedBranch := gui.getSelectedBranch()
selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil {
return nil
}
@ -77,7 +66,7 @@ func (gui *Gui) handleCreatePullRequestMenu() error {
func (gui *Gui) handleCopyPullRequestURLPress() error {
hostingServiceMgr := gui.getHostingServiceMgr()
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
branchExistsOnRemote := gui.git.Remote.CheckRemoteBranchExists(branch.Name)
@ -109,7 +98,7 @@ func (gui *Gui) handleGitFetch() error {
}
func (gui *Gui) handleForceCheckout() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
message := gui.c.Tr.SureForceCheckout
title := gui.c.Tr.ForceCheckoutBranch
@ -156,7 +145,7 @@ func (gui *Gui) getCheckedOutBranch() *models.Branch {
}
func (gui *Gui) createNewBranchWithName(newBranchName string) error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil
}
@ -165,7 +154,7 @@ func (gui *Gui) createNewBranchWithName(newBranchName string) error {
return gui.c.Error(err)
}
gui.State.Panels.Branches.SelectedLineIdx = 0
gui.State.Contexts.Branches.SetSelectedLineIdx(0)
return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}
@ -174,7 +163,7 @@ func (gui *Gui) handleDeleteBranch() error {
}
func (gui *Gui) deleteBranch(force bool) error {
selectedBranch := gui.getSelectedBranch()
selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil {
return nil
}
@ -245,12 +234,12 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
}
func (gui *Gui) handleMerge() error {
selectedBranchName := gui.getSelectedBranch().Name
selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name
return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName)
}
func (gui *Gui) handleRebaseOntoLocalBranch() error {
selectedBranchName := gui.getSelectedBranch().Name
selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name
return gui.handleRebaseOntoBranch(selectedBranchName)
}
@ -279,7 +268,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
}
func (gui *Gui) handleFastForward() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil || !branch.IsRealBranch() {
return nil
}
@ -305,7 +294,7 @@ func (gui *Gui) handleFastForward() error {
)
return gui.c.WithLoaderPanel(message, func() error {
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
if branch == gui.getCheckedOutBranch() {
gui.c.LogAction(action)
err := gui.git.Sync.Pull(
@ -334,7 +323,7 @@ func (gui *Gui) handleFastForward() error {
}
func (gui *Gui) handleCreateResetToBranchMenu() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil
}
@ -343,7 +332,7 @@ func (gui *Gui) handleCreateResetToBranchMenu() error {
}
func (gui *Gui) handleRenameBranch() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil || !branch.IsRealBranch() {
return nil
}
@ -364,7 +353,7 @@ func (gui *Gui) handleRenameBranch() error {
// now that we've got our stuff again we need to find that branch and reselect it.
for i, newBranch := range gui.State.Model.Branches {
if newBranch.Name == newBranchName {
gui.State.Panels.Branches.SetSelectedLineIdx(i)
gui.State.Contexts.Branches.SetSelectedLineIdx(i)
if err := gui.State.Contexts.Branches.HandleRender(); err != nil {
return err
}
@ -391,7 +380,7 @@ func (gui *Gui) handleRenameBranch() error {
}
func (gui *Gui) handleEnterBranch() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil
}
@ -400,7 +389,7 @@ func (gui *Gui) handleEnterBranch() error {
}
func (gui *Gui) handleNewBranchOffBranch() error {
selectedBranch := gui.getSelectedBranch()
selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil {
return nil
}

View file

@ -5,16 +5,11 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
func (gui *Gui) getSelectedCommitFileNode() *filetree.CommitFileNode {
return gui.State.Contexts.CommitFiles.GetSelectedFileNode()
}
func (gui *Gui) getSelectedCommitFile() *models.CommitFile {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -22,20 +17,21 @@ func (gui *Gui) getSelectedCommitFile() *models.CommitFile {
}
func (gui *Gui) getSelectedCommitFilePath() string {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return ""
}
return node.GetPath()
}
// TODO: do we need this?
func (gui *Gui) onCommitFileFocus() error {
gui.escapeLineByLinePanel()
return nil
}
func (gui *Gui) commitFilesRenderToMain() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -62,7 +58,7 @@ func (gui *Gui) commitFilesRenderToMain() error {
}
func (gui *Gui) handleCheckoutCommitFile() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -88,7 +84,7 @@ func (gui *Gui) handleDiscardOldFileChange() error {
HandleConfirm: func() error {
return gui.c.WithWaitingStatus(gui.c.Tr.RebasingStatus, func() error {
gui.c.LogAction(gui.c.Tr.Actions.DiscardOldFileChange)
if err := gui.git.Rebase.DiscardOldFileChanges(gui.State.Model.Commits, gui.State.Panels.Commits.SelectedLineIdx, fileName); err != nil {
if err := gui.git.Rebase.DiscardOldFileChanges(gui.State.Model.Commits, gui.State.Contexts.BranchCommits.GetSelectedLineIdx(), fileName); err != nil {
if err := gui.helpers.Rebase.CheckMergeOrRebase(err); err != nil {
return err
}
@ -122,7 +118,7 @@ func (gui *Gui) refreshCommitFilesView() error {
}
func (gui *Gui) handleOpenOldCommitFile() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -131,7 +127,7 @@ func (gui *Gui) handleOpenOldCommitFile() error {
}
func (gui *Gui) handleEditCommitFile() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -144,7 +140,7 @@ func (gui *Gui) handleEditCommitFile() error {
}
func (gui *Gui) handleToggleFileForPatch() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -212,7 +208,7 @@ func (gui *Gui) handleEnterCommitFile() error {
}
func (gui *Gui) enterCommitFile(opts types.OnFocusOpts) error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -246,7 +242,7 @@ func (gui *Gui) enterCommitFile(opts types.OnFocusOpts) error {
}
func (gui *Gui) handleToggleCommitFileDirCollapsed() error {
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}

View file

@ -11,18 +11,12 @@ const COMMIT_THRESHOLD = 200
// list panel functions
func (gui *Gui) getSelectedLocalCommit() *models.Commit {
selectedLine := gui.State.Panels.Commits.SelectedLineIdx
if selectedLine == -1 || selectedLine > len(gui.State.Model.Commits)-1 {
return nil
}
return gui.State.Model.Commits[selectedLine]
return gui.State.Contexts.BranchCommits.GetSelected()
}
func (gui *Gui) onCommitFocus() error {
state := gui.State.Panels.Commits
if state.SelectedLineIdx > COMMIT_THRESHOLD && state.LimitCommits {
state.LimitCommits = false
if gui.State.Contexts.BranchCommits.GetSelectedLineIdx() > COMMIT_THRESHOLD && gui.State.LimitCommits {
gui.State.LimitCommits = false
go utils.Safe(func() {
if err := gui.refreshCommitsWithLimit(); err != nil {
_ = gui.c.Error(err)
@ -37,7 +31,7 @@ func (gui *Gui) onCommitFocus() error {
func (gui *Gui) branchCommitsRenderToMain() error {
var task updateTask
commit := gui.getSelectedLocalCommit()
commit := gui.State.Contexts.BranchCommits.GetSelected()
if commit == nil {
task = NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch)
} else {

View file

@ -77,7 +77,29 @@ func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int {
}
func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, int, int, int) {
panelWidth := gui.getConfirmationPanelWidth()
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth)
return gui.getConfirmationPanelDimensionsAux(panelWidth, panelHeight)
}
func (gui *Gui) getConfirmationPanelDimensionsForContentHeight(contentHeight int) (int, int, int, int) {
panelWidth := gui.getConfirmationPanelWidth()
return gui.getConfirmationPanelDimensionsAux(panelWidth, contentHeight)
}
func (gui *Gui) getConfirmationPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) {
width, height := gui.g.Size()
if panelHeight > height*3/4 {
panelHeight = height * 3 / 4
}
return width/2 - panelWidth/2,
height/2 - panelHeight/2 - panelHeight%2 - 1,
width/2 + panelWidth/2,
height/2 + panelHeight/2
}
func (gui *Gui) getConfirmationPanelWidth() int {
width, _ := gui.g.Size()
// we want a minimum width up to a point, then we do it based on ratio.
panelWidth := 4 * width / 7
minWidth := 80
@ -88,14 +110,8 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
panelWidth = minWidth
}
}
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth)
if panelHeight > height*3/4 {
panelHeight = height * 3 / 4
}
return width/2 - panelWidth/2,
height/2 - panelHeight/2 - panelHeight%2 - 1,
width/2 + panelWidth/2,
height/2 + panelHeight/2
return panelWidth
}
func (gui *Gui) prepareConfirmationPanel(

View file

@ -65,7 +65,6 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
}
if !c.IsFocusable() {
panic(c.GetKey())
return nil
}

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type BranchesContext struct {
*BranchesViewModel
*ListContextTrait
}
var _ types.IListContext = (*BranchesContext)(nil)
func NewBranchesContext(
getModel func() []*models.Branch,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *BranchesContext {
viewModel := NewBranchesViewModel(getModel)
return &BranchesContext{
BranchesViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *BranchesContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type BranchesViewModel struct {
*traits.ListCursor
getModel func() []*models.Branch
}
func NewBranchesViewModel(getModel func() []*models.Branch) *BranchesViewModel {
self := &BranchesViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *BranchesViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *BranchesViewModel) GetSelected() *models.Branch {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -9,7 +9,6 @@ import (
type CommitFilesContext struct {
*filetree.CommitFileTreeViewModel
*BaseContext
*ListContextTrait
}
@ -17,7 +16,7 @@ var _ types.IListContext = (*CommitFilesContext)(nil)
func NewCommitFilesContext(
getModel func() []*models.CommitFile,
getView func() *gocui.View,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
@ -26,43 +25,30 @@ func NewCommitFilesContext(
c *types.ControllerCommon,
) *CommitFilesContext {
baseContext := NewBaseContext(NewBaseContextOpts{
viewModel := filetree.NewCommitFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
return &CommitFilesContext{
CommitFileTreeViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(
NewBaseContext(NewBaseContextOpts{
ViewName: "commitFiles",
WindowName: "commits",
Key: COMMIT_FILES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})
self := &CommitFilesContext{}
takeFocus := func() error { return c.PushContext(self) }
viewModel := filetree.NewCommitFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
viewTrait := NewViewTrait(getView)
listContextTrait := &ListContextTrait{
base: baseContext,
list: viewModel,
viewTrait: viewTrait,
GetDisplayStrings: getDisplayStrings,
}),
ContextCallbackOpts{
OnFocus: onFocus,
OnRenderToMain: onRenderToMain,
OnFocusLost: onFocusLost,
takeFocus: takeFocus,
// TODO: handle this in a trait
RenderSelection: false,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
baseContext.AddKeybindingsFn(listContextTrait.keybindings)
self.BaseContext = baseContext
self.ListContextTrait = listContextTrait
self.CommitFileTreeViewModel = viewModel
return self
}
func (self *CommitFilesContext) GetSelectedItemId() string {

View file

@ -1,6 +1,10 @@
package context
import "github.com/jesseduffield/lazygit/pkg/gui/types"
import (
"sync"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
const (
GLOBAL_CONTEXT_KEY types.ContextKey = "global"
@ -60,18 +64,18 @@ type ContextTree struct {
Global types.Context
Status types.Context
Files *WorkingTreeContext
Submodules types.IListContext
Menu types.IListContext
Branches types.IListContext
Remotes types.IListContext
RemoteBranches types.IListContext
Menu *MenuContext
Branches *BranchesContext
Tags *TagsContext
BranchCommits types.IListContext
BranchCommits *LocalCommitsContext
CommitFiles *CommitFilesContext
ReflogCommits types.IListContext
SubCommits types.IListContext
Stash types.IListContext
Suggestions types.IListContext
Remotes *RemotesContext
Submodules *SubmodulesContext
RemoteBranches *RemoteBranchesContext
ReflogCommits *ReflogCommitsContext
SubCommits *SubCommitsContext
Stash *StashContext
Suggestions *SuggestionsContext
Normal types.Context
Staging types.Context
PatchBuilding types.Context
@ -113,6 +117,7 @@ func (self *ContextTree) Flatten() []types.Context {
type ViewContextMap struct {
content map[string]types.Context
sync.RWMutex
}
func NewViewContextMap() *ViewContextMap {
@ -120,10 +125,15 @@ func NewViewContextMap() *ViewContextMap {
}
func (self *ViewContextMap) Get(viewName string) types.Context {
self.RLock()
defer self.RUnlock()
return self.content[viewName]
}
func (self *ViewContextMap) Set(viewName string, context types.Context) {
self.Lock()
defer self.Unlock()
self.content[viewName] = context
}

View file

@ -3,44 +3,35 @@ package context
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type ListContextTrait struct {
base types.IBaseContext
list types.IList
viewTrait *ViewTrait
takeFocus func() error
GetDisplayStrings func(startIdx int, length int) [][]string
OnFocus func(...types.OnFocusOpts) error
OnRenderToMain func(...types.OnFocusOpts) error
OnFocusLost func() error
// if this is true, we'll call GetDisplayStrings for just the visible part of the
// view and re-render that. This is useful when you need to render different
// content based on the selection (e.g. for showing the selected commit)
RenderSelection bool
types.Context
c *types.ControllerCommon
list types.IList
viewTrait *ViewTrait
getDisplayStrings func(startIdx int, length int) [][]string
}
func (self *ListContextTrait) GetList() types.IList {
return self.list
}
// TODO: remove
func (self *ListContextTrait) GetPanelState() types.IListPanelState {
return self.list
}
func (self *ListContextTrait) GetViewTrait() types.IViewTrait {
return self.viewTrait
}
func (self *ListContextTrait) FocusLine() {
// we need a way of knowing whether we've rendered to the view yet.
self.viewTrait.FocusPoint(self.list.GetSelectedLineIdx())
if self.RenderSelection {
min, max := self.viewTrait.ViewPortYBounds()
displayStrings := self.GetDisplayStrings(min, max)
content := utils.RenderDisplayStrings(displayStrings)
self.viewTrait.SetViewPortContent(content)
}
self.viewTrait.SetFooter(formatListFooter(self.list.GetSelectedLineIdx(), self.list.GetItemsLength()))
}
@ -48,164 +39,29 @@ func formatListFooter(selectedLineIdx int, length int) string {
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
}
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
func (self *ListContextTrait) HandleRender() error {
if self.GetDisplayStrings != nil {
self.list.RefreshSelectedIdx()
content := utils.RenderDisplayStrings(self.GetDisplayStrings(0, self.list.GetItemsLength()))
self.viewTrait.SetContent(content)
self.c.Render()
}
return nil
}
func (self *ListContextTrait) HandleFocusLost() error {
if self.OnFocusLost != nil {
return self.OnFocusLost()
}
self.viewTrait.SetOriginX(0)
return nil
}
func (self *ListContextTrait) HandleFocus(opts ...types.OnFocusOpts) error {
self.FocusLine()
if self.OnFocus != nil {
if err := self.OnFocus(opts...); err != nil {
return err
}
return self.Context.HandleFocus(opts...)
}
if self.OnRenderToMain != nil {
if err := self.OnRenderToMain(opts...); err != nil {
return err
}
func (self *ListContextTrait) HandleFocusLost() error {
self.viewTrait.SetOriginX(0)
return self.Context.HandleFocus()
}
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
func (self *ListContextTrait) HandleRender() error {
self.list.RefreshSelectedIdx()
content := utils.RenderDisplayStrings(self.getDisplayStrings(0, self.list.GetItemsLength()))
self.viewTrait.SetContent(content)
self.c.Render()
return nil
}
func (self *ListContextTrait) HandlePrevLine() error {
return self.handleLineChange(-1)
}
func (self *ListContextTrait) HandleNextLine() error {
return self.handleLineChange(1)
}
func (self *ListContextTrait) HandleScrollLeft() error {
return self.scroll(self.viewTrait.ScrollLeft)
}
func (self *ListContextTrait) HandleScrollRight() error {
return self.scroll(self.viewTrait.ScrollRight)
}
func (self *ListContextTrait) scroll(scrollFunc func()) error {
scrollFunc()
return self.HandleFocus()
}
func (self *ListContextTrait) handleLineChange(change int) error {
before := self.list.GetSelectedLineIdx()
self.list.MoveSelectedLine(change)
after := self.list.GetSelectedLineIdx()
// 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 {
return self.HandleFocus()
}
return nil
}
func (self *ListContextTrait) HandlePrevPage() error {
return self.handleLineChange(-self.viewTrait.PageDelta())
}
func (self *ListContextTrait) HandleNextPage() error {
return self.handleLineChange(self.viewTrait.PageDelta())
}
func (self *ListContextTrait) HandleGotoTop() error {
return self.handleLineChange(-self.list.GetItemsLength())
}
func (self *ListContextTrait) HandleGotoBottom() error {
return self.handleLineChange(self.list.GetItemsLength())
}
func (self *ListContextTrait) HandleClick(onClick func() error) error {
prevSelectedLineIdx := self.list.GetSelectedLineIdx()
// because we're handling a click, we need to determine the new line idx based
// on the view itself.
newSelectedLineIdx := self.viewTrait.SelectedLineIdx()
currentContextKey := self.c.CurrentContext().GetKey()
alreadyFocused := currentContextKey == self.base.GetKey()
// we need to focus the view
if !alreadyFocused {
if err := self.takeFocus(); err != nil {
return err
}
}
if newSelectedLineIdx > self.list.GetItemsLength()-1 {
return nil
}
self.list.SetSelectedLineIdx(newSelectedLineIdx)
if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && onClick != nil {
return onClick()
}
return self.HandleFocus()
}
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
self.list.SetSelectedLineIdx(selectedLineIdx)
self.GetList().SetSelectedLineIdx(selectedLineIdx)
return self.HandleFocus()
}
func (self *ListContextTrait) HandleRenderToMain() error {
if self.OnRenderToMain != nil {
return self.OnRenderToMain()
}
return nil
}
func (self *ListContextTrait) keybindings(opts types.KeybindingsOpts) []*types.Binding {
return []*types.Binding{
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.c.Tr.LcPrevPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.c.Tr.LcNextPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.c.Tr.LcGotoTop},
{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }},
{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight},
{
Key: opts.GetKey(opts.Config.Universal.StartSearch),
Handler: func() error { self.c.OpenSearch(); return nil },
Description: self.c.Tr.LcStartSearch,
Tag: "navigation",
},
{
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
Description: self.c.Tr.LcGotoBottom,
Handler: self.HandleGotoBottom,
Tag: "navigation",
},
}
}

View file

@ -0,0 +1,87 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type LocalCommitsContext struct {
*LocalCommitsViewModel
*ViewportListContextTrait
}
var _ types.IListContext = (*LocalCommitsContext)(nil)
func NewLocalCommitsContext(
getModel func() []*models.Commit,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *LocalCommitsContext {
viewModel := NewLocalCommitsViewModel(getModel)
return &LocalCommitsContext{
LocalCommitsViewModel: viewModel,
ViewportListContextTrait: &ViewportListContextTrait{
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "commits",
WindowName: "commits",
Key: BRANCH_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
}},
}
}
func (self *LocalCommitsContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type LocalCommitsViewModel struct {
*traits.ListCursor
getModel func() []*models.Commit
}
func NewLocalCommitsViewModel(getModel func() []*models.Commit) *LocalCommitsViewModel {
self := &LocalCommitsViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *LocalCommitsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *LocalCommitsViewModel) GetSelected() *models.Commit {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,108 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type MenuContext struct {
*MenuViewModel
*ListContextTrait
}
var _ types.IListContext = (*MenuContext)(nil)
func NewMenuContext(
view *gocui.View,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
getOptionsMap func() map[string]string,
) *MenuContext {
viewModel := NewMenuViewModel()
return &MenuContext{
MenuViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "menu",
Key: "menu",
Kind: types.PERSISTENT_POPUP,
OnGetOptionsMap: getOptionsMap,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
getDisplayStrings: viewModel.GetDisplayStrings,
list: viewModel,
viewTrait: NewViewTrait(view),
c: c,
},
}
}
// TODO: remove this thing.
func (self *MenuContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.DisplayString
}
type MenuViewModel struct {
*traits.ListCursor
menuItems []*types.MenuItem
}
func NewMenuViewModel() *MenuViewModel {
self := &MenuViewModel{
menuItems: nil,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *MenuViewModel) GetItemsLength() int {
return len(self.menuItems)
}
func (self *MenuViewModel) GetSelected() *types.MenuItem {
if self.GetItemsLength() == 0 {
return nil
}
return self.menuItems[self.GetSelectedLineIdx()]
}
func (self *MenuViewModel) SetMenuItems(items []*types.MenuItem) {
self.menuItems = items
}
// TODO: move into presentation package
func (self *MenuViewModel) GetDisplayStrings(startIdx int, length int) [][]string {
stringArrays := make([][]string, len(self.menuItems))
for i, item := range self.menuItems {
if item.DisplayStrings == nil {
styledStr := item.DisplayString
if item.OpensMenu {
styledStr = presentation.OpensMenuStyle(styledStr)
}
stringArrays[i] = []string{styledStr}
} else {
stringArrays[i] = item.DisplayStrings
}
}
return stringArrays
}

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type ReflogCommitsContext struct {
*ReflogCommitsViewModel
*ListContextTrait
}
var _ types.IListContext = (*ReflogCommitsContext)(nil)
func NewReflogCommitsContext(
getModel func() []*models.Commit,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *ReflogCommitsContext {
viewModel := NewReflogCommitsViewModel(getModel)
return &ReflogCommitsContext{
ReflogCommitsViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "commits",
WindowName: "commits",
Key: REFLOG_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *ReflogCommitsContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type ReflogCommitsViewModel struct {
*traits.ListCursor
getModel func() []*models.Commit
}
func NewReflogCommitsViewModel(getModel func() []*models.Commit) *ReflogCommitsViewModel {
self := &ReflogCommitsViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *ReflogCommitsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *ReflogCommitsViewModel) GetSelected() *models.Commit {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type RemoteBranchesContext struct {
*RemoteBranchesViewModel
*ListContextTrait
}
var _ types.IListContext = (*RemoteBranchesContext)(nil)
func NewRemoteBranchesContext(
getModel func() []*models.RemoteBranch,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *RemoteBranchesContext {
viewModel := NewRemoteBranchesViewModel(getModel)
return &RemoteBranchesContext{
RemoteBranchesViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: REMOTE_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *RemoteBranchesContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type RemoteBranchesViewModel struct {
*traits.ListCursor
getModel func() []*models.RemoteBranch
}
func NewRemoteBranchesViewModel(getModel func() []*models.RemoteBranch) *RemoteBranchesViewModel {
self := &RemoteBranchesViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *RemoteBranchesViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *RemoteBranchesViewModel) GetSelected() *models.RemoteBranch {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type RemotesContext struct {
*RemotesViewModel
*ListContextTrait
}
var _ types.IListContext = (*RemotesContext)(nil)
func NewRemotesContext(
getModel func() []*models.Remote,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *RemotesContext {
viewModel := NewRemotesViewModel(getModel)
return &RemotesContext{
RemotesViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: REMOTES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *RemotesContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type RemotesViewModel struct {
*traits.ListCursor
getModel func() []*models.Remote
}
func NewRemotesViewModel(getModel func() []*models.Remote) *RemotesViewModel {
self := &RemotesViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *RemotesViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *RemotesViewModel) GetSelected() *models.Remote {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -1,7 +1,6 @@
package gui
package context
import (
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@ -12,10 +11,10 @@ type SimpleContext struct {
// this is for pushing some content to the main view
OnRenderToMain func(opts ...types.OnFocusOpts) error
*context.BaseContext
*BaseContext
}
type NewSimpleContextOpts struct {
type ContextCallbackOpts struct {
OnFocus func(opts ...types.OnFocusOpts) error
OnFocusLost func() error
OnRender func() error
@ -23,7 +22,7 @@ type NewSimpleContextOpts struct {
OnRenderToMain func(opts ...types.OnFocusOpts) error
}
func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpts) *SimpleContext {
func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *SimpleContext {
return &SimpleContext{
OnFocus: opts.OnFocus,
OnFocusLost: opts.OnFocusLost,
@ -35,13 +34,6 @@ func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpt
var _ types.Context = &SimpleContext{}
func (self *SimpleContext) HandleRender() error {
if self.OnRender != nil {
return self.OnRender()
}
return nil
}
func (self *SimpleContext) HandleFocus(opts ...types.OnFocusOpts) error {
if self.OnFocus != nil {
if err := self.OnFocus(opts...); err != nil {
@ -65,6 +57,13 @@ func (self *SimpleContext) HandleFocusLost() error {
return nil
}
func (self *SimpleContext) HandleRender() error {
if self.OnRender != nil {
return self.OnRender()
}
return nil
}
func (self *SimpleContext) HandleRenderToMain() error {
if self.OnRenderToMain != nil {
return self.OnRenderToMain()

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type StashContext struct {
*StashViewModel
*ListContextTrait
}
var _ types.IListContext = (*StashContext)(nil)
func NewStashContext(
getModel func() []*models.StashEntry,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *StashContext {
viewModel := NewStashViewModel(getModel)
return &StashContext{
StashViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "stash",
WindowName: "stash",
Key: STASH_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *StashContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type StashViewModel struct {
*traits.ListCursor
getModel func() []*models.StashEntry
}
func NewStashViewModel(getModel func() []*models.StashEntry) *StashViewModel {
self := &StashViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *StashViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *StashViewModel) GetSelected() *models.StashEntry {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,87 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type SubCommitsContext struct {
*SubCommitsViewModel
*ViewportListContextTrait
}
var _ types.IListContext = (*SubCommitsContext)(nil)
func NewSubCommitsContext(
getModel func() []*models.Commit,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *SubCommitsContext {
viewModel := NewSubCommitsViewModel(getModel)
return &SubCommitsContext{
SubCommitsViewModel: viewModel,
ViewportListContextTrait: &ViewportListContextTrait{
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: SUB_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
}},
}
}
func (self *SubCommitsContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type SubCommitsViewModel struct {
*traits.ListCursor
getModel func() []*models.Commit
}
func NewSubCommitsViewModel(getModel func() []*models.Commit) *SubCommitsViewModel {
self := &SubCommitsViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *SubCommitsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *SubCommitsViewModel) GetSelected() *models.Commit {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,86 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type SubmodulesContext struct {
*SubmodulesViewModel
*ListContextTrait
}
var _ types.IListContext = (*SubmodulesContext)(nil)
func NewSubmodulesContext(
getModel func() []*models.SubmoduleConfig,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *SubmodulesContext {
viewModel := NewSubmodulesViewModel(getModel)
return &SubmodulesContext{
SubmodulesViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "files",
WindowName: "files",
Key: SUBMODULES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *SubmodulesContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.ID()
}
type SubmodulesViewModel struct {
*traits.ListCursor
getModel func() []*models.SubmoduleConfig
}
func NewSubmodulesViewModel(getModel func() []*models.SubmoduleConfig) *SubmodulesViewModel {
self := &SubmodulesViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *SubmodulesViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *SubmodulesViewModel) GetSelected() *models.SubmoduleConfig {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -0,0 +1,85 @@
package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type SuggestionsContext struct {
*SuggestionsViewModel
*ListContextTrait
}
var _ types.IListContext = (*SuggestionsContext)(nil)
func NewSuggestionsContext(
getModel func() []*types.Suggestion,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
onRenderToMain func(...types.OnFocusOpts) error,
onFocusLost func() error,
c *types.ControllerCommon,
) *SuggestionsContext {
viewModel := NewSuggestionsViewModel(getModel)
return &SuggestionsContext{
SuggestionsViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "suggestions",
WindowName: "suggestions",
Key: SUGGESTIONS_CONTEXT_KEY,
Kind: types.PERSISTENT_POPUP,
Focusable: true,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
}
func (self *SuggestionsContext) GetSelectedItemId() string {
item := self.GetSelected()
if item == nil {
return ""
}
return item.Value
}
type SuggestionsViewModel struct {
*traits.ListCursor
getModel func() []*types.Suggestion
}
func NewSuggestionsViewModel(getModel func() []*types.Suggestion) *SuggestionsViewModel {
self := &SuggestionsViewModel{
getModel: getModel,
}
self.ListCursor = traits.NewListCursor(self)
return self
}
func (self *SuggestionsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *SuggestionsViewModel) GetSelected() *types.Suggestion {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -9,7 +9,6 @@ import (
type TagsContext struct {
*TagsViewModel
*BaseContext
*ListContextTrait
}
@ -17,7 +16,7 @@ var _ types.IListContext = (*TagsContext)(nil)
func NewTagsContext(
getModel func() []*models.Tag,
getView func() *gocui.View,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
@ -26,47 +25,32 @@ func NewTagsContext(
c *types.ControllerCommon,
) *TagsContext {
baseContext := NewBaseContext(NewBaseContextOpts{
viewModel := NewTagsViewModel(getModel)
return &TagsContext{
TagsViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: TAGS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})
self := &TagsContext{}
takeFocus := func() error { return c.PushContext(self) }
list := NewTagsViewModel(getModel)
viewTrait := NewViewTrait(getView)
listContextTrait := &ListContextTrait{
base: baseContext,
list: list,
viewTrait: viewTrait,
GetDisplayStrings: getDisplayStrings,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnRenderToMain: onRenderToMain,
OnFocusLost: onFocusLost,
takeFocus: takeFocus,
// TODO: handle this in a trait
RenderSelection: false,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
baseContext.AddKeybindingsFn(listContextTrait.keybindings)
self.BaseContext = baseContext
self.ListContextTrait = listContextTrait
self.TagsViewModel = list
return self
}
func (self *TagsContext) GetSelectedItemId() string {
item := self.GetSelectedTag()
item := self.GetSelected()
if item == nil {
return ""
}
@ -79,18 +63,6 @@ type TagsViewModel struct {
getModel func() []*models.Tag
}
func (self *TagsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *TagsViewModel) GetSelectedTag() *models.Tag {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}
func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel {
self := &TagsViewModel{
getModel: getModel,
@ -100,3 +72,15 @@ func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel {
return self
}
func (self *TagsViewModel) GetItemsLength() int {
return len(self.getModel())
}
func (self *TagsViewModel) GetSelected() *models.Tag {
if self.GetItemsLength() == 0 {
return nil
}
return self.getModel()[self.GetSelectedLineIdx()]
}

View file

@ -2,70 +2,62 @@ package context
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
const HORIZONTAL_SCROLL_FACTOR = 3
type ViewTrait struct {
getView func() *gocui.View
view *gocui.View
}
func NewViewTrait(getView func() *gocui.View) *ViewTrait {
return &ViewTrait{getView: getView}
var _ types.IViewTrait = &ViewTrait{}
func NewViewTrait(view *gocui.View) *ViewTrait {
return &ViewTrait{view: view}
}
func (self *ViewTrait) FocusPoint(yIdx int) {
view := self.getView()
view.FocusPoint(view.OriginX(), yIdx)
self.view.FocusPoint(self.view.OriginX(), yIdx)
}
func (self *ViewTrait) SetViewPortContent(content string) {
view := self.getView()
_, y := view.Origin()
view.OverwriteLines(y, content)
_, y := self.view.Origin()
self.view.OverwriteLines(y, content)
}
func (self *ViewTrait) SetContent(content string) {
self.getView().SetContent(content)
self.view.SetContent(content)
}
func (self *ViewTrait) SetFooter(value string) {
self.getView().Footer = value
self.view.Footer = value
}
func (self *ViewTrait) SetOriginX(value int) {
_ = self.getView().SetOriginX(value)
_ = self.view.SetOriginX(value)
}
// tells us the bounds of line indexes shown in the view currently
func (self *ViewTrait) ViewPortYBounds() (int, int) {
view := self.getView()
_, min := view.Origin()
max := view.InnerHeight() + 1
_, min := self.view.Origin()
max := self.view.InnerHeight() + 1
return min, max
}
func (self *ViewTrait) ScrollLeft() {
view := self.getView()
newOriginX := utils.Max(view.OriginX()-view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0)
_ = view.SetOriginX(newOriginX)
newOriginX := utils.Max(self.view.OriginX()-self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0)
_ = self.view.SetOriginX(newOriginX)
}
func (self *ViewTrait) ScrollRight() {
view := self.getView()
_ = view.SetOriginX(view.OriginX() + view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR)
_ = self.view.SetOriginX(self.view.OriginX() + self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR)
}
// this returns the amount we'll scroll if we want to scroll by a page.
func (self *ViewTrait) PageDelta() int {
view := self.getView()
_, height := view.Size()
_, height := self.view.Size()
delta := height - 1
if delta == 0 {
@ -76,5 +68,5 @@ func (self *ViewTrait) PageDelta() int {
}
func (self *ViewTrait) SelectedLineIdx() int {
return self.getView().SelectedLineIdx()
return self.view.SelectedLineIdx()
}

View file

@ -0,0 +1,22 @@
package context
import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
// This embeds a list context trait and adds logic to re-render the viewport
// whenever a line is focused. We use this in the commits panel because different
// sections of the log graph need to be highlighted depending on the currently selected line
type ViewportListContextTrait struct {
*ListContextTrait
}
func (self *ViewportListContextTrait) FocusLine() {
self.ListContextTrait.FocusLine()
min, max := self.GetViewTrait().ViewPortYBounds()
displayStrings := self.ListContextTrait.getDisplayStrings(min, max)
content := utils.RenderDisplayStrings(displayStrings)
self.GetViewTrait().SetViewPortContent(content)
}

View file

@ -9,7 +9,6 @@ import (
type WorkingTreeContext struct {
*filetree.FileTreeViewModel
*BaseContext
*ListContextTrait
}
@ -17,7 +16,7 @@ var _ types.IListContext = (*WorkingTreeContext)(nil)
func NewWorkingTreeContext(
getModel func() []*models.File,
getView func() *gocui.View,
view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error,
@ -26,43 +25,28 @@ func NewWorkingTreeContext(
c *types.ControllerCommon,
) *WorkingTreeContext {
baseContext := NewBaseContext(NewBaseContextOpts{
viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
return &WorkingTreeContext{
FileTreeViewModel: viewModel,
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
ViewName: "files",
WindowName: "files",
Key: FILES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})
self := &WorkingTreeContext{}
takeFocus := func() error { return c.PushContext(self) }
viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
viewTrait := NewViewTrait(getView)
listContextTrait := &ListContextTrait{
base: baseContext,
list: viewModel,
viewTrait: viewTrait,
GetDisplayStrings: getDisplayStrings,
}), ContextCallbackOpts{
OnFocus: onFocus,
OnRenderToMain: onRenderToMain,
OnFocusLost: onFocusLost,
takeFocus: takeFocus,
// TODO: handle this in a trait
RenderSelection: false,
OnRenderToMain: onRenderToMain,
}),
list: viewModel,
viewTrait: NewViewTrait(view),
getDisplayStrings: getDisplayStrings,
c: c,
},
}
baseContext.AddKeybindingsFn(listContextTrait.keybindings)
self.BaseContext = baseContext
self.ListContextTrait = listContextTrait
self.FileTreeViewModel = viewModel
return self
}
func (self *WorkingTreeContext) GetSelectedItemId() string {

View file

@ -35,7 +35,7 @@ func (gui *Gui) allContexts2() []types.Context {
func (gui *Gui) contextTree() *context.ContextTree {
return &context.ContextTree{
Global: NewSimpleContext(
Global: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.GLOBAL_CONTEXT,
ViewName: "",
@ -43,11 +43,11 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.GLOBAL_CONTEXT_KEY,
Focusable: false,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain),
},
),
Status: NewSimpleContext(
Status: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.SIDE_CONTEXT,
ViewName: "status",
@ -55,7 +55,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.STATUS_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain),
},
),
@ -72,7 +72,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
Tags: gui.tagsListContext(),
Stash: gui.stashListContext(),
Suggestions: gui.suggestionsListContext(),
Normal: NewSimpleContext(
Normal: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.MAIN_CONTEXT,
ViewName: "main",
@ -80,13 +80,13 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.MAIN_NORMAL_CONTEXT_KEY,
Focusable: false,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: func(opts ...types.OnFocusOpts) error {
return nil // TODO: should we do something here? We should allow for scrolling the panel
},
},
),
Staging: NewSimpleContext(
Staging: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.MAIN_CONTEXT,
ViewName: "main",
@ -94,7 +94,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.MAIN_STAGING_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: func(opts ...types.OnFocusOpts) error {
forceSecondaryFocused := false
selectedLineIdx := -1
@ -110,7 +110,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
},
},
),
PatchBuilding: NewSimpleContext(
PatchBuilding: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.MAIN_CONTEXT,
ViewName: "main",
@ -118,7 +118,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.MAIN_PATCH_BUILDING_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: func(opts ...types.OnFocusOpts) error {
selectedLineIdx := -1
if len(opts) > 0 && (opts[0].ClickedViewName == "main" || opts[0].ClickedViewName == "secondary") {
@ -129,7 +129,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
},
},
),
Merging: NewSimpleContext(
Merging: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.MAIN_CONTEXT,
ViewName: "main",
@ -138,11 +138,11 @@ func (gui *Gui) contextTree() *context.ContextTree {
OnGetOptionsMap: gui.getMergingOptions,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: OnFocusWrapper(func() error { return gui.renderConflictsWithLock(true) }),
},
),
Credentials: NewSimpleContext(
Credentials: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.PERSISTENT_POPUP,
ViewName: "credentials",
@ -150,11 +150,11 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.CREDENTIALS_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: OnFocusWrapper(gui.handleAskFocused),
},
),
Confirmation: NewSimpleContext(
Confirmation: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.TEMPORARY_POPUP,
ViewName: "confirmation",
@ -162,11 +162,11 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.CONFIRMATION_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: OnFocusWrapper(gui.handleAskFocused),
},
),
CommitMessage: NewSimpleContext(
CommitMessage: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.PERSISTENT_POPUP,
ViewName: "commitMessage",
@ -174,11 +174,11 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.COMMIT_MESSAGE_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocus: OnFocusWrapper(gui.handleCommitMessageFocused),
},
),
Search: NewSimpleContext(
Search: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.PERSISTENT_POPUP,
ViewName: "search",
@ -186,9 +186,9 @@ func (gui *Gui) contextTree() *context.ContextTree {
Key: context.SEARCH_CONTEXT_KEY,
Focusable: true,
}),
NewSimpleContextOpts{},
context.ContextCallbackOpts{},
),
CommandLog: NewSimpleContext(
CommandLog: context.NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.EXTRAS_CONTEXT,
ViewName: "extras",
@ -197,7 +197,7 @@ func (gui *Gui) contextTree() *context.ContextTree {
OnGetOptionsMap: gui.getMergingOptions,
Focusable: true,
}),
NewSimpleContextOpts{
context.ContextCallbackOpts{
OnFocusLost: func() error {
gui.Views.Extras.Autoscroll = true
return nil

View file

@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@ -14,11 +15,10 @@ type BisectController struct {
baseController
c *types.ControllerCommon
context types.IListContext
context *context.LocalCommitsContext
git *commands.GitCommand
bisectHelper *BisectHelper
getSelectedLocalCommit func() *models.Commit
getCommits func() []*models.Commit
}
@ -26,11 +26,10 @@ var _ types.IController = &BisectController{}
func NewBisectController(
c *types.ControllerCommon,
context types.IListContext,
context *context.LocalCommitsContext,
git *commands.GitCommand,
bisectHelper *BisectHelper,
getSelectedLocalCommit func() *models.Commit,
getCommits func() []*models.Commit,
) *BisectController {
return &BisectController{
@ -40,7 +39,6 @@ func NewBisectController(
git: git,
bisectHelper: bisectHelper,
getSelectedLocalCommit: getSelectedLocalCommit,
getCommits: getCommits,
}
}
@ -234,7 +232,7 @@ func (self *BisectController) selectCurrentBisectCommit() {
// find index of commit with that sha, move cursor to that.
for i, commit := range self.getCommits() {
if commit.Sha == info.GetCurrentSha() {
self.context.GetPanelState().SetSelectedLineIdx(i)
self.context.SetSelectedLineIdx(i)
_ = self.context.HandleFocus()
break
}
@ -244,7 +242,7 @@ func (self *BisectController) selectCurrentBisectCommit() {
func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error {
return func() error {
commit := self.getSelectedLocalCommit()
commit := self.context.GetSelected()
if commit == nil {
return nil
}

View file

@ -94,10 +94,10 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
Handler: self.checkSelectedFileNode(self.press),
Description: self.c.Tr.LcToggleStaged,
},
{
Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.checkSelectedFileNode(self.press)) },
},
// {
// Key: gocui.MouseLeft,
// Handler: func() error { return self.context.HandleClick(self.checkSelectedFileNode(self.press)) },
// },
{
Key: opts.GetKey("<c-b>"), // TODO: softcode
Handler: self.handleStatusFilterPressed,

View file

@ -0,0 +1,144 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type ListControllerFactory struct {
c *types.ControllerCommon
}
func NewListControllerFactory(c *types.ControllerCommon) *ListControllerFactory {
return &ListControllerFactory{
c: c,
}
}
func (self *ListControllerFactory) Create(context types.IListContext) *ListController {
return &ListController{
baseController: baseController{},
c: self.c,
context: context,
}
}
type ListController struct {
baseController
c *types.ControllerCommon
context types.IListContext
}
func (self *ListController) Context() types.Context {
return self.context
}
func (self *ListController) HandlePrevLine() error {
return self.handleLineChange(-1)
}
func (self *ListController) HandleNextLine() error {
return self.handleLineChange(1)
}
func (self *ListController) HandleScrollLeft() error {
return self.scroll(self.context.GetViewTrait().ScrollLeft)
}
func (self *ListController) HandleScrollRight() error {
return self.scroll(self.context.GetViewTrait().ScrollRight)
}
func (self *ListController) scroll(scrollFunc func()) error {
scrollFunc()
return self.context.HandleFocus()
}
func (self *ListController) handleLineChange(change int) error {
before := self.context.GetList().GetSelectedLineIdx()
self.context.GetList().MoveSelectedLine(change)
after := self.context.GetList().GetSelectedLineIdx()
// 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 {
return self.context.HandleFocus()
}
return nil
}
func (self *ListController) HandlePrevPage() error {
return self.handleLineChange(-self.context.GetViewTrait().PageDelta())
}
func (self *ListController) HandleNextPage() error {
return self.handleLineChange(self.context.GetViewTrait().PageDelta())
}
func (self *ListController) HandleGotoTop() error {
return self.handleLineChange(-self.context.GetList().GetItemsLength())
}
func (self *ListController) HandleGotoBottom() error {
return self.handleLineChange(self.context.GetList().GetItemsLength())
}
func (self *ListController) HandleClick(onClick func() error) error {
prevSelectedLineIdx := self.context.GetList().GetSelectedLineIdx()
// because we're handling a click, we need to determine the new line idx based
// on the view itself.
newSelectedLineIdx := self.context.GetViewTrait().SelectedLineIdx()
currentContextKey := self.c.CurrentContext().GetKey()
alreadyFocused := currentContextKey == self.context.GetKey()
// we need to focus the view
if !alreadyFocused {
if err := self.c.PushContext(self.context); err != nil {
return err
}
}
if newSelectedLineIdx > self.context.GetList().GetItemsLength()-1 {
return nil
}
self.context.GetList().SetSelectedLineIdx(newSelectedLineIdx)
if prevSelectedLineIdx == newSelectedLineIdx && alreadyFocused && onClick != nil {
return onClick()
}
return self.context.HandleFocus()
}
func (self *ListController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
return []*types.Binding{
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.c.Tr.LcPrevPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.c.Tr.LcNextPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.c.Tr.LcGotoTop},
{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }},
{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight},
{
Key: opts.GetKey(opts.Config.Universal.StartSearch),
Handler: func() error { self.c.OpenSearch(); return nil },
Description: self.c.Tr.LcStartSearch,
Tag: "navigation",
},
{
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
Description: self.c.Tr.LcGotoBottom,
Handler: self.HandleGotoBottom,
Tag: "navigation",
},
}
}

View file

@ -8,6 +8,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -24,7 +25,7 @@ type (
type LocalCommitsController struct {
baseController
c *types.ControllerCommon
context types.IListContext
context *context.LocalCommitsContext
os *oscommands.OSCommand
git *commands.GitCommand
tagsHelper *TagsHelper
@ -32,9 +33,7 @@ type LocalCommitsController struct {
cherryPickHelper *CherryPickHelper
rebaseHelper *RebaseHelper
getSelectedLocalCommit func() *models.Commit
model *types.Model
getSelectedLocalCommitIdx func() int
CheckMergeOrRebase CheckMergeOrRebase
pullFiles PullFilesFn
getHostingServiceMgr GetHostingServiceMgrFn
@ -49,16 +48,14 @@ var _ types.IController = &LocalCommitsController{}
func NewLocalCommitsController(
c *types.ControllerCommon,
context types.IListContext,
context *context.LocalCommitsContext,
os *oscommands.OSCommand,
git *commands.GitCommand,
tagsHelper *TagsHelper,
refsHelper IRefsHelper,
cherryPickHelper *CherryPickHelper,
rebaseHelper *RebaseHelper,
getSelectedLocalCommit func() *models.Commit,
model *types.Model,
getSelectedLocalCommitIdx func() int,
CheckMergeOrRebase CheckMergeOrRebase,
pullFiles PullFilesFn,
getHostingServiceMgr GetHostingServiceMgrFn,
@ -78,9 +75,7 @@ func NewLocalCommitsController(
refsHelper: refsHelper,
cherryPickHelper: cherryPickHelper,
rebaseHelper: rebaseHelper,
getSelectedLocalCommit: getSelectedLocalCommit,
model: model,
getSelectedLocalCommitIdx: getSelectedLocalCommitIdx,
CheckMergeOrRebase: CheckMergeOrRebase,
pullFiles: pullFiles,
getHostingServiceMgr: getHostingServiceMgr,
@ -194,10 +189,10 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
Description: self.c.Tr.LcGotoBottom,
Tag: "navigation",
},
{
Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
},
// {
// Key: gocui.MouseLeft,
// Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
// },
}
for _, binding := range outsideFilterModeBindings {
@ -316,7 +311,7 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
InitialContent: message,
HandleConfirm: func(response string) error {
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
if err := self.git.Rebase.RewordCommit(self.model.Commits, self.getSelectedLocalCommitIdx(), response); err != nil {
if err := self.git.Rebase.RewordCommit(self.model.Commits, self.context.GetSelectedLineIdx(), response); err != nil {
return self.c.Error(err)
}
@ -336,7 +331,7 @@ func (self *LocalCommitsController) rewordEditor() error {
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
subProcess, err := self.git.Rebase.RewordCommitInEditor(
self.model.Commits, self.getSelectedLocalCommitIdx(),
self.model.Commits, self.context.GetSelectedLineIdx(),
)
if err != nil {
return self.c.Error(err)
@ -399,7 +394,7 @@ func (self *LocalCommitsController) pick() error {
}
func (self *LocalCommitsController) interactiveRebase(action string) error {
err := self.git.Rebase.InteractiveRebase(self.model.Commits, self.getSelectedLocalCommitIdx(), action)
err := self.git.Rebase.InteractiveRebase(self.model.Commits, self.context.GetSelectedLineIdx(), action)
return self.CheckMergeOrRebase(err)
}
@ -407,7 +402,7 @@ func (self *LocalCommitsController) interactiveRebase(action string) error {
// commit meaning you are trying to edit the todo file rather than actually
// begin a rebase. It then updates the todo file with that action
func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool, error) {
selectedCommit := self.getSelectedLocalCommit()
selectedCommit := self.context.GetSelected()
if selectedCommit.Status != "rebasing" {
return false, nil
}
@ -427,7 +422,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool,
)
if err := self.git.Rebase.EditRebaseTodo(
self.getSelectedLocalCommitIdx(), action,
self.context.GetSelectedLineIdx(), action,
); err != nil {
return false, self.c.Error(err)
}
@ -438,7 +433,7 @@ func (self *LocalCommitsController) handleMidRebaseCommand(action string) (bool,
}
func (self *LocalCommitsController) handleCommitMoveDown() error {
index := self.context.GetPanelState().GetSelectedLineIdx()
index := self.context.GetSelectedLineIdx()
commits := self.model.Commits
selectedCommit := self.model.Commits[index]
if selectedCommit.Status == "rebasing" {
@ -454,8 +449,7 @@ func (self *LocalCommitsController) handleCommitMoveDown() error {
if err := self.git.Rebase.MoveTodoDown(index); err != nil {
return self.c.Error(err)
}
// TODO: use MoveSelectedLine
_ = self.context.HandleNextLine()
self.context.MoveSelectedLine(1)
return self.c.Refresh(types.RefreshOptions{
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
})
@ -465,8 +459,7 @@ func (self *LocalCommitsController) handleCommitMoveDown() error {
self.c.LogAction(self.c.Tr.Actions.MoveCommitDown)
err := self.git.Rebase.MoveCommitDown(self.model.Commits, index)
if err == nil {
// TODO: use MoveSelectedLine
_ = self.context.HandleNextLine()
self.context.MoveSelectedLine(1)
}
return self.CheckMergeOrRebase(err)
})
@ -491,7 +484,7 @@ func (self *LocalCommitsController) handleCommitMoveUp() error {
if err := self.git.Rebase.MoveTodoDown(index - 1); err != nil {
return self.c.Error(err)
}
_ = self.context.HandlePrevLine()
self.context.MoveSelectedLine(-1)
return self.c.Refresh(types.RefreshOptions{
Mode: types.SYNC, Scope: []types.RefreshableView{types.REBASE_COMMITS},
})
@ -501,7 +494,7 @@ func (self *LocalCommitsController) handleCommitMoveUp() error {
self.c.LogAction(self.c.Tr.Actions.MoveCommitUp)
err := self.git.Rebase.MoveCommitDown(self.model.Commits, index-1)
if err == nil {
_ = self.context.HandlePrevLine()
self.context.MoveSelectedLine(-1)
}
return self.CheckMergeOrRebase(err)
})
@ -514,7 +507,7 @@ func (self *LocalCommitsController) handleCommitAmendTo() error {
HandleConfirm: func() error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func() error {
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
err := self.git.Rebase.AmendTo(self.getSelectedLocalCommit().Sha)
err := self.git.Rebase.AmendTo(self.context.GetSelected().Sha)
return self.CheckMergeOrRebase(err)
})
},
@ -569,7 +562,7 @@ func (self *LocalCommitsController) createRevertMergeCommitMenu(commit *models.C
}
func (self *LocalCommitsController) afterRevertCommit() error {
_ = self.context.HandleNextLine()
self.context.MoveSelectedLine(1)
return self.c.Refresh(types.RefreshOptions{
Mode: types.BLOCK_UI, Scope: []types.RefreshableView{types.COMMITS, types.BRANCHES},
})
@ -669,7 +662,7 @@ func (self *LocalCommitsController) gotoBottom() error {
}
}
_ = self.context.HandleGotoBottom()
self.context.SetSelectedLineIdx(self.context.GetItemsLength() - 1)
return nil
}
@ -791,7 +784,7 @@ func (self *LocalCommitsController) handleOpenCommitInBrowser(commit *models.Com
func (self *LocalCommitsController) checkSelected(callback func(*models.Commit) error) func() error {
return func() error {
commit := self.getSelectedLocalCommit()
commit := self.context.GetSelected()
if commit == nil {
return nil
}
@ -813,7 +806,7 @@ func (self *LocalCommitsController) copy(commit *models.Commit) error {
}
func (self *LocalCommitsController) copyRange(*models.Commit) error {
return self.cherryPickHelper.CopyRange(self.context.GetPanelState().GetSelectedLineIdx(), self.model.Commits, self.context)
return self.cherryPickHelper.CopyRange(self.context.GetSelectedLineIdx(), self.model.Commits, self.context)
}
func (self *LocalCommitsController) paste() error {

View file

@ -1,7 +1,7 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@ -9,24 +9,20 @@ type MenuController struct {
baseController
c *types.ControllerCommon
context types.IListContext
getSelectedMenuItem func() *types.MenuItem
context *context.MenuContext
}
var _ types.IController = &MenuController{}
func NewMenuController(
c *types.ControllerCommon,
context types.IListContext,
getSelectedMenuItem func() *types.MenuItem,
context *context.MenuContext,
) *MenuController {
return &MenuController{
baseController: baseController{},
c: c,
context: context,
getSelectedMenuItem: getSelectedMenuItem,
}
}
@ -44,17 +40,17 @@ func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types.
Key: opts.GetKey(opts.Config.Universal.ConfirmAlt1),
Handler: self.press,
},
{
Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.press) },
},
// {
// Key: gocui.MouseLeft,
// Handler: func() error { return self.context.HandleClick(self.press) },
// },
}
return bindings
}
func (self *MenuController) press() error {
selectedItem := self.getSelectedMenuItem()
selectedItem := self.context.GetSelected()
if err := self.c.PopContext(); err != nil {
return err

View file

@ -1,7 +1,6 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
@ -13,10 +12,9 @@ type RemotesController struct {
baseController
c *types.ControllerCommon
context types.IListContext
context *context.RemotesContext
git *commands.GitCommand
getSelectedRemote func() *models.Remote
setRemoteBranches func([]*models.RemoteBranch)
contexts *context.ContextTree
}
@ -25,10 +23,9 @@ var _ types.IController = &RemotesController{}
func NewRemotesController(
c *types.ControllerCommon,
context types.IListContext,
context *context.RemotesContext,
git *commands.GitCommand,
contexts *context.ContextTree,
getSelectedRemote func() *models.Remote,
setRemoteBranches func([]*models.RemoteBranch),
) *RemotesController {
return &RemotesController{
@ -37,7 +34,6 @@ func NewRemotesController(
git: git,
contexts: contexts,
context: context,
getSelectedRemote: getSelectedRemote,
setRemoteBranches: setRemoteBranches,
}
}
@ -48,10 +44,10 @@ func (self *RemotesController) GetKeybindings(opts types.KeybindingsOpts) []*typ
Key: opts.GetKey(opts.Config.Universal.GoInto),
Handler: self.checkSelected(self.enter),
},
{
Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
},
// {
// Key: gocui.MouseLeft,
// Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
// },
{
Key: opts.GetKey(opts.Config.Branches.FetchRemote),
Handler: self.checkSelected(self.fetch),
@ -183,7 +179,7 @@ func (self *RemotesController) fetch(remote *models.Remote) error {
func (self *RemotesController) checkSelected(callback func(*models.Remote) error) func() error {
return func() error {
file := self.getSelectedRemote()
file := self.context.GetSelected()
if file == nil {
return nil
}

View file

@ -5,9 +5,9 @@ import (
"path/filepath"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
@ -16,21 +16,19 @@ type SubmodulesController struct {
baseController
c *types.ControllerCommon
context types.IListContext
context *context.SubmodulesContext
git *commands.GitCommand
enterSubmodule func(submodule *models.SubmoduleConfig) error
getSelectedSubmodule func() *models.SubmoduleConfig
}
var _ types.IController = &SubmodulesController{}
func NewSubmodulesController(
c *types.ControllerCommon,
context types.IListContext,
context *context.SubmodulesContext,
git *commands.GitCommand,
enterSubmodule func(submodule *models.SubmoduleConfig) error,
getSelectedSubmodule func() *models.SubmoduleConfig,
) *SubmodulesController {
return &SubmodulesController{
baseController: baseController{},
@ -38,7 +36,6 @@ func NewSubmodulesController(
context: context,
git: git,
enterSubmodule: enterSubmodule,
getSelectedSubmodule: getSelectedSubmodule,
}
}
@ -80,10 +77,10 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []*
Description: self.c.Tr.LcViewBulkSubmoduleOptions,
OpensMenu: true,
},
{
Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
},
// {
// Key: gocui.MouseLeft,
// Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) },
// },
}
}
@ -230,7 +227,7 @@ func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) erro
func (self *SubmodulesController) checkSelected(callback func(*models.SubmoduleConfig) error) func() error {
return func() error {
submodule := self.getSelectedSubmodule()
submodule := self.context.GetSelected()
if submodule == nil {
return nil
}

View file

@ -158,7 +158,7 @@ func (self *TagsController) create() error {
func (self *TagsController) withSelectedTag(f func(tag *models.Tag) error) func() error {
return func() error {
tag := self.context.GetSelectedTag()
tag := self.context.GetSelected()
if tag == nil {
return nil
}

View file

@ -44,16 +44,16 @@ func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (s
objects := CustomCommandObjects{
SelectedFile: gui.getSelectedFile(),
SelectedPath: gui.getSelectedPath(),
SelectedLocalCommit: gui.getSelectedLocalCommit(),
SelectedReflogCommit: gui.getSelectedReflogCommit(),
SelectedLocalBranch: gui.getSelectedBranch(),
SelectedRemoteBranch: gui.getSelectedRemoteBranch(),
SelectedRemote: gui.getSelectedRemote(),
SelectedTag: gui.State.Contexts.Tags.GetSelectedTag(),
SelectedStashEntry: gui.getSelectedStashEntry(),
SelectedLocalCommit: gui.State.Contexts.BranchCommits.GetSelected(),
SelectedReflogCommit: gui.State.Contexts.ReflogCommits.GetSelected(),
SelectedLocalBranch: gui.State.Contexts.Branches.GetSelected(),
SelectedRemoteBranch: gui.State.Contexts.RemoteBranches.GetSelected(),
SelectedRemote: gui.State.Contexts.Remotes.GetSelected(),
SelectedTag: gui.State.Contexts.Tags.GetSelected(),
SelectedStashEntry: gui.State.Contexts.Stash.GetSelected(),
SelectedCommitFile: gui.getSelectedCommitFile(),
SelectedCommitFilePath: gui.getSelectedCommitFilePath(),
SelectedSubCommit: gui.getSelectedSubCommit(),
SelectedSubCommit: gui.State.Contexts.SubCommits.GetSelected(),
CheckedOutBranch: gui.getCheckedOutBranch(),
PromptResponses: promptResponses,
}

View file

@ -43,7 +43,7 @@ func (gui *Gui) currentDiffTerminals() []string {
return []string{gui.State.Contexts.CommitFiles.GetRefName()}
case context.LOCAL_BRANCHES_CONTEXT_KEY:
// for our local branches we want to include both the branch and its upstream
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch != nil {
names := []string{branch.ID()}
if branch.IsTrackingRemote() {

View file

@ -16,7 +16,7 @@ func (gui *Gui) handleCreateFilteringMenuPanel() error {
fileName = node.GetPath()
}
case gui.State.Contexts.CommitFiles:
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node != nil {
fileName = node.GetPath()
}

View file

@ -8,7 +8,7 @@ import (
)
func (gui *Gui) handleCreateGitFlowMenu() error {
branch := gui.getSelectedBranch()
branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil
}

View file

@ -179,11 +179,11 @@ type GuiRepoState struct {
// Suggestions will sometimes appear when typing into a prompt
Suggestions []*types.Suggestion
MenuItems []*types.MenuItem
Updating bool
Panels *panelStates
SplitMainPanel bool
LimitCommits bool
IsRefreshingFiles bool
Searching searchingState
@ -253,68 +253,11 @@ type MergingPanelState struct {
UserVerticalScrolling bool
}
// TODO: consider splitting this out into the window and the branches view
type branchPanelState struct {
listPanelState
}
type remotePanelState struct {
listPanelState
}
type remoteBranchesState struct {
listPanelState
}
type commitPanelState struct {
listPanelState
LimitCommits bool
}
type reflogCommitPanelState struct {
listPanelState
}
type subCommitPanelState struct {
listPanelState
// e.g. name of branch whose commits we're looking at
refName string
}
type stashPanelState struct {
listPanelState
}
type menuPanelState struct {
listPanelState
OnPress func() error
}
type submodulePanelState struct {
listPanelState
}
type suggestionsPanelState struct {
listPanelState
}
// as we move things to the new context approach we're going to eventually
// remove this struct altogether and store this state on the contexts.
type panelStates struct {
Branches *branchPanelState
Remotes *remotePanelState
RemoteBranches *remoteBranchesState
Commits *commitPanelState
ReflogCommits *reflogCommitPanelState
SubCommits *subCommitPanelState
Stash *stashPanelState
Menu *menuPanelState
LineByLine *LblPanelState
Merging *MergingPanelState
Submodules *submodulePanelState
Suggestions *suggestionsPanelState
}
type Views struct {
@ -449,22 +392,12 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
},
Panels: &panelStates{
// TODO: work out why some of these are -1 and some are 0. Last time I checked there was a good reason but I'm less certain now
Submodules: &submodulePanelState{listPanelState{SelectedLineIdx: -1}},
Branches: &branchPanelState{listPanelState{SelectedLineIdx: 0}},
Remotes: &remotePanelState{listPanelState{SelectedLineIdx: 0}},
RemoteBranches: &remoteBranchesState{listPanelState{SelectedLineIdx: -1}},
Commits: &commitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, LimitCommits: true},
ReflogCommits: &reflogCommitPanelState{listPanelState{SelectedLineIdx: 0}},
SubCommits: &subCommitPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, refName: ""},
Stash: &stashPanelState{listPanelState{SelectedLineIdx: -1}},
Menu: &menuPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}, OnPress: nil},
Suggestions: &suggestionsPanelState{listPanelState: listPanelState{SelectedLineIdx: 0}},
Merging: &MergingPanelState{
State: mergeconflicts.NewState(),
UserVerticalScrolling: false,
},
},
LimitCommits: true,
Ptmx: nil,
Modes: Modes{
Filtering: filtering.New(filterPath),
@ -584,7 +517,7 @@ func (gui *Gui) resetControllers() {
controllerCommon,
gui.git,
gui.State.Contexts,
func() { gui.State.Panels.Commits.LimitCommits = true },
func() { gui.State.LimitCommits = true },
),
Bisect: controllers.NewBisectHelper(controllerCommon, gui.git),
Suggestions: controllers.NewSuggestionsHelper(controllerCommon, model, gui.refreshSuggestions),
@ -615,7 +548,6 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.Submodules,
gui.git,
gui.enterSubmodule,
gui.getSelectedSubmodule,
)
bisectController := controllers.NewBisectController(
@ -623,7 +555,6 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.BranchCommits,
gui.git,
gui.helpers.Bisect,
gui.getSelectedLocalCommit,
func() []*models.Commit { return gui.State.Model.Commits },
)
@ -672,15 +603,13 @@ func (gui *Gui) resetControllers() {
gui.helpers.Refs,
gui.helpers.CherryPick,
gui.helpers.Rebase,
gui.getSelectedLocalCommit,
model,
func() int { return gui.State.Panels.Commits.SelectedLineIdx },
gui.helpers.Rebase.CheckMergeOrRebase,
syncController.HandlePull,
gui.getHostingServiceMgr,
gui.SwitchToCommitFilesContext,
func() bool { return gui.State.Panels.Commits.LimitCommits },
func(value bool) { gui.State.Panels.Commits.LimitCommits = value },
func() bool { return gui.State.LimitCommits },
func(value bool) { gui.State.LimitCommits = value },
func() bool { return gui.ShowWholeGitGraph },
func(value bool) { gui.ShowWholeGitGraph = value },
),
@ -689,13 +618,11 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.Remotes,
gui.git,
gui.State.Contexts,
gui.getSelectedRemote,
func(branches []*models.RemoteBranch) { gui.State.Model.RemoteBranches = branches },
),
Menu: controllers.NewMenuController(
controllerCommon,
gui.State.Contexts.Menu,
gui.getSelectedMenuItem,
),
Undo: controllers.NewUndoController(
controllerCommon,
@ -714,6 +641,11 @@ func (gui *Gui) resetControllers() {
controllers.AttachControllers(gui.State.Contexts.Remotes, gui.Controllers.Remotes)
controllers.AttachControllers(gui.State.Contexts.Menu, gui.Controllers.Menu)
controllers.AttachControllers(gui.State.Contexts.Global, gui.Controllers.Sync, gui.Controllers.Undo, gui.Controllers.Global)
listControllerFactory := controllers.NewListControllerFactory(gui.c)
for _, context := range gui.getListContexts() {
controllers.AttachControllers(context, listControllerFactory.Create(context))
}
}
var RuneReplacements = map[rune]string{

View file

@ -1,267 +0,0 @@
package gui
import (
"fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type ListContext struct {
GetItemsLength func() int
GetDisplayStrings func(startIdx int, length int) [][]string
OnFocus func(...types.OnFocusOpts) error
OnRenderToMain func(...types.OnFocusOpts) error
OnFocusLost func() error
OnGetSelectedItemId func() string
OnGetPanelState func() types.IListPanelState
// if this is true, we'll call GetDisplayStrings for just the visible part of the
// view and re-render that. This is useful when you need to render different
// content based on the selection (e.g. for showing the selected commit)
RenderSelection bool
Gui *Gui
*context.BaseContext
}
var _ types.IListContext = &ListContext{}
func (self *ListContext) GetPanelState() types.IListPanelState {
return self.OnGetPanelState()
}
func (self *ListContext) FocusLine() {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
// ignoring error for now
return
}
// we need a way of knowing whether we've rendered to the view yet.
view.FocusPoint(view.OriginX(), self.GetPanelState().GetSelectedLineIdx())
if self.RenderSelection {
_, originY := view.Origin()
displayStrings := self.GetDisplayStrings(originY, view.InnerHeight()+1)
self.Gui.renderDisplayStringsInViewPort(view, displayStrings)
}
view.Footer = formatListFooter(self.GetPanelState().GetSelectedLineIdx(), self.GetItemsLength())
}
func formatListFooter(selectedLineIdx int, length int) string {
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
}
func (self *ListContext) GetSelectedItemId() string {
return self.OnGetSelectedItemId()
}
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
func (self *ListContext) HandleRender() error {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
if self.GetDisplayStrings != nil {
self.Gui.refreshSelectedLine(self.GetPanelState(), self.GetItemsLength())
self.Gui.renderDisplayStrings(view, self.GetDisplayStrings(0, self.GetItemsLength()))
self.Gui.render()
}
return nil
}
func (self *ListContext) HandleFocusLost() error {
if self.OnFocusLost != nil {
return self.OnFocusLost()
}
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
_ = view.SetOriginX(0)
return nil
}
func (self *ListContext) HandleFocus(opts ...types.OnFocusOpts) error {
self.FocusLine()
if self.OnFocus != nil {
if err := self.OnFocus(opts...); err != nil {
return err
}
}
if self.OnRenderToMain != nil {
if err := self.OnRenderToMain(opts...); err != nil {
return err
}
}
return nil
}
func (self *ListContext) HandlePrevLine() error {
return self.handleLineChange(-1)
}
func (self *ListContext) HandleNextLine() error {
return self.handleLineChange(1)
}
func (self *ListContext) HandleScrollLeft() error {
return self.scroll(self.Gui.scrollLeft)
}
func (self *ListContext) HandleScrollRight() error {
return self.scroll(self.Gui.scrollRight)
}
func (self *ListContext) scroll(scrollFunc func(*gocui.View)) error {
if self.ignoreKeybinding() {
return nil
}
// get the view, move the origin
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
scrollFunc(view)
return self.HandleFocus()
}
func (self *ListContext) ignoreKeybinding() bool {
return !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused()
}
func (self *ListContext) handleLineChange(change int) error {
if self.ignoreKeybinding() {
return nil
}
selectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == self.GetItemsLength()-1) {
return nil
}
self.Gui.changeSelectedLine(self.GetPanelState(), self.GetItemsLength(), change)
return self.HandleFocus()
}
func (self *ListContext) HandleNextPage() error {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
delta := self.Gui.pageDelta(view)
return self.handleLineChange(delta)
}
func (self *ListContext) HandleGotoTop() error {
return self.handleLineChange(-self.GetItemsLength())
}
func (self *ListContext) HandleGotoBottom() error {
return self.handleLineChange(self.GetItemsLength())
}
func (self *ListContext) HandlePrevPage() error {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
delta := self.Gui.pageDelta(view)
return self.handleLineChange(-delta)
}
func (self *ListContext) HandleClick(onClick func() error) error {
if self.ignoreKeybinding() {
return nil
}
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
prevSelectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
newSelectedLineIdx := view.SelectedLineIdx()
// we need to focus the view
if err := self.Gui.c.PushContext(self); err != nil {
return err
}
if newSelectedLineIdx > self.GetItemsLength()-1 {
return nil
}
self.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx)
prevViewName := self.Gui.currentViewName()
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == self.ViewName && onClick != nil {
return onClick()
}
return self.HandleFocus()
}
func (self *ListContext) OnSearchSelect(selectedLineIdx int) error {
self.GetPanelState().SetSelectedLineIdx(selectedLineIdx)
return self.HandleFocus()
}
func (self *ListContext) HandleRenderToMain() error {
if self.OnRenderToMain != nil {
return self.OnRenderToMain()
}
return nil
}
func (self *ListContext) attachKeybindings() *ListContext {
self.BaseContext.AddKeybindingsFn(self.keybindings)
return self
}
func (self *ListContext) keybindings(opts types.KeybindingsOpts) []*types.Binding {
return []*types.Binding{
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevItem), Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: self.HandlePrevLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextItem), Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.PrevPage), Modifier: gocui.ModNone, Handler: self.HandlePrevPage, Description: self.Gui.c.Tr.LcPrevPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.NextPage), Modifier: gocui.ModNone, Handler: self.HandleNextPage, Description: self.Gui.c.Tr.LcNextPage},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.GotoTop), Modifier: gocui.ModNone, Handler: self.HandleGotoTop, Description: self.Gui.c.Tr.LcGotoTop},
{Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: func() error { return self.HandleClick(nil) }},
{Tag: "navigation", Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: self.HandleNextLine},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollLeft), Modifier: gocui.ModNone, Handler: self.HandleScrollLeft},
{Tag: "navigation", Key: opts.GetKey(opts.Config.Universal.ScrollRight), Modifier: gocui.ModNone, Handler: self.HandleScrollRight},
{
Key: opts.GetKey(opts.Config.Universal.StartSearch),
Handler: func() error { return self.Gui.handleOpenSearch(self.GetViewName()) },
Description: self.Gui.c.Tr.LcStartSearch,
Tag: "navigation",
},
{
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
Description: self.Gui.c.Tr.LcGotoBottom,
Handler: self.HandleGotoBottom,
Tag: "navigation",
},
}
}

View file

@ -3,7 +3,6 @@ package gui
import (
"log"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
@ -12,27 +11,21 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
func (gui *Gui) menuListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "menu",
Key: "menu",
Kind: types.PERSISTENT_POPUP,
OnGetOptionsMap: gui.getMenuOptions,
Focusable: true,
}),
GetItemsLength: func() int { return gui.Views.Menu.LinesHeight() },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Menu },
Gui: gui,
// no GetDisplayStrings field because we do a custom render on menu creation
}).attachKeybindings()
func (gui *Gui) menuListContext() *context.MenuContext {
return context.NewMenuContext(
gui.Views.Menu,
nil,
nil,
nil,
gui.c,
gui.getMenuOptions,
)
}
func (gui *Gui) filesListContext() *context.WorkingTreeContext {
return context.NewWorkingTreeContext(
func() []*models.File { return gui.State.Model.Files },
func() *gocui.View { return gui.Views.Files },
gui.Views.Files,
func(startIdx int, length int) [][]string {
lines := presentation.RenderFileTree(gui.State.Contexts.Files.FileTreeViewModel, gui.State.Modes.Diffing.Ref, gui.State.Model.Submodules)
mappedLines := make([][]string, len(lines))
@ -49,82 +42,46 @@ func (gui *Gui) filesListContext() *context.WorkingTreeContext {
)
}
func (gui *Gui) branchesListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: context.LOCAL_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.Branches) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Branches },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) branchesListContext() *context.BranchesContext {
return context.NewBranchesContext(
func() []*models.Branch { return gui.State.Model.Branches },
gui.Views.Branches,
func(startIdx int, length int) [][]string {
return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedBranch()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) remotesListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: context.REMOTES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.Remotes) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Remotes },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) remotesListContext() *context.RemotesContext {
return context.NewRemotesContext(
func() []*models.Remote { return gui.State.Model.Remotes },
gui.Views.Branches,
func(startIdx int, length int) [][]string {
return presentation.GetRemoteListDisplayStrings(gui.State.Model.Remotes, gui.State.Modes.Diffing.Ref)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedRemote()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) remoteBranchesListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: context.REMOTE_BRANCHES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.RemoteBranches) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.RemoteBranches },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) remoteBranchesListContext() *context.RemoteBranchesContext {
return context.NewRemoteBranchesContext(
func() []*models.RemoteBranch { return gui.State.Model.RemoteBranches },
gui.Views.Branches,
func(startIdx int, length int) [][]string {
return presentation.GetRemoteBranchListDisplayStrings(gui.State.Model.RemoteBranches, gui.State.Modes.Diffing.Ref)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedRemoteBranch()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) withDiffModeCheck(f func() error) func() error {
@ -140,7 +97,7 @@ func (gui *Gui) withDiffModeCheck(f func() error) func() error {
func (gui *Gui) tagsListContext() *context.TagsContext {
return context.NewTagsContext(
func() []*models.Tag { return gui.State.Model.Tags },
func() *gocui.View { return gui.Views.Branches },
gui.Views.Branches,
func(startIdx int, length int) [][]string {
return presentation.GetTagListDisplayStrings(gui.State.Model.Tags, gui.State.Modes.Diffing.Ref)
},
@ -151,25 +108,14 @@ func (gui *Gui) tagsListContext() *context.TagsContext {
)
}
func (gui *Gui) branchCommitsListContext() types.IListContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "commits",
WindowName: "commits",
Key: context.BRANCH_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.Commits) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Commits },
OnFocus: OnFocusWrapper(gui.onCommitFocus),
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) branchCommitsListContext() *context.LocalCommitsContext {
return context.NewLocalCommitsContext(
func() []*models.Commit { return gui.State.Model.Commits },
gui.Views.Commits,
func(startIdx int, length int) [][]string {
selectedCommitSha := ""
if gui.currentContext().GetKey() == context.BRANCH_COMMITS_CONTEXT_KEY {
selectedCommit := gui.getSelectedLocalCommit()
selectedCommit := gui.State.Contexts.BranchCommits.GetSelected()
if selectedCommit != nil {
selectedCommitSha = selectedCommit.Sha
}
@ -179,7 +125,7 @@ func (gui *Gui) branchCommitsListContext() types.IListContext {
gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref,
parseEmoji,
gui.c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
length,
@ -187,35 +133,21 @@ func (gui *Gui) branchCommitsListContext() types.IListContext {
gui.State.Model.BisectInfo,
)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedLocalCommit()
if item == nil {
return ""
}
return item.ID()
},
RenderSelection: true,
}).attachKeybindings()
OnFocusWrapper(gui.onCommitFocus),
OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) subCommitsListContext() types.IListContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "branches",
WindowName: "branches",
Key: context.SUB_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.SubCommits) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.SubCommits },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) subCommitsListContext() *context.SubCommitsContext {
return context.NewSubCommitsContext(
func() []*models.Commit { return gui.State.Model.SubCommits },
gui.Views.Branches,
func(startIdx int, length int) [][]string {
selectedCommitSha := ""
if gui.currentContext().GetKey() == context.SUB_COMMITS_CONTEXT_KEY {
selectedCommit := gui.getSelectedSubCommit()
selectedCommit := gui.State.Contexts.SubCommits.GetSelected()
if selectedCommit != nil {
selectedCommitSha = selectedCommit.Sha
}
@ -225,7 +157,7 @@ func (gui *Gui) subCommitsListContext() types.IListContext {
gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref,
parseEmoji,
gui.c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
length,
@ -233,15 +165,11 @@ func (gui *Gui) subCommitsListContext() types.IListContext {
git_commands.NewNullBisectInfo(),
)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedSubCommit()
if item == nil {
return ""
}
return item.ID()
},
RenderSelection: true,
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) shouldShowGraph() bool {
@ -263,69 +191,44 @@ func (gui *Gui) shouldShowGraph() bool {
return false
}
func (gui *Gui) reflogCommitsListContext() types.IListContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "commits",
WindowName: "commits",
Key: context.REFLOG_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.FilteredReflogCommits) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.ReflogCommits },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) reflogCommitsListContext() *context.ReflogCommitsContext {
return context.NewReflogCommitsContext(
func() []*models.Commit { return gui.State.Model.FilteredReflogCommits },
gui.Views.Commits,
func(startIdx int, length int) [][]string {
return presentation.GetReflogCommitListDisplayStrings(
gui.State.Model.FilteredReflogCommits,
gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref,
parseEmoji,
gui.c.UserConfig.Git.ParseEmoji,
)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedReflogCommit()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) stashListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "stash",
WindowName: "stash",
Key: context.STASH_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.StashEntries) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Stash },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) stashListContext() *context.StashContext {
return context.NewStashContext(
func() []*models.StashEntry { return gui.State.Model.StashEntries },
gui.Views.Stash,
func(startIdx int, length int) [][]string {
return presentation.GetStashEntryListDisplayStrings(gui.State.Model.StashEntries, gui.State.Modes.Diffing.Ref)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedStashEntry()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
return context.NewCommitFilesContext(
func() []*models.CommitFile { return gui.State.Model.CommitFiles },
func() *gocui.View { return gui.Views.CommitFiles },
gui.Views.CommitFiles,
func(startIdx int, length int) [][]string {
if gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetItemsLength() == 0 {
return [][]string{{style.FgRed.Sprint("(none)")}}
@ -346,48 +249,32 @@ func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
)
}
func (gui *Gui) submodulesListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "files",
WindowName: "files",
Key: context.SUBMODULES_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Model.Submodules) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Submodules },
OnRenderToMain: OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)),
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) submodulesListContext() *context.SubmodulesContext {
return context.NewSubmodulesContext(
func() []*models.SubmoduleConfig { return gui.State.Model.Submodules },
gui.Views.Files,
func(startIdx int, length int) [][]string {
return presentation.GetSubmoduleListDisplayStrings(gui.State.Model.Submodules)
},
OnGetSelectedItemId: func() string {
item := gui.getSelectedSubmodule()
if item == nil {
return ""
}
return item.ID()
},
}).attachKeybindings()
nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)),
nil,
gui.c,
)
}
func (gui *Gui) suggestionsListContext() types.IListContext {
return (&ListContext{
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{
ViewName: "suggestions",
WindowName: "suggestions",
Key: context.SUGGESTIONS_CONTEXT_KEY,
Kind: types.PERSISTENT_POPUP,
Focusable: true,
}),
GetItemsLength: func() int { return len(gui.State.Suggestions) },
OnGetPanelState: func() types.IListPanelState { return gui.State.Panels.Suggestions },
Gui: gui,
GetDisplayStrings: func(startIdx int, length int) [][]string {
func (gui *Gui) suggestionsListContext() *context.SuggestionsContext {
return context.NewSuggestionsContext(
func() []*types.Suggestion { return gui.State.Suggestions },
gui.Views.Files,
func(startIdx int, length int) [][]string {
return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions)
},
}).attachKeybindings()
nil,
nil,
nil,
gui.c,
)
}
func (gui *Gui) getListContexts() []types.IListContext {

View file

@ -6,7 +6,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) getMenuOptions() map[string]string {
@ -35,44 +34,24 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
})
}
gui.State.MenuItems = opts.Items
stringArrays := make([][]string, len(opts.Items))
for i, item := range opts.Items {
for _, item := range opts.Items {
if item.OpensMenu && item.DisplayStrings != nil {
return errors.New("Message for the developer of this app: you've set opensMenu with displaystrings on the menu panel. Bad developer!. Apologies, user")
}
if item.DisplayStrings == nil {
styledStr := item.DisplayString
if item.OpensMenu {
styledStr = opensMenuStyle(styledStr)
}
stringArrays[i] = []string{styledStr}
} else {
stringArrays[i] = item.DisplayStrings
}
}
list := utils.RenderDisplayStrings(stringArrays)
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(false, list)
x0, y0, x1, y1 := gui.getConfirmationPanelDimensionsForContentHeight(len(opts.Items))
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = opts.Title
menuView.FgColor = theme.GocuiDefaultTextColor
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
return nil
}))
menuView.SetContent(list)
gui.State.Panels.Menu.SelectedLineIdx = 0
gui.State.Contexts.Menu.SetMenuItems(opts.Items)
gui.State.Contexts.Menu.GetPanelState().SetSelectedLineIdx(0)
_ = gui.c.PostRefreshUpdate(gui.State.Contexts.Menu)
// TODO: ensure that if we're opened a menu from within a menu that it renders correctly
return gui.c.PushContext(gui.State.Contexts.Menu)
}
func (gui *Gui) getSelectedMenuItem() *types.MenuItem {
if len(gui.State.MenuItems) == 0 {
return nil
}
return gui.State.MenuItems[gui.State.Panels.Menu.SelectedLineIdx]
}

View file

@ -3,6 +3,7 @@ package gui
import (
"strings"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
@ -34,16 +35,12 @@ func (gui *Gui) getBindings(context types.Context) []*types.Binding {
func (gui *Gui) displayDescription(binding *types.Binding) string {
if binding.OpensMenu {
return opensMenuStyle(binding.Description)
return presentation.OpensMenuStyle(binding.Description)
}
return style.FgCyan.Sprint(binding.Description)
}
func opensMenuStyle(str string) string {
return style.FgMagenta.Sprintf("%s...", str)
}
func (gui *Gui) handleCreateOptionsMenu() error {
context := gui.currentContext()
bindings := gui.getBindings(context)

View file

@ -26,7 +26,7 @@ func (gui *Gui) refreshPatchBuildingPanel(selectedLineIdx int) error {
gui.Views.Secondary.Title = "Custom Patch"
// get diff from commit file that's currently selected
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}
@ -87,7 +87,7 @@ func (gui *Gui) handleToggleSelectionForPatch() error {
}
// add range of lines to those set for the file
node := gui.getSelectedCommitFileNode()
node := gui.State.Contexts.CommitFiles.GetSelectedFileNode()
if node == nil {
return nil
}

View file

@ -118,7 +118,7 @@ func (gui *Gui) handleMovePatchToSelectedCommit() error {
return gui.c.WithWaitingStatus(gui.c.Tr.RebasingStatus, func() error {
commitIndex := gui.getPatchCommitIndex()
gui.c.LogAction(gui.c.Tr.Actions.MovePatchToSelectedCommit)
err := gui.git.Patch.MovePatchToSelectedCommit(gui.State.Model.Commits, commitIndex, gui.State.Panels.Commits.SelectedLineIdx)
err := gui.git.Patch.MovePatchToSelectedCommit(gui.State.Model.Commits, commitIndex, gui.State.Contexts.BranchCommits.GetSelectedLineIdx())
return gui.helpers.Rebase.CheckMergeOrRebase(err)
})
}

View file

@ -0,0 +1,7 @@
package presentation
import "github.com/jesseduffield/lazygit/pkg/gui/style"
func OpensMenuStyle(str string) string {
return style.FgMagenta.Sprintf("%s...", str)
}

View file

@ -9,17 +9,11 @@ import (
// list panel functions
func (gui *Gui) getSelectedReflogCommit() *models.Commit {
selectedLine := gui.State.Panels.ReflogCommits.SelectedLineIdx
reflogComits := gui.State.Model.FilteredReflogCommits
if selectedLine == -1 || len(reflogComits) == 0 {
return nil
}
return reflogComits[selectedLine]
return gui.State.Contexts.ReflogCommits.GetSelected()
}
func (gui *Gui) reflogCommitsRenderToMain() error {
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
var task updateTask
if commit == nil {
task = NewRenderStringTask("No reflog history")
@ -38,7 +32,7 @@ func (gui *Gui) reflogCommitsRenderToMain() error {
}
func (gui *Gui) CheckoutReflogCommit() error {
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
if commit == nil {
return nil
}
@ -55,19 +49,17 @@ func (gui *Gui) CheckoutReflogCommit() error {
return err
}
gui.State.Panels.ReflogCommits.SelectedLineIdx = 0
return nil
}
func (gui *Gui) handleCreateReflogResetMenu() error {
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
return gui.helpers.Refs.CreateGitResetMenu(commit.Sha)
}
func (gui *Gui) handleViewReflogCommitFiles() error {
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
if commit == nil {
return nil
}
@ -81,7 +73,7 @@ func (gui *Gui) handleViewReflogCommitFiles() error {
}
func (gui *Gui) handleCopyReflogCommit() error {
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
if commit == nil {
return nil
}
@ -91,7 +83,7 @@ func (gui *Gui) handleCopyReflogCommit() error {
func (gui *Gui) handleCopyReflogCommitRange() error {
// just doing this to ensure something is selected
commit := gui.getSelectedReflogCommit()
commit := gui.State.Contexts.ReflogCommits.GetSelected()
if commit == nil {
return nil
}

View file

@ -211,7 +211,7 @@ func (gui *Gui) refreshCommitsWithLimit() error {
commits, err := gui.git.Loaders.Commits.GetCommits(
loaders.GetCommitsOptions{
Limit: gui.State.Panels.Commits.LimitCommits,
Limit: gui.State.LimitCommits,
FilterPath: gui.State.Modes.Filtering.GetPath(),
IncludeRebaseCommits: true,
RefName: gui.refForLog(),
@ -484,7 +484,7 @@ func (gui *Gui) refreshReflogCommits() error {
}
func (gui *Gui) refreshRemotes() error {
prevSelectedRemote := gui.getSelectedRemote()
prevSelectedRemote := gui.State.Contexts.Remotes.GetSelected()
remotes, err := gui.git.Loaders.Remotes.GetRemotes()
if err != nil {

View file

@ -4,25 +4,15 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// list panel functions
func (gui *Gui) getSelectedRemoteBranch() *models.RemoteBranch {
selectedLine := gui.State.Panels.RemoteBranches.SelectedLineIdx
if selectedLine == -1 || len(gui.State.Model.RemoteBranches) == 0 {
return nil
}
return gui.State.Model.RemoteBranches[selectedLine]
}
func (gui *Gui) remoteBranchesRenderToMain() error {
var task updateTask
remoteBranch := gui.getSelectedRemoteBranch()
remoteBranch := gui.State.Contexts.RemoteBranches.GetSelected()
if remoteBranch == nil {
task = NewRenderStringTask("No branches for this remote")
} else {
@ -43,12 +33,12 @@ func (gui *Gui) handleRemoteBranchesEscape() error {
}
func (gui *Gui) handleMergeRemoteBranch() error {
selectedBranchName := gui.getSelectedRemoteBranch().FullName()
selectedBranchName := gui.State.Contexts.RemoteBranches.GetSelected().FullName()
return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName)
}
func (gui *Gui) handleDeleteRemoteBranch() error {
remoteBranch := gui.getSelectedRemoteBranch()
remoteBranch := gui.State.Contexts.RemoteBranches.GetSelected()
if remoteBranch == nil {
return nil
}
@ -72,12 +62,12 @@ func (gui *Gui) handleDeleteRemoteBranch() error {
}
func (gui *Gui) handleRebaseOntoRemoteBranch() error {
selectedBranchName := gui.getSelectedRemoteBranch().FullName()
selectedBranchName := gui.State.Contexts.RemoteBranches.GetSelected().FullName()
return gui.handleRebaseOntoBranch(selectedBranchName)
}
func (gui *Gui) handleSetBranchUpstream() error {
selectedBranch := gui.getSelectedRemoteBranch()
selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected()
checkedOutBranch := gui.getCheckedOutBranch()
message := utils.ResolvePlaceholderString(
@ -103,7 +93,7 @@ func (gui *Gui) handleSetBranchUpstream() error {
}
func (gui *Gui) handleCreateResetToRemoteBranchMenu() error {
selectedBranch := gui.getSelectedRemoteBranch()
selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected()
if selectedBranch == nil {
return nil
}
@ -112,7 +102,7 @@ func (gui *Gui) handleCreateResetToRemoteBranchMenu() error {
}
func (gui *Gui) handleEnterRemoteBranch() error {
selectedBranch := gui.getSelectedRemoteBranch()
selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected()
if selectedBranch == nil {
return nil
}
@ -121,7 +111,7 @@ func (gui *Gui) handleEnterRemoteBranch() error {
}
func (gui *Gui) handleNewBranchOffRemoteBranch() error {
selectedBranch := gui.getSelectedRemoteBranch()
selectedBranch := gui.State.Contexts.RemoteBranches.GetSelected()
if selectedBranch == nil {
return nil
}

View file

@ -4,24 +4,14 @@ import (
"fmt"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
// list panel functions
func (gui *Gui) getSelectedRemote() *models.Remote {
selectedLine := gui.State.Panels.Remotes.SelectedLineIdx
if selectedLine == -1 || len(gui.State.Model.Remotes) == 0 {
return nil
}
return gui.State.Model.Remotes[selectedLine]
}
func (gui *Gui) remotesRenderToMain() error {
var task updateTask
remote := gui.getSelectedRemote()
remote := gui.State.Contexts.Remotes.GetSelected()
if remote == nil {
task = NewRenderStringTask("No remotes")
} else {

View file

@ -9,12 +9,7 @@ import (
// list panel functions
func (gui *Gui) getSelectedStashEntry() *models.StashEntry {
selectedLine := gui.State.Panels.Stash.SelectedLineIdx
if selectedLine == -1 {
return nil
}
return gui.State.Model.StashEntries[selectedLine]
return gui.State.Contexts.Stash.GetSelected()
}
func (gui *Gui) stashRenderToMain() error {

View file

@ -2,25 +2,14 @@ package gui
import (
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
// list panel functions
func (gui *Gui) getSelectedSubCommit() *models.Commit {
selectedLine := gui.State.Panels.SubCommits.SelectedLineIdx
commits := gui.State.Model.SubCommits
if selectedLine == -1 || len(commits) == 0 {
return nil
}
return commits[selectedLine]
}
func (gui *Gui) subCommitsRenderToMain() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
var task updateTask
if commit == nil {
task = NewRenderStringTask("No commits")
@ -39,7 +28,7 @@ func (gui *Gui) subCommitsRenderToMain() error {
}
func (gui *Gui) handleCheckoutSubCommit() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
if commit == nil {
return nil
}
@ -62,13 +51,13 @@ func (gui *Gui) handleCheckoutSubCommit() error {
}
func (gui *Gui) handleCreateSubCommitResetMenu() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
return gui.helpers.Refs.CreateGitResetMenu(commit.Sha)
}
func (gui *Gui) handleViewSubCommitFiles() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
if commit == nil {
return nil
}
@ -85,7 +74,7 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error {
// need to populate my sub commits
commits, err := gui.git.Loaders.Commits.GetCommits(
loaders.GetCommitsOptions{
Limit: gui.State.Panels.Commits.LimitCommits,
Limit: gui.State.LimitCommits,
FilterPath: gui.State.Modes.Filtering.GetPath(),
IncludeRebaseCommits: false,
RefName: refName,
@ -96,7 +85,6 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error {
}
gui.State.Model.SubCommits = commits
gui.State.Panels.SubCommits.refName = refName
gui.State.Contexts.SubCommits.GetPanelState().SetSelectedLineIdx(0)
gui.State.Contexts.SubCommits.SetParentContext(gui.currentSideListContext())
@ -104,7 +92,7 @@ func (gui *Gui) switchToSubCommitsContext(refName string) error {
}
func (gui *Gui) handleNewBranchOffSubCommit() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
if commit == nil {
return nil
}
@ -113,7 +101,7 @@ func (gui *Gui) handleNewBranchOffSubCommit() error {
}
func (gui *Gui) handleCopySubCommit() error {
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
if commit == nil {
return nil
}
@ -123,7 +111,7 @@ func (gui *Gui) handleCopySubCommit() error {
func (gui *Gui) handleCopySubCommitRange() error {
// just doing this to ensure something is selected
commit := gui.getSelectedSubCommit()
commit := gui.State.Contexts.SubCommits.GetSelected()
if commit == nil {
return nil
}

View file

@ -8,18 +8,9 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) getSelectedSubmodule() *models.SubmoduleConfig {
selectedLine := gui.State.Panels.Submodules.SelectedLineIdx
if selectedLine == -1 || len(gui.State.Model.Submodules) == 0 {
return nil
}
return gui.State.Model.Submodules[selectedLine]
}
func (gui *Gui) submodulesRenderToMain() error {
var task updateTask
submodule := gui.getSelectedSubmodule()
submodule := gui.State.Contexts.Submodules.GetSelected()
if submodule == nil {
task = NewRenderStringTask("No submodules")
} else {

View file

@ -15,17 +15,12 @@ func (gui *Gui) getSelectedSuggestionValue() string {
}
func (gui *Gui) getSelectedSuggestion() *types.Suggestion {
selectedLine := gui.State.Panels.Suggestions.SelectedLineIdx
if selectedLine == -1 {
return nil
}
return gui.State.Suggestions[selectedLine]
return gui.State.Contexts.Suggestions.GetSelected()
}
func (gui *Gui) setSuggestions(suggestions []*types.Suggestion) {
gui.State.Suggestions = suggestions
gui.State.Panels.Suggestions.SelectedLineIdx = 0
gui.State.Contexts.Suggestions.SetSelectedLineIdx(0)
_ = gui.resetOrigin(gui.Views.Suggestions)
_ = gui.State.Contexts.Suggestions.HandleRender()
}

View file

@ -2,7 +2,7 @@ package gui
func (self *Gui) tagsRenderToMain() error {
var task updateTask
tag := self.State.Contexts.Tags.GetSelectedTag()
tag := self.State.Contexts.Tags.GetSelected()
if tag == nil {
task = NewRenderStringTask("No tags")
} else {

View file

@ -24,6 +24,7 @@ type ParentContexter interface {
}
type IBaseContext interface {
HasKeybindings
ParentContexter
GetKind() ContextKind
@ -35,9 +36,7 @@ type IBaseContext interface {
GetOptionsMap() map[string]string
GetKeybindings(opts KeybindingsOpts) []*Binding
AddKeybindingsFn(KeybindingsFn)
GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding
AddMouseKeybindingsFn(MouseKeybindingsFn)
}
@ -50,6 +49,33 @@ type Context interface {
HandleRenderToMain() error
}
type IListContext interface {
Context
GetSelectedItemId() string
GetList() IList
OnSearchSelect(selectedLineIdx int) error
FocusLine()
GetPanelState() IListPanelState
GetViewTrait() IViewTrait
}
type IViewTrait interface {
FocusPoint(yIdx int)
SetViewPortContent(content string)
SetContent(content string)
SetFooter(value string)
SetOriginX(value int)
ViewPortYBounds() (int, int)
ScrollLeft()
ScrollRight()
PageDelta() int
SelectedLineIdx() int
}
type OnFocusOpts struct {
ClickedViewName string
ClickedViewLineIdx int
@ -76,28 +102,6 @@ type IController interface {
Context() Context
}
type IListContext interface {
HasKeybindings
GetSelectedItemId() string
HandlePrevLine() error
HandleNextLine() error
HandleScrollLeft() error
HandleScrollRight() error
HandlePrevPage() error
HandleNextPage() error
HandleGotoTop() error
HandleGotoBottom() error
HandleClick(onClick func() error) error
OnSearchSelect(selectedLineIdx int) error
FocusLine()
GetPanelState() IListPanelState
Context
}
type IList interface {
IListCursor
GetItemsLength() int