1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-15 01:34:26 +02:00
This commit is contained in:
Jesse Duffield
2020-08-16 10:05:45 +10:00
parent cec4cb48cb
commit 0ea0c48631
6 changed files with 221 additions and 131 deletions

View File

@ -742,7 +742,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel(g *gocui.Gui, v *gocui.View) err
} }
for _, view := range gui.getListViews() { for _, view := range gui.getListViews() {
if view.viewName == "commits" { if view.ViewName == "commits" {
return view.handleGotoBottom(g, v) return view.handleGotoBottom(g, v)
} }
} }

View File

@ -1,5 +1,9 @@
package gui package gui
import (
"github.com/jesseduffield/lazygit/pkg/gui/stack"
)
// changeContext is a helper function for when we want to change a 'main' context // changeContext is a helper function for when we want to change a 'main' context
// which currently just means a context that affects both the main and secondary views // which currently just means a context that affects both the main and secondary views
// other views can have their context changed directly but this function helps // other views can have their context changed directly but this function helps
@ -17,3 +21,66 @@ func (gui *Gui) changeMainViewsContext(context string) {
gui.State.MainContext = context gui.State.MainContext = context
} }
type contextManager struct {
gui *Gui
stack stack.Stack
}
func (c *contextManager) push(contextKey string) {
c.stack.Push(contextKey)
}
// push focus, pop focus.
type Context interface {
OnFocus() error
}
type SimpleContext struct {
Self Context
}
type RemotesContext struct {
Self Context
Branches Context
}
type CommitsContext struct {
Self Context
Files Context
}
type ContextTree struct {
Status SimpleContext
Files SimpleContext
Branches SimpleContext
Remotes RemotesContext
Tags SimpleContext
Commits CommitsContext
Stash SimpleContext
Staging SimpleContext
PatchBuilding SimpleContext
Merging SimpleContext
Menu SimpleContext
Credentials SimpleContext
Confirmation SimpleContext
CommitMessage SimpleContext
}
func (gui *Gui) createContextTree() {
gui.State.Contexts = ContextTree{
Files: SimpleContext{
Self: gui.filesListView(),
},
}
}
// func (c *contextManager) pop() (string, bool) {
// value, ok := c.stack.Pop()
// if !ok {
// // bottom of the stack, let's go to the default context: the files context
// c.gui.switchFocus(nil, newView)
// }
// }

View File

@ -236,6 +236,8 @@ type guiState struct {
StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once StartupStage int // one of INITIAL and COMPLETE. Allows us to not load everything at once
FilterPath string // the filename that gets passed to git log FilterPath string // the filename that gets passed to git log
Diff DiffState Diff DiffState
Contexts ContextTree
} }
func (gui *Gui) resetState() { func (gui *Gui) resetState() {

View File

@ -1408,37 +1408,37 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
for _, listView := range gui.getListViews() { for _, listView := range gui.getListViews() {
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevItem-alt"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevItem-alt"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevItem"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevItem"), Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listView.handlePrevLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listView.handlePrevLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextItem-alt"), Modifier: gocui.ModNone, Handler: listView.handleNextLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextItem-alt"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextItem"), Modifier: gocui.ModNone, Handler: listView.handleNextLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextItem"), Modifier: gocui.ModNone, Handler: listView.handleNextLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.prevPage"), Modifier: gocui.ModNone, Handler: listView.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.prevPage"), Modifier: gocui.ModNone, Handler: listView.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.nextPage"), Modifier: gocui.ModNone, Handler: listView.handleNextPage, Description: gui.Tr.SLocalize("nextPage")}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.nextPage"), Modifier: gocui.ModNone, Handler: listView.handleNextPage, Description: gui.Tr.SLocalize("nextPage")},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gui.getKey("universal.gotoTop"), Modifier: gocui.ModNone, Handler: listView.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gui.getKey("universal.gotoTop"), Modifier: gocui.ModNone, Handler: listView.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listView.handleNextLine}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listView.handleNextLine},
{ViewName: listView.viewName, Contexts: []string{listView.context}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listView.handleClick}, {ViewName: listView.ViewName, Contexts: []string{listView.Context}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listView.handleClick},
}...) }...)
// the commits panel needs to lazyload things so it has a couple of its own handlers // the commits panel needs to lazyload things so it has a couple of its own handlers
openSearchHandler := gui.handleOpenSearch openSearchHandler := gui.handleOpenSearch
gotoBottomHandler := listView.handleGotoBottom gotoBottomHandler := listView.handleGotoBottom
if listView.viewName == "commits" { if listView.ViewName == "commits" {
openSearchHandler = gui.handleOpenSearchForCommitsPanel openSearchHandler = gui.handleOpenSearchForCommitsPanel
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
} }
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ {
ViewName: listView.viewName, ViewName: listView.ViewName,
Contexts: []string{listView.context}, Contexts: []string{listView.Context},
Key: gui.getKey("universal.startSearch"), Key: gui.getKey("universal.startSearch"),
Handler: openSearchHandler, Handler: openSearchHandler,
Description: gui.Tr.SLocalize("startSearch"), Description: gui.Tr.SLocalize("startSearch"),
}, },
{ {
ViewName: listView.viewName, ViewName: listView.ViewName,
Contexts: []string{listView.context}, Contexts: []string{listView.Context},
Key: gui.getKey("universal.gotoBottom"), Key: gui.getKey("universal.gotoBottom"),
Handler: gotoBottomHandler, Handler: gotoBottomHandler,
Description: gui.Tr.SLocalize("gotoBottom"), Description: gui.Tr.SLocalize("gotoBottom"),

View File

@ -3,15 +3,15 @@ package gui
import "github.com/jesseduffield/gocui" import "github.com/jesseduffield/gocui"
type listView struct { type listView struct {
viewName string ViewName string
context string Context string
getItemsLength func() int GetItemsLength func() int
getSelectedLineIdxPtr func() *int GetSelectedLineIdxPtr func() *int
handleFocus func() error OnFocus func() error
handleItemSelect func() error OnItemSelect func() error
handleClickSelectedItem func() error OnClickSelectedItem func() error
gui *Gui Gui *Gui
rendersToMainView bool RendersToMainView bool
} }
func (lv *listView) handlePrevLine(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handlePrevLine(g *gocui.Gui, v *gocui.View) error {
@ -23,35 +23,35 @@ func (lv *listView) handleNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (lv *listView) handleLineChange(change int) error { func (lv *listView) handleLineChange(change int) error {
if !lv.gui.isPopupPanel(lv.viewName) && lv.gui.popupPanelFocused() { if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil return nil
} }
view, err := lv.gui.g.View(lv.viewName) view, err := lv.Gui.g.View(lv.ViewName)
if err != nil { if err != nil {
return err return err
} }
lv.gui.changeSelectedLine(lv.getSelectedLineIdxPtr(), lv.getItemsLength(), change) lv.Gui.changeSelectedLine(lv.GetSelectedLineIdxPtr(), lv.GetItemsLength(), change)
view.FocusPoint(0, *lv.getSelectedLineIdxPtr()) view.FocusPoint(0, *lv.GetSelectedLineIdxPtr())
if lv.rendersToMainView { if lv.RendersToMainView {
if err := lv.gui.resetOrigin(lv.gui.getMainView()); err != nil { if err := lv.Gui.resetOrigin(lv.Gui.getMainView()); err != nil {
return err return err
} }
if err := lv.gui.resetOrigin(lv.gui.getSecondaryView()); err != nil { if err := lv.Gui.resetOrigin(lv.Gui.getSecondaryView()); err != nil {
return err return err
} }
} }
if lv.handleItemSelect != nil { if lv.OnItemSelect != nil {
return lv.handleItemSelect() return lv.OnItemSelect()
} }
return nil return nil
} }
func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
view, err := lv.gui.g.View(lv.viewName) view, err := lv.Gui.g.View(lv.ViewName)
if err != nil { if err != nil {
return nil return nil
} }
@ -64,15 +64,15 @@ func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
} }
func (lv *listView) handleGotoTop(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handleGotoTop(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(-lv.getItemsLength()) return lv.handleLineChange(-lv.GetItemsLength())
} }
func (lv *listView) handleGotoBottom(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handleGotoBottom(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(lv.getItemsLength()) return lv.handleLineChange(lv.GetItemsLength())
} }
func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
view, err := lv.gui.g.View(lv.viewName) view, err := lv.Gui.g.View(lv.ViewName)
if err != nil { if err != nil {
return nil return nil
} }
@ -85,171 +85,171 @@ func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
} }
func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error { func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
if !lv.gui.isPopupPanel(lv.viewName) && lv.gui.popupPanelFocused() { if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil return nil
} }
selectedLineIdxPtr := lv.getSelectedLineIdxPtr() selectedLineIdxPtr := lv.GetSelectedLineIdxPtr()
prevSelectedLineIdx := *selectedLineIdxPtr prevSelectedLineIdx := *selectedLineIdxPtr
newSelectedLineIdx := v.SelectedLineIdx() newSelectedLineIdx := v.SelectedLineIdx()
// we need to focus the view // we need to focus the view
if err := lv.gui.switchFocus(nil, v); err != nil { if err := lv.Gui.switchFocus(nil, v); err != nil {
return err return err
} }
if newSelectedLineIdx > lv.getItemsLength()-1 { if newSelectedLineIdx > lv.GetItemsLength()-1 {
return lv.handleFocus() return lv.OnFocus()
} }
*selectedLineIdxPtr = newSelectedLineIdx *selectedLineIdxPtr = newSelectedLineIdx
prevViewName := lv.gui.currentViewName() prevViewName := lv.Gui.currentViewName()
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lv.viewName && lv.handleClickSelectedItem != nil { if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lv.ViewName && lv.OnClickSelectedItem != nil {
return lv.handleClickSelectedItem() return lv.OnClickSelectedItem()
} }
if lv.handleItemSelect != nil { if lv.OnItemSelect != nil {
return lv.handleItemSelect() return lv.OnItemSelect()
} }
return nil return nil
} }
func (lv *listView) onSearchSelect(selectedLineIdx int) error { func (lv *listView) onSearchSelect(selectedLineIdx int) error {
*lv.getSelectedLineIdxPtr() = selectedLineIdx *lv.GetSelectedLineIdxPtr() = selectedLineIdx
if lv.handleItemSelect != nil { if lv.OnItemSelect != nil {
return lv.handleItemSelect() return lv.OnItemSelect()
} }
return nil return nil
} }
func (gui *Gui) menuListView() *listView { func (gui *Gui) menuListView() *listView {
return &listView{ return &listView{
viewName: "menu", ViewName: "menu",
getItemsLength: func() int { return gui.getMenuView().LinesHeight() }, GetItemsLength: func() int { return gui.getMenuView().LinesHeight() },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine },
handleFocus: gui.handleMenuSelect, OnFocus: gui.handleMenuSelect,
handleItemSelect: gui.handleMenuSelect, OnItemSelect: gui.handleMenuSelect,
// need to add a layer of indirection here because the callback changes during runtime // need to add a layer of indirection here because the callback changes during runtime
handleClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) }, OnClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) },
gui: gui, Gui: gui,
rendersToMainView: false, RendersToMainView: false,
} }
} }
func (gui *Gui) filesListView() *listView { func (gui *Gui) filesListView() *listView {
return &listView{ return &listView{
viewName: "files", ViewName: "files",
getItemsLength: func() int { return len(gui.State.Files) }, GetItemsLength: func() int { return len(gui.State.Files) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine },
handleFocus: gui.focusAndSelectFile, OnFocus: gui.focusAndSelectFile,
handleItemSelect: gui.focusAndSelectFile, OnItemSelect: gui.focusAndSelectFile,
handleClickSelectedItem: gui.handleFilePress, OnClickSelectedItem: gui.handleFilePress,
gui: gui, Gui: gui,
rendersToMainView: false, RendersToMainView: false,
} }
} }
func (gui *Gui) branchesListView() *listView { func (gui *Gui) branchesListView() *listView {
return &listView{ return &listView{
viewName: "branches", ViewName: "branches",
context: "local-branches", Context: "local-branches",
getItemsLength: func() int { return len(gui.State.Branches) }, GetItemsLength: func() int { return len(gui.State.Branches) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Branches.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Branches.SelectedLine },
handleFocus: gui.handleBranchSelect, OnFocus: gui.handleBranchSelect,
handleItemSelect: gui.handleBranchSelect, OnItemSelect: gui.handleBranchSelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) remotesListView() *listView { func (gui *Gui) remotesListView() *listView {
return &listView{ return &listView{
viewName: "branches", ViewName: "branches",
context: "remotes", Context: "remotes",
getItemsLength: func() int { return len(gui.State.Remotes) }, GetItemsLength: func() int { return len(gui.State.Remotes) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Remotes.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Remotes.SelectedLine },
handleFocus: gui.renderRemotesWithSelection, OnFocus: gui.renderRemotesWithSelection,
handleItemSelect: gui.handleRemoteSelect, OnItemSelect: gui.handleRemoteSelect,
handleClickSelectedItem: gui.handleRemoteEnter, OnClickSelectedItem: gui.handleRemoteEnter,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) remoteBranchesListView() *listView { func (gui *Gui) remoteBranchesListView() *listView {
return &listView{ return &listView{
viewName: "branches", ViewName: "branches",
context: "remote-branches", Context: "remote-branches",
getItemsLength: func() int { return len(gui.State.RemoteBranches) }, GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine },
handleFocus: gui.handleRemoteBranchSelect, OnFocus: gui.handleRemoteBranchSelect,
handleItemSelect: gui.handleRemoteBranchSelect, OnItemSelect: gui.handleRemoteBranchSelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) tagsListView() *listView { func (gui *Gui) tagsListView() *listView {
return &listView{ return &listView{
viewName: "branches", ViewName: "branches",
context: "tags", Context: "tags",
getItemsLength: func() int { return len(gui.State.Tags) }, GetItemsLength: func() int { return len(gui.State.Tags) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine },
handleFocus: gui.handleTagSelect, OnFocus: gui.handleTagSelect,
handleItemSelect: gui.handleTagSelect, OnItemSelect: gui.handleTagSelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) branchCommitsListView() *listView { func (gui *Gui) branchCommitsListView() *listView {
return &listView{ return &listView{
viewName: "commits", ViewName: "commits",
context: "branch-commits", Context: "branch-commits",
getItemsLength: func() int { return len(gui.State.Commits) }, GetItemsLength: func() int { return len(gui.State.Commits) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine },
handleFocus: gui.handleCommitSelect, OnFocus: gui.handleCommitSelect,
handleItemSelect: gui.handleCommitSelect, OnItemSelect: gui.handleCommitSelect,
handleClickSelectedItem: gui.handleSwitchToCommitFilesPanel, OnClickSelectedItem: gui.handleSwitchToCommitFilesPanel,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) reflogCommitsListView() *listView { func (gui *Gui) reflogCommitsListView() *listView {
return &listView{ return &listView{
viewName: "commits", ViewName: "commits",
context: "reflog-commits", Context: "reflog-commits",
getItemsLength: func() int { return len(gui.State.FilteredReflogCommits) }, GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine },
handleFocus: gui.handleReflogCommitSelect, OnFocus: gui.handleReflogCommitSelect,
handleItemSelect: gui.handleReflogCommitSelect, OnItemSelect: gui.handleReflogCommitSelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) stashListView() *listView { func (gui *Gui) stashListView() *listView {
return &listView{ return &listView{
viewName: "stash", ViewName: "stash",
getItemsLength: func() int { return len(gui.State.StashEntries) }, GetItemsLength: func() int { return len(gui.State.StashEntries) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine },
handleFocus: gui.handleStashEntrySelect, OnFocus: gui.handleStashEntrySelect,
handleItemSelect: gui.handleStashEntrySelect, OnItemSelect: gui.handleStashEntrySelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }
func (gui *Gui) commitFilesListView() *listView { func (gui *Gui) commitFilesListView() *listView {
return &listView{ return &listView{
viewName: "commitFiles", ViewName: "commitFiles",
getItemsLength: func() int { return len(gui.State.CommitFiles) }, GetItemsLength: func() int { return len(gui.State.CommitFiles) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine }, GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine },
handleFocus: gui.handleCommitFileSelect, OnFocus: gui.handleCommitFileSelect,
handleItemSelect: gui.handleCommitFileSelect, OnItemSelect: gui.handleCommitFileSelect,
gui: gui, Gui: gui,
rendersToMainView: true, RendersToMainView: true,
} }
} }

21
pkg/gui/stack/stack.go Normal file
View File

@ -0,0 +1,21 @@
package stack
type Stack struct {
stack []string
}
func (s *Stack) Push(contextKey string) {
s.stack = append(s.stack, contextKey)
}
func (s *Stack) Pop() (string, bool) {
if len(s.stack) == 0 {
return "", false
}
n := len(s.stack) - 1
value := s.stack[n]
s.stack = s.stack[:n]
return value, true
}