1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-15 00:15:32 +02:00

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 // 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 { func (gui *Gui) branchesRenderToMain() error {
var task updateTask var task updateTask
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil { if branch == nil {
task = NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo) task = NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo)
} else { } else {
@ -48,24 +35,26 @@ func (gui *Gui) branchesRenderToMain() error {
// specific functions // specific functions
func (gui *Gui) handleBranchPress() error { func (gui *Gui) handleBranchPress() error {
if gui.State.Panels.Branches.SelectedLineIdx == -1 { branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil {
return nil return nil
} }
if gui.State.Panels.Branches.SelectedLineIdx == 0 {
if branch == gui.getCheckedOutBranch() {
return gui.c.ErrorMsg(gui.c.Tr.AlreadyCheckedOutBranch) return gui.c.ErrorMsg(gui.c.Tr.AlreadyCheckedOutBranch)
} }
branch := gui.getSelectedBranch()
gui.c.LogAction(gui.c.Tr.Actions.CheckoutBranch) gui.c.LogAction(gui.c.Tr.Actions.CheckoutBranch)
return gui.helpers.Refs.CheckoutRef(branch.Name, types.CheckoutRefOptions{}) return gui.helpers.Refs.CheckoutRef(branch.Name, types.CheckoutRefOptions{})
} }
func (gui *Gui) handleCreatePullRequestPress() error { func (gui *Gui) handleCreatePullRequestPress() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
return gui.createPullRequest(branch.Name, "") return gui.createPullRequest(branch.Name, "")
} }
func (gui *Gui) handleCreatePullRequestMenu() error { func (gui *Gui) handleCreatePullRequestMenu() error {
selectedBranch := gui.getSelectedBranch() selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil { if selectedBranch == nil {
return nil return nil
} }
@ -77,7 +66,7 @@ func (gui *Gui) handleCreatePullRequestMenu() error {
func (gui *Gui) handleCopyPullRequestURLPress() error { func (gui *Gui) handleCopyPullRequestURLPress() error {
hostingServiceMgr := gui.getHostingServiceMgr() hostingServiceMgr := gui.getHostingServiceMgr()
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
branchExistsOnRemote := gui.git.Remote.CheckRemoteBranchExists(branch.Name) branchExistsOnRemote := gui.git.Remote.CheckRemoteBranchExists(branch.Name)
@ -109,7 +98,7 @@ func (gui *Gui) handleGitFetch() error {
} }
func (gui *Gui) handleForceCheckout() error { func (gui *Gui) handleForceCheckout() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
message := gui.c.Tr.SureForceCheckout message := gui.c.Tr.SureForceCheckout
title := gui.c.Tr.ForceCheckoutBranch title := gui.c.Tr.ForceCheckoutBranch
@ -156,7 +145,7 @@ func (gui *Gui) getCheckedOutBranch() *models.Branch {
} }
func (gui *Gui) createNewBranchWithName(newBranchName string) error { func (gui *Gui) createNewBranchWithName(newBranchName string) error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil { if branch == nil {
return nil return nil
} }
@ -165,7 +154,7 @@ func (gui *Gui) createNewBranchWithName(newBranchName string) error {
return gui.c.Error(err) 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}) 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 { func (gui *Gui) deleteBranch(force bool) error {
selectedBranch := gui.getSelectedBranch() selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil { if selectedBranch == nil {
return nil return nil
} }
@ -245,12 +234,12 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
} }
func (gui *Gui) handleMerge() error { func (gui *Gui) handleMerge() error {
selectedBranchName := gui.getSelectedBranch().Name selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name
return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName) return gui.mergeBranchIntoCheckedOutBranch(selectedBranchName)
} }
func (gui *Gui) handleRebaseOntoLocalBranch() error { func (gui *Gui) handleRebaseOntoLocalBranch() error {
selectedBranchName := gui.getSelectedBranch().Name selectedBranchName := gui.State.Contexts.Branches.GetSelected().Name
return gui.handleRebaseOntoBranch(selectedBranchName) return gui.handleRebaseOntoBranch(selectedBranchName)
} }
@ -279,7 +268,7 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
} }
func (gui *Gui) handleFastForward() error { func (gui *Gui) handleFastForward() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil || !branch.IsRealBranch() { if branch == nil || !branch.IsRealBranch() {
return nil return nil
} }
@ -305,7 +294,7 @@ func (gui *Gui) handleFastForward() error {
) )
return gui.c.WithLoaderPanel(message, func() error { return gui.c.WithLoaderPanel(message, func() error {
if gui.State.Panels.Branches.SelectedLineIdx == 0 { if branch == gui.getCheckedOutBranch() {
gui.c.LogAction(action) gui.c.LogAction(action)
err := gui.git.Sync.Pull( err := gui.git.Sync.Pull(
@ -334,7 +323,7 @@ func (gui *Gui) handleFastForward() error {
} }
func (gui *Gui) handleCreateResetToBranchMenu() error { func (gui *Gui) handleCreateResetToBranchMenu() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil { if branch == nil {
return nil return nil
} }
@ -343,7 +332,7 @@ func (gui *Gui) handleCreateResetToBranchMenu() error {
} }
func (gui *Gui) handleRenameBranch() error { func (gui *Gui) handleRenameBranch() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil || !branch.IsRealBranch() { if branch == nil || !branch.IsRealBranch() {
return nil 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. // 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 { for i, newBranch := range gui.State.Model.Branches {
if newBranch.Name == newBranchName { 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 { if err := gui.State.Contexts.Branches.HandleRender(); err != nil {
return err return err
} }
@ -391,7 +380,7 @@ func (gui *Gui) handleRenameBranch() error {
} }
func (gui *Gui) handleEnterBranch() error { func (gui *Gui) handleEnterBranch() error {
branch := gui.getSelectedBranch() branch := gui.State.Contexts.Branches.GetSelected()
if branch == nil { if branch == nil {
return nil return nil
} }
@ -400,7 +389,7 @@ func (gui *Gui) handleEnterBranch() error {
} }
func (gui *Gui) handleNewBranchOffBranch() error { func (gui *Gui) handleNewBranchOffBranch() error {
selectedBranch := gui.getSelectedBranch() selectedBranch := gui.State.Contexts.Branches.GetSelected()
if selectedBranch == nil { if selectedBranch == nil {
return nil return nil
} }

View File

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

View File

@ -11,18 +11,12 @@ const COMMIT_THRESHOLD = 200
// list panel functions // list panel functions
func (gui *Gui) getSelectedLocalCommit() *models.Commit { func (gui *Gui) getSelectedLocalCommit() *models.Commit {
selectedLine := gui.State.Panels.Commits.SelectedLineIdx return gui.State.Contexts.BranchCommits.GetSelected()
if selectedLine == -1 || selectedLine > len(gui.State.Model.Commits)-1 {
return nil
}
return gui.State.Model.Commits[selectedLine]
} }
func (gui *Gui) onCommitFocus() error { func (gui *Gui) onCommitFocus() error {
state := gui.State.Panels.Commits if gui.State.Contexts.BranchCommits.GetSelectedLineIdx() > COMMIT_THRESHOLD && gui.State.LimitCommits {
if state.SelectedLineIdx > COMMIT_THRESHOLD && state.LimitCommits { gui.State.LimitCommits = false
state.LimitCommits = false
go utils.Safe(func() { go utils.Safe(func() {
if err := gui.refreshCommitsWithLimit(); err != nil { if err := gui.refreshCommitsWithLimit(); err != nil {
_ = gui.c.Error(err) _ = gui.c.Error(err)
@ -37,7 +31,7 @@ func (gui *Gui) onCommitFocus() error {
func (gui *Gui) branchCommitsRenderToMain() error { func (gui *Gui) branchCommitsRenderToMain() error {
var task updateTask var task updateTask
commit := gui.getSelectedLocalCommit() commit := gui.State.Contexts.BranchCommits.GetSelected()
if commit == nil { if commit == nil {
task = NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch) task = NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch)
} else { } 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) { 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() 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. // we want a minimum width up to a point, then we do it based on ratio.
panelWidth := 4 * width / 7 panelWidth := 4 * width / 7
minWidth := 80 minWidth := 80
@ -88,14 +110,8 @@ func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, i
panelWidth = minWidth panelWidth = minWidth
} }
} }
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth)
if panelHeight > height*3/4 { return panelWidth
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) prepareConfirmationPanel( func (gui *Gui) prepareConfirmationPanel(

View File

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

View File

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

View File

@ -3,44 +3,35 @@ package context
import ( import (
"fmt" "fmt"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
type ListContextTrait struct { type ListContextTrait struct {
base types.IBaseContext types.Context
list types.IList
viewTrait *ViewTrait
takeFocus func() error c *types.ControllerCommon
list types.IList
GetDisplayStrings func(startIdx int, length int) [][]string viewTrait *ViewTrait
OnFocus func(...types.OnFocusOpts) error getDisplayStrings func(startIdx int, length int) [][]string
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
c *types.ControllerCommon
} }
func (self *ListContextTrait) GetList() types.IList {
return self.list
}
// TODO: remove
func (self *ListContextTrait) GetPanelState() types.IListPanelState { func (self *ListContextTrait) GetPanelState() types.IListPanelState {
return self.list return self.list
} }
func (self *ListContextTrait) GetViewTrait() types.IViewTrait {
return self.viewTrait
}
func (self *ListContextTrait) FocusLine() { func (self *ListContextTrait) FocusLine() {
// we need a way of knowing whether we've rendered to the view yet. // we need a way of knowing whether we've rendered to the view yet.
self.viewTrait.FocusPoint(self.list.GetSelectedLineIdx()) 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())) 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) 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 { func (self *ListContextTrait) HandleFocus(opts ...types.OnFocusOpts) error {
self.FocusLine() self.FocusLine()
if self.OnFocus != nil { return self.Context.HandleFocus(opts...)
if err := self.OnFocus(opts...); err != nil { }
return err
}
}
if self.OnRenderToMain != nil { func (self *ListContextTrait) HandleFocusLost() error {
if err := self.OnRenderToMain(opts...); err != nil { self.viewTrait.SetOriginX(0)
return err
} 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 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 { func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
self.list.SetSelectedLineIdx(selectedLineIdx) self.GetList().SetSelectedLineIdx(selectedLineIdx)
return self.HandleFocus() 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 ( import (
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -12,10 +11,10 @@ type SimpleContext struct {
// this is for pushing some content to the main view // this is for pushing some content to the main view
OnRenderToMain func(opts ...types.OnFocusOpts) error OnRenderToMain func(opts ...types.OnFocusOpts) error
*context.BaseContext *BaseContext
} }
type NewSimpleContextOpts struct { type ContextCallbackOpts struct {
OnFocus func(opts ...types.OnFocusOpts) error OnFocus func(opts ...types.OnFocusOpts) error
OnFocusLost func() error OnFocusLost func() error
OnRender func() error OnRender func() error
@ -23,7 +22,7 @@ type NewSimpleContextOpts struct {
OnRenderToMain func(opts ...types.OnFocusOpts) error OnRenderToMain func(opts ...types.OnFocusOpts) error
} }
func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpts) *SimpleContext { func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *SimpleContext {
return &SimpleContext{ return &SimpleContext{
OnFocus: opts.OnFocus, OnFocus: opts.OnFocus,
OnFocusLost: opts.OnFocusLost, OnFocusLost: opts.OnFocusLost,
@ -35,13 +34,6 @@ func NewSimpleContext(baseContext *context.BaseContext, opts NewSimpleContextOpt
var _ types.Context = &SimpleContext{} 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 { func (self *SimpleContext) HandleFocus(opts ...types.OnFocusOpts) error {
if self.OnFocus != nil { if self.OnFocus != nil {
if err := self.OnFocus(opts...); err != nil { if err := self.OnFocus(opts...); err != nil {
@ -65,6 +57,13 @@ func (self *SimpleContext) HandleFocusLost() error {
return nil return nil
} }
func (self *SimpleContext) HandleRender() error {
if self.OnRender != nil {
return self.OnRender()
}
return nil
}
func (self *SimpleContext) HandleRenderToMain() error { func (self *SimpleContext) HandleRenderToMain() error {
if self.OnRenderToMain != nil { if self.OnRenderToMain != nil {
return self.OnRenderToMain() 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 { type TagsContext struct {
*TagsViewModel *TagsViewModel
*BaseContext
*ListContextTrait *ListContextTrait
} }
@ -17,7 +16,7 @@ var _ types.IListContext = (*TagsContext)(nil)
func NewTagsContext( func NewTagsContext(
getModel func() []*models.Tag, getModel func() []*models.Tag,
getView func() *gocui.View, view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string, getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error, onFocus func(...types.OnFocusOpts) error,
@ -26,47 +25,32 @@ func NewTagsContext(
c *types.ControllerCommon, c *types.ControllerCommon,
) *TagsContext { ) *TagsContext {
baseContext := NewBaseContext(NewBaseContextOpts{ viewModel := NewTagsViewModel(getModel)
ViewName: "branches",
WindowName: "branches",
Key: TAGS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})
self := &TagsContext{} return &TagsContext{
takeFocus := func() error { return c.PushContext(self) } TagsViewModel: viewModel,
ListContextTrait: &ListContextTrait{
list := NewTagsViewModel(getModel) Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
viewTrait := NewViewTrait(getView) ViewName: "branches",
listContextTrait := &ListContextTrait{ WindowName: "branches",
base: baseContext, Key: TAGS_CONTEXT_KEY,
list: list, Kind: types.SIDE_CONTEXT,
viewTrait: viewTrait, Focusable: true,
}), ContextCallbackOpts{
GetDisplayStrings: getDisplayStrings, OnFocus: onFocus,
OnFocus: onFocus, OnFocusLost: onFocusLost,
OnRenderToMain: onRenderToMain, OnRenderToMain: onRenderToMain,
OnFocusLost: onFocusLost, }),
takeFocus: takeFocus, list: viewModel,
viewTrait: NewViewTrait(view),
// TODO: handle this in a trait getDisplayStrings: getDisplayStrings,
RenderSelection: false, c: c,
},
c: c,
} }
baseContext.AddKeybindingsFn(listContextTrait.keybindings)
self.BaseContext = baseContext
self.ListContextTrait = listContextTrait
self.TagsViewModel = list
return self
} }
func (self *TagsContext) GetSelectedItemId() string { func (self *TagsContext) GetSelectedItemId() string {
item := self.GetSelectedTag() item := self.GetSelected()
if item == nil { if item == nil {
return "" return ""
} }
@ -79,18 +63,6 @@ type TagsViewModel struct {
getModel func() []*models.Tag 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 { func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel {
self := &TagsViewModel{ self := &TagsViewModel{
getModel: getModel, getModel: getModel,
@ -100,3 +72,15 @@ func NewTagsViewModel(getModel func() []*models.Tag) *TagsViewModel {
return self 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 ( import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
const HORIZONTAL_SCROLL_FACTOR = 3 const HORIZONTAL_SCROLL_FACTOR = 3
type ViewTrait struct { type ViewTrait struct {
getView func() *gocui.View view *gocui.View
} }
func NewViewTrait(getView func() *gocui.View) *ViewTrait { var _ types.IViewTrait = &ViewTrait{}
return &ViewTrait{getView: getView}
func NewViewTrait(view *gocui.View) *ViewTrait {
return &ViewTrait{view: view}
} }
func (self *ViewTrait) FocusPoint(yIdx int) { func (self *ViewTrait) FocusPoint(yIdx int) {
view := self.getView() self.view.FocusPoint(self.view.OriginX(), yIdx)
view.FocusPoint(view.OriginX(), yIdx)
} }
func (self *ViewTrait) SetViewPortContent(content string) { func (self *ViewTrait) SetViewPortContent(content string) {
view := self.getView() _, y := self.view.Origin()
self.view.OverwriteLines(y, content)
_, y := view.Origin()
view.OverwriteLines(y, content)
} }
func (self *ViewTrait) SetContent(content string) { func (self *ViewTrait) SetContent(content string) {
self.getView().SetContent(content) self.view.SetContent(content)
} }
func (self *ViewTrait) SetFooter(value string) { func (self *ViewTrait) SetFooter(value string) {
self.getView().Footer = value self.view.Footer = value
} }
func (self *ViewTrait) SetOriginX(value int) { 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 // tells us the bounds of line indexes shown in the view currently
func (self *ViewTrait) ViewPortYBounds() (int, int) { func (self *ViewTrait) ViewPortYBounds() (int, int) {
view := self.getView() _, min := self.view.Origin()
max := self.view.InnerHeight() + 1
_, min := view.Origin()
max := view.InnerHeight() + 1
return min, max return min, max
} }
func (self *ViewTrait) ScrollLeft() { func (self *ViewTrait) ScrollLeft() {
view := self.getView() newOriginX := utils.Max(self.view.OriginX()-self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0)
_ = self.view.SetOriginX(newOriginX)
newOriginX := utils.Max(view.OriginX()-view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0)
_ = view.SetOriginX(newOriginX)
} }
func (self *ViewTrait) ScrollRight() { func (self *ViewTrait) ScrollRight() {
view := self.getView() _ = self.view.SetOriginX(self.view.OriginX() + self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR)
_ = view.SetOriginX(view.OriginX() + view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR)
} }
// this returns the amount we'll scroll if we want to scroll by a page. // this returns the amount we'll scroll if we want to scroll by a page.
func (self *ViewTrait) PageDelta() int { func (self *ViewTrait) PageDelta() int {
view := self.getView() _, height := self.view.Size()
_, height := view.Size()
delta := height - 1 delta := height - 1
if delta == 0 { if delta == 0 {
@ -76,5 +68,5 @@ func (self *ViewTrait) PageDelta() int {
} }
func (self *ViewTrait) SelectedLineIdx() 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 { type WorkingTreeContext struct {
*filetree.FileTreeViewModel *filetree.FileTreeViewModel
*BaseContext
*ListContextTrait *ListContextTrait
} }
@ -17,7 +16,7 @@ var _ types.IListContext = (*WorkingTreeContext)(nil)
func NewWorkingTreeContext( func NewWorkingTreeContext(
getModel func() []*models.File, getModel func() []*models.File,
getView func() *gocui.View, view *gocui.View,
getDisplayStrings func(startIdx int, length int) [][]string, getDisplayStrings func(startIdx int, length int) [][]string,
onFocus func(...types.OnFocusOpts) error, onFocus func(...types.OnFocusOpts) error,
@ -26,43 +25,28 @@ func NewWorkingTreeContext(
c *types.ControllerCommon, c *types.ControllerCommon,
) *WorkingTreeContext { ) *WorkingTreeContext {
baseContext := 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) viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
viewTrait := NewViewTrait(getView)
listContextTrait := &ListContextTrait{
base: baseContext,
list: viewModel,
viewTrait: viewTrait,
GetDisplayStrings: getDisplayStrings, return &WorkingTreeContext{
OnFocus: onFocus, FileTreeViewModel: viewModel,
OnRenderToMain: onRenderToMain, ListContextTrait: &ListContextTrait{
OnFocusLost: onFocusLost, Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
takeFocus: takeFocus, ViewName: "files",
WindowName: "files",
// TODO: handle this in a trait Key: FILES_CONTEXT_KEY,
RenderSelection: false, Kind: types.SIDE_CONTEXT,
Focusable: true,
c: c, }), ContextCallbackOpts{
OnFocus: onFocus,
OnFocusLost: onFocusLost,
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 { func (self *WorkingTreeContext) GetSelectedItemId() string {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,9 +5,9 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "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/style"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -16,29 +16,26 @@ type SubmodulesController struct {
baseController baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context *context.SubmodulesContext
git *commands.GitCommand git *commands.GitCommand
enterSubmodule func(submodule *models.SubmoduleConfig) error enterSubmodule func(submodule *models.SubmoduleConfig) error
getSelectedSubmodule func() *models.SubmoduleConfig
} }
var _ types.IController = &SubmodulesController{} var _ types.IController = &SubmodulesController{}
func NewSubmodulesController( func NewSubmodulesController(
c *types.ControllerCommon, c *types.ControllerCommon,
context types.IListContext, context *context.SubmodulesContext,
git *commands.GitCommand, git *commands.GitCommand,
enterSubmodule func(submodule *models.SubmoduleConfig) error, enterSubmodule func(submodule *models.SubmoduleConfig) error,
getSelectedSubmodule func() *models.SubmoduleConfig,
) *SubmodulesController { ) *SubmodulesController {
return &SubmodulesController{ return &SubmodulesController{
baseController: baseController{}, baseController: baseController{},
c: c, c: c,
context: context, context: context,
git: git, git: git,
enterSubmodule: enterSubmodule, enterSubmodule: enterSubmodule,
getSelectedSubmodule: getSelectedSubmodule,
} }
} }
@ -80,10 +77,10 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []*
Description: self.c.Tr.LcViewBulkSubmoduleOptions, Description: self.c.Tr.LcViewBulkSubmoduleOptions,
OpensMenu: true, OpensMenu: true,
}, },
{ // {
Key: gocui.MouseLeft, // Key: gocui.MouseLeft,
Handler: func() error { return self.context.HandleClick(self.checkSelected(self.enter)) }, // 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 { func (self *SubmodulesController) checkSelected(callback func(*models.SubmoduleConfig) error) func() error {
return func() error { return func() error {
submodule := self.getSelectedSubmodule() submodule := self.context.GetSelected()
if submodule == nil { if submodule == nil {
return 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 { func (self *TagsController) withSelectedTag(f func(tag *models.Tag) error) func() error {
return func() error { return func() error {
tag := self.context.GetSelectedTag() tag := self.context.GetSelected()
if tag == nil { if tag == nil {
return nil return nil
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -179,11 +179,11 @@ type GuiRepoState struct {
// Suggestions will sometimes appear when typing into a prompt // Suggestions will sometimes appear when typing into a prompt
Suggestions []*types.Suggestion Suggestions []*types.Suggestion
MenuItems []*types.MenuItem
Updating bool Updating bool
Panels *panelStates Panels *panelStates
SplitMainPanel bool SplitMainPanel bool
LimitCommits bool
IsRefreshingFiles bool IsRefreshingFiles bool
Searching searchingState Searching searchingState
@ -253,68 +253,11 @@ type MergingPanelState struct {
UserVerticalScrolling bool 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 // 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. // remove this struct altogether and store this state on the contexts.
type panelStates struct { type panelStates struct {
Branches *branchPanelState LineByLine *LblPanelState
Remotes *remotePanelState Merging *MergingPanelState
RemoteBranches *remoteBranchesState
Commits *commitPanelState
ReflogCommits *reflogCommitPanelState
SubCommits *subCommitPanelState
Stash *stashPanelState
Menu *menuPanelState
LineByLine *LblPanelState
Merging *MergingPanelState
Submodules *submodulePanelState
Suggestions *suggestionsPanelState
} }
type Views struct { type Views struct {
@ -449,23 +392,13 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
}, },
Panels: &panelStates{ 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{ Merging: &MergingPanelState{
State: mergeconflicts.NewState(), State: mergeconflicts.NewState(),
UserVerticalScrolling: false, UserVerticalScrolling: false,
}, },
}, },
Ptmx: nil, LimitCommits: true,
Ptmx: nil,
Modes: Modes{ Modes: Modes{
Filtering: filtering.New(filterPath), Filtering: filtering.New(filterPath),
CherryPicking: cherrypicking.New(), CherryPicking: cherrypicking.New(),
@ -584,7 +517,7 @@ func (gui *Gui) resetControllers() {
controllerCommon, controllerCommon,
gui.git, gui.git,
gui.State.Contexts, gui.State.Contexts,
func() { gui.State.Panels.Commits.LimitCommits = true }, func() { gui.State.LimitCommits = true },
), ),
Bisect: controllers.NewBisectHelper(controllerCommon, gui.git), Bisect: controllers.NewBisectHelper(controllerCommon, gui.git),
Suggestions: controllers.NewSuggestionsHelper(controllerCommon, model, gui.refreshSuggestions), Suggestions: controllers.NewSuggestionsHelper(controllerCommon, model, gui.refreshSuggestions),
@ -615,7 +548,6 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.Submodules, gui.State.Contexts.Submodules,
gui.git, gui.git,
gui.enterSubmodule, gui.enterSubmodule,
gui.getSelectedSubmodule,
) )
bisectController := controllers.NewBisectController( bisectController := controllers.NewBisectController(
@ -623,7 +555,6 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.BranchCommits, gui.State.Contexts.BranchCommits,
gui.git, gui.git,
gui.helpers.Bisect, gui.helpers.Bisect,
gui.getSelectedLocalCommit,
func() []*models.Commit { return gui.State.Model.Commits }, func() []*models.Commit { return gui.State.Model.Commits },
) )
@ -672,15 +603,13 @@ func (gui *Gui) resetControllers() {
gui.helpers.Refs, gui.helpers.Refs,
gui.helpers.CherryPick, gui.helpers.CherryPick,
gui.helpers.Rebase, gui.helpers.Rebase,
gui.getSelectedLocalCommit,
model, model,
func() int { return gui.State.Panels.Commits.SelectedLineIdx },
gui.helpers.Rebase.CheckMergeOrRebase, gui.helpers.Rebase.CheckMergeOrRebase,
syncController.HandlePull, syncController.HandlePull,
gui.getHostingServiceMgr, gui.getHostingServiceMgr,
gui.SwitchToCommitFilesContext, gui.SwitchToCommitFilesContext,
func() bool { return gui.State.Panels.Commits.LimitCommits }, func() bool { return gui.State.LimitCommits },
func(value bool) { gui.State.Panels.Commits.LimitCommits = value }, func(value bool) { gui.State.LimitCommits = value },
func() bool { return gui.ShowWholeGitGraph }, func() bool { return gui.ShowWholeGitGraph },
func(value bool) { gui.ShowWholeGitGraph = value }, func(value bool) { gui.ShowWholeGitGraph = value },
), ),
@ -689,13 +618,11 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.Remotes, gui.State.Contexts.Remotes,
gui.git, gui.git,
gui.State.Contexts, gui.State.Contexts,
gui.getSelectedRemote,
func(branches []*models.RemoteBranch) { gui.State.Model.RemoteBranches = branches }, func(branches []*models.RemoteBranch) { gui.State.Model.RemoteBranches = branches },
), ),
Menu: controllers.NewMenuController( Menu: controllers.NewMenuController(
controllerCommon, controllerCommon,
gui.State.Contexts.Menu, gui.State.Contexts.Menu,
gui.getSelectedMenuItem,
), ),
Undo: controllers.NewUndoController( Undo: controllers.NewUndoController(
controllerCommon, controllerCommon,
@ -714,6 +641,11 @@ func (gui *Gui) resetControllers() {
controllers.AttachControllers(gui.State.Contexts.Remotes, gui.Controllers.Remotes) controllers.AttachControllers(gui.State.Contexts.Remotes, gui.Controllers.Remotes)
controllers.AttachControllers(gui.State.Contexts.Menu, gui.Controllers.Menu) controllers.AttachControllers(gui.State.Contexts.Menu, gui.Controllers.Menu)
controllers.AttachControllers(gui.State.Contexts.Global, gui.Controllers.Sync, gui.Controllers.Undo, gui.Controllers.Global) 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{ 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 ( import (
"log" "log"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/context"
@ -12,27 +11,21 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
func (gui *Gui) menuListContext() types.IListContext { func (gui *Gui) menuListContext() *context.MenuContext {
return (&ListContext{ return context.NewMenuContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ gui.Views.Menu,
ViewName: "menu", nil,
Key: "menu", nil,
Kind: types.PERSISTENT_POPUP, nil,
OnGetOptionsMap: gui.getMenuOptions, gui.c,
Focusable: true, gui.getMenuOptions,
}), )
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) filesListContext() *context.WorkingTreeContext { func (gui *Gui) filesListContext() *context.WorkingTreeContext {
return context.NewWorkingTreeContext( return context.NewWorkingTreeContext(
func() []*models.File { return gui.State.Model.Files }, func() []*models.File { return gui.State.Model.Files },
func() *gocui.View { return gui.Views.Files }, gui.Views.Files,
func(startIdx int, length int) [][]string { func(startIdx int, length int) [][]string {
lines := presentation.RenderFileTree(gui.State.Contexts.Files.FileTreeViewModel, gui.State.Modes.Diffing.Ref, gui.State.Model.Submodules) lines := presentation.RenderFileTree(gui.State.Contexts.Files.FileTreeViewModel, gui.State.Modes.Diffing.Ref, gui.State.Model.Submodules)
mappedLines := make([][]string, len(lines)) mappedLines := make([][]string, len(lines))
@ -49,82 +42,46 @@ func (gui *Gui) filesListContext() *context.WorkingTreeContext {
) )
} }
func (gui *Gui) branchesListContext() types.IListContext { func (gui *Gui) branchesListContext() *context.BranchesContext {
return (&ListContext{ return context.NewBranchesContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*models.Branch { return gui.State.Model.Branches },
ViewName: "branches", gui.Views.Branches,
WindowName: "branches", func(startIdx int, length int) [][]string {
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 {
return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref) return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref)
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedBranch() OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) remotesListContext() types.IListContext { func (gui *Gui) remotesListContext() *context.RemotesContext {
return (&ListContext{ return context.NewRemotesContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*models.Remote { return gui.State.Model.Remotes },
ViewName: "branches", gui.Views.Branches,
WindowName: "branches", func(startIdx int, length int) [][]string {
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 {
return presentation.GetRemoteListDisplayStrings(gui.State.Model.Remotes, gui.State.Modes.Diffing.Ref) return presentation.GetRemoteListDisplayStrings(gui.State.Model.Remotes, gui.State.Modes.Diffing.Ref)
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedRemote() OnFocusWrapper(gui.withDiffModeCheck(gui.remotesRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) remoteBranchesListContext() types.IListContext { func (gui *Gui) remoteBranchesListContext() *context.RemoteBranchesContext {
return (&ListContext{ return context.NewRemoteBranchesContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*models.RemoteBranch { return gui.State.Model.RemoteBranches },
ViewName: "branches", gui.Views.Branches,
WindowName: "branches", func(startIdx int, length int) [][]string {
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 {
return presentation.GetRemoteBranchListDisplayStrings(gui.State.Model.RemoteBranches, gui.State.Modes.Diffing.Ref) return presentation.GetRemoteBranchListDisplayStrings(gui.State.Model.RemoteBranches, gui.State.Modes.Diffing.Ref)
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedRemoteBranch() OnFocusWrapper(gui.withDiffModeCheck(gui.remoteBranchesRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) withDiffModeCheck(f func() error) func() error { 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 { func (gui *Gui) tagsListContext() *context.TagsContext {
return context.NewTagsContext( return context.NewTagsContext(
func() []*models.Tag { return gui.State.Model.Tags }, func() []*models.Tag { return gui.State.Model.Tags },
func() *gocui.View { return gui.Views.Branches }, gui.Views.Branches,
func(startIdx int, length int) [][]string { func(startIdx int, length int) [][]string {
return presentation.GetTagListDisplayStrings(gui.State.Model.Tags, gui.State.Modes.Diffing.Ref) 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 { func (gui *Gui) branchCommitsListContext() *context.LocalCommitsContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji return context.NewLocalCommitsContext(
return (&ListContext{ func() []*models.Commit { return gui.State.Model.Commits },
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ gui.Views.Commits,
ViewName: "commits", func(startIdx int, length int) [][]string {
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 {
selectedCommitSha := "" selectedCommitSha := ""
if gui.currentContext().GetKey() == context.BRANCH_COMMITS_CONTEXT_KEY { if gui.currentContext().GetKey() == context.BRANCH_COMMITS_CONTEXT_KEY {
selectedCommit := gui.getSelectedLocalCommit() selectedCommit := gui.State.Contexts.BranchCommits.GetSelected()
if selectedCommit != nil { if selectedCommit != nil {
selectedCommitSha = selectedCommit.Sha selectedCommitSha = selectedCommit.Sha
} }
@ -179,7 +125,7 @@ func (gui *Gui) branchCommitsListContext() types.IListContext {
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(), gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref, gui.State.Modes.Diffing.Ref,
parseEmoji, gui.c.UserConfig.Git.ParseEmoji,
selectedCommitSha, selectedCommitSha,
startIdx, startIdx,
length, length,
@ -187,35 +133,21 @@ func (gui *Gui) branchCommitsListContext() types.IListContext {
gui.State.Model.BisectInfo, gui.State.Model.BisectInfo,
) )
}, },
OnGetSelectedItemId: func() string { OnFocusWrapper(gui.onCommitFocus),
item := gui.getSelectedLocalCommit() OnFocusWrapper(gui.withDiffModeCheck(gui.branchCommitsRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
RenderSelection: true,
}).attachKeybindings()
} }
func (gui *Gui) subCommitsListContext() types.IListContext { func (gui *Gui) subCommitsListContext() *context.SubCommitsContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji return context.NewSubCommitsContext(
return (&ListContext{ func() []*models.Commit { return gui.State.Model.SubCommits },
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ gui.Views.Branches,
ViewName: "branches", func(startIdx int, length int) [][]string {
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 {
selectedCommitSha := "" selectedCommitSha := ""
if gui.currentContext().GetKey() == context.SUB_COMMITS_CONTEXT_KEY { if gui.currentContext().GetKey() == context.SUB_COMMITS_CONTEXT_KEY {
selectedCommit := gui.getSelectedSubCommit() selectedCommit := gui.State.Contexts.SubCommits.GetSelected()
if selectedCommit != nil { if selectedCommit != nil {
selectedCommitSha = selectedCommit.Sha selectedCommitSha = selectedCommit.Sha
} }
@ -225,7 +157,7 @@ func (gui *Gui) subCommitsListContext() types.IListContext {
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(), gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref, gui.State.Modes.Diffing.Ref,
parseEmoji, gui.c.UserConfig.Git.ParseEmoji,
selectedCommitSha, selectedCommitSha,
startIdx, startIdx,
length, length,
@ -233,15 +165,11 @@ func (gui *Gui) subCommitsListContext() types.IListContext {
git_commands.NewNullBisectInfo(), git_commands.NewNullBisectInfo(),
) )
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedSubCommit() OnFocusWrapper(gui.withDiffModeCheck(gui.subCommitsRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
RenderSelection: true,
}).attachKeybindings()
} }
func (gui *Gui) shouldShowGraph() bool { func (gui *Gui) shouldShowGraph() bool {
@ -263,69 +191,44 @@ func (gui *Gui) shouldShowGraph() bool {
return false return false
} }
func (gui *Gui) reflogCommitsListContext() types.IListContext { func (gui *Gui) reflogCommitsListContext() *context.ReflogCommitsContext {
parseEmoji := gui.c.UserConfig.Git.ParseEmoji return context.NewReflogCommitsContext(
return (&ListContext{ func() []*models.Commit { return gui.State.Model.FilteredReflogCommits },
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ gui.Views.Commits,
ViewName: "commits", func(startIdx int, length int) [][]string {
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 {
return presentation.GetReflogCommitListDisplayStrings( return presentation.GetReflogCommitListDisplayStrings(
gui.State.Model.FilteredReflogCommits, gui.State.Model.FilteredReflogCommits,
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
gui.helpers.CherryPick.CherryPickedCommitShaMap(), gui.helpers.CherryPick.CherryPickedCommitShaMap(),
gui.State.Modes.Diffing.Ref, gui.State.Modes.Diffing.Ref,
parseEmoji, gui.c.UserConfig.Git.ParseEmoji,
) )
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedReflogCommit() OnFocusWrapper(gui.withDiffModeCheck(gui.reflogCommitsRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) stashListContext() types.IListContext { func (gui *Gui) stashListContext() *context.StashContext {
return (&ListContext{ return context.NewStashContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*models.StashEntry { return gui.State.Model.StashEntries },
ViewName: "stash", gui.Views.Stash,
WindowName: "stash", func(startIdx int, length int) [][]string {
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 {
return presentation.GetStashEntryListDisplayStrings(gui.State.Model.StashEntries, gui.State.Modes.Diffing.Ref) return presentation.GetStashEntryListDisplayStrings(gui.State.Model.StashEntries, gui.State.Modes.Diffing.Ref)
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedStashEntry() OnFocusWrapper(gui.withDiffModeCheck(gui.stashRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) commitFilesListContext() *context.CommitFilesContext { func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
return context.NewCommitFilesContext( return context.NewCommitFilesContext(
func() []*models.CommitFile { return gui.State.Model.CommitFiles }, func() []*models.CommitFile { return gui.State.Model.CommitFiles },
func() *gocui.View { return gui.Views.CommitFiles }, gui.Views.CommitFiles,
func(startIdx int, length int) [][]string { func(startIdx int, length int) [][]string {
if gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetItemsLength() == 0 { if gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.GetItemsLength() == 0 {
return [][]string{{style.FgRed.Sprint("(none)")}} return [][]string{{style.FgRed.Sprint("(none)")}}
@ -346,48 +249,32 @@ func (gui *Gui) commitFilesListContext() *context.CommitFilesContext {
) )
} }
func (gui *Gui) submodulesListContext() types.IListContext { func (gui *Gui) submodulesListContext() *context.SubmodulesContext {
return (&ListContext{ return context.NewSubmodulesContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*models.SubmoduleConfig { return gui.State.Model.Submodules },
ViewName: "files", gui.Views.Files,
WindowName: "files", func(startIdx int, length int) [][]string {
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 {
return presentation.GetSubmoduleListDisplayStrings(gui.State.Model.Submodules) return presentation.GetSubmoduleListDisplayStrings(gui.State.Model.Submodules)
}, },
OnGetSelectedItemId: func() string { nil,
item := gui.getSelectedSubmodule() OnFocusWrapper(gui.withDiffModeCheck(gui.submodulesRenderToMain)),
if item == nil { nil,
return "" gui.c,
} )
return item.ID()
},
}).attachKeybindings()
} }
func (gui *Gui) suggestionsListContext() types.IListContext { func (gui *Gui) suggestionsListContext() *context.SuggestionsContext {
return (&ListContext{ return context.NewSuggestionsContext(
BaseContext: context.NewBaseContext(context.NewBaseContextOpts{ func() []*types.Suggestion { return gui.State.Suggestions },
ViewName: "suggestions", gui.Views.Files,
WindowName: "suggestions", func(startIdx int, length int) [][]string {
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 {
return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions) return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions)
}, },
}).attachKeybindings() nil,
nil,
nil,
gui.c,
)
} }
func (gui *Gui) getListContexts() []types.IListContext { 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/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) getMenuOptions() map[string]string { func (gui *Gui) getMenuOptions() map[string]string {
@ -35,44 +34,24 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
}) })
} }
gui.State.MenuItems = opts.Items for _, item := range opts.Items {
stringArrays := make([][]string, len(opts.Items))
for i, item := range opts.Items {
if item.OpensMenu && item.DisplayStrings != nil { 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") 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.getConfirmationPanelDimensionsForContentHeight(len(opts.Items))
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(false, list)
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0) menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = opts.Title menuView.Title = opts.Title
menuView.FgColor = theme.GocuiDefaultTextColor menuView.FgColor = theme.GocuiDefaultTextColor
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error { menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
return nil 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) 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 ( import (
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "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 { func (gui *Gui) displayDescription(binding *types.Binding) string {
if binding.OpensMenu { if binding.OpensMenu {
return opensMenuStyle(binding.Description) return presentation.OpensMenuStyle(binding.Description)
} }
return style.FgCyan.Sprint(binding.Description) return style.FgCyan.Sprint(binding.Description)
} }
func opensMenuStyle(str string) string {
return style.FgMagenta.Sprintf("%s...", str)
}
func (gui *Gui) handleCreateOptionsMenu() error { func (gui *Gui) handleCreateOptionsMenu() error {
context := gui.currentContext() context := gui.currentContext()
bindings := gui.getBindings(context) bindings := gui.getBindings(context)

View File

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

View File

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

View File

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

View File

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

View File

@ -4,24 +4,14 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
) )
// list panel functions // 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 { func (gui *Gui) remotesRenderToMain() error {
var task updateTask var task updateTask
remote := gui.getSelectedRemote() remote := gui.State.Contexts.Remotes.GetSelected()
if remote == nil { if remote == nil {
task = NewRenderStringTask("No remotes") task = NewRenderStringTask("No remotes")
} else { } else {

View File

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

View File

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

View File

@ -8,18 +8,9 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/style" "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 { func (gui *Gui) submodulesRenderToMain() error {
var task updateTask var task updateTask
submodule := gui.getSelectedSubmodule() submodule := gui.State.Contexts.Submodules.GetSelected()
if submodule == nil { if submodule == nil {
task = NewRenderStringTask("No submodules") task = NewRenderStringTask("No submodules")
} else { } else {

View File

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

View File

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

View File

@ -24,6 +24,7 @@ type ParentContexter interface {
} }
type IBaseContext interface { type IBaseContext interface {
HasKeybindings
ParentContexter ParentContexter
GetKind() ContextKind GetKind() ContextKind
@ -35,9 +36,7 @@ type IBaseContext interface {
GetOptionsMap() map[string]string GetOptionsMap() map[string]string
GetKeybindings(opts KeybindingsOpts) []*Binding
AddKeybindingsFn(KeybindingsFn) AddKeybindingsFn(KeybindingsFn)
GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding
AddMouseKeybindingsFn(MouseKeybindingsFn) AddMouseKeybindingsFn(MouseKeybindingsFn)
} }
@ -50,6 +49,33 @@ type Context interface {
HandleRenderToMain() error 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 { type OnFocusOpts struct {
ClickedViewName string ClickedViewName string
ClickedViewLineIdx int ClickedViewLineIdx int
@ -76,28 +102,6 @@ type IController interface {
Context() Context 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 { type IList interface {
IListCursor IListCursor
GetItemsLength() int GetItemsLength() int