1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-12 11:15:00 +02:00
lazygit/pkg/gui/list_context.go

374 lines
13 KiB
Go
Raw Normal View History

2019-11-16 05:00:27 +02:00
package gui
import "github.com/jesseduffield/gocui"
2020-08-17 13:58:30 +02:00
type ListContext struct {
2020-08-16 02:05:45 +02:00
ViewName string
2020-08-16 06:16:28 +02:00
ContextKey string
2020-08-16 02:05:45 +02:00
GetItemsLength func() int
GetSelectedLineIdxPtr func() *int
OnFocus func() error
2020-08-16 05:58:29 +02:00
OnFocusLost func() error
2020-08-16 02:05:45 +02:00
OnItemSelect func() error
OnClickSelectedItem func() error
2020-08-19 10:06:51 +02:00
// 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
OnRender func() error
Gui *Gui
RendersToMainView bool
Kind int
2019-11-16 05:00:27 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) GetKey() string {
return lc.ContextKey
2020-08-16 05:58:29 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) GetKind() int {
return lc.Kind
2020-08-16 05:58:29 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) GetViewName() string {
return lc.ViewName
2020-08-16 05:58:29 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) HandleFocusLost() error {
if lc.OnFocusLost != nil {
return lc.OnFocusLost()
2020-08-16 05:58:29 +02:00
}
return nil
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) HandleFocus() error {
return lc.OnFocus()
2020-08-16 05:58:29 +02:00
}
2020-08-19 10:06:51 +02:00
func (lc *ListContext) HandleRender() error {
return lc.OnRender()
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handlePrevLine(g *gocui.Gui, v *gocui.View) error {
return lc.handleLineChange(-1)
2019-11-16 05:00:27 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleNextLine(g *gocui.Gui, v *gocui.View) error {
return lc.handleLineChange(1)
2019-11-16 05:00:27 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleLineChange(change int) error {
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
2019-11-16 05:00:27 +02:00
return nil
}
2020-08-17 13:58:30 +02:00
view, err := lc.Gui.g.View(lc.ViewName)
2020-08-16 01:18:57 +02:00
if err != nil {
return err
}
2020-08-17 13:58:30 +02:00
lc.Gui.changeSelectedLine(lc.GetSelectedLineIdxPtr(), lc.GetItemsLength(), change)
view.FocusPoint(0, *lc.GetSelectedLineIdxPtr())
2019-11-16 05:00:27 +02:00
2020-08-17 13:58:30 +02:00
if lc.RendersToMainView {
if err := lc.Gui.resetOrigin(lc.Gui.getMainView()); err != nil {
2019-11-16 05:00:27 +02:00
return err
}
2020-08-17 13:58:30 +02:00
if err := lc.Gui.resetOrigin(lc.Gui.getSecondaryView()); err != nil {
2020-08-16 01:18:57 +02:00
return err
}
2019-11-16 05:00:27 +02:00
}
2020-08-17 13:58:30 +02:00
if lc.OnItemSelect != nil {
return lc.OnItemSelect()
2020-08-16 01:18:57 +02:00
}
return nil
2019-11-16 05:00:27 +02:00
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleNextPage(g *gocui.Gui, v *gocui.View) error {
view, err := lc.Gui.g.View(lc.ViewName)
if err != nil {
return nil
}
_, height := view.Size()
delta := height - 1
if delta == 0 {
delta = 1
}
2020-08-17 13:58:30 +02:00
return lc.handleLineChange(delta)
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleGotoTop(g *gocui.Gui, v *gocui.View) error {
return lc.handleLineChange(-lc.GetItemsLength())
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleGotoBottom(g *gocui.Gui, v *gocui.View) error {
return lc.handleLineChange(lc.GetItemsLength())
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
view, err := lc.Gui.g.View(lc.ViewName)
if err != nil {
return nil
}
_, height := view.Size()
delta := height - 1
if delta == 0 {
delta = 1
}
2020-08-17 13:58:30 +02:00
return lc.handleLineChange(-delta)
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) handleClick(g *gocui.Gui, v *gocui.View) error {
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
return nil
}
2020-08-17 13:58:30 +02:00
selectedLineIdxPtr := lc.GetSelectedLineIdxPtr()
prevSelectedLineIdx := *selectedLineIdxPtr
newSelectedLineIdx := v.SelectedLineIdx()
2020-08-16 01:18:57 +02:00
// we need to focus the view
2020-08-17 13:58:30 +02:00
if err := lc.Gui.switchContext(lc); err != nil {
2020-08-16 01:18:57 +02:00
return err
}
2020-08-17 13:58:30 +02:00
if newSelectedLineIdx > lc.GetItemsLength()-1 {
2020-08-16 05:58:29 +02:00
return nil
}
*selectedLineIdxPtr = newSelectedLineIdx
2020-08-17 13:58:30 +02:00
prevViewName := lc.Gui.currentViewName()
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lc.ViewName && lc.OnClickSelectedItem != nil {
return lc.OnClickSelectedItem()
}
2020-08-17 13:58:30 +02:00
if lc.OnItemSelect != nil {
return lc.OnItemSelect()
2020-08-16 01:18:57 +02:00
}
return nil
}
2020-08-17 13:58:30 +02:00
func (lc *ListContext) onSearchSelect(selectedLineIdx int) error {
*lc.GetSelectedLineIdxPtr() = selectedLineIdx
if lc.OnItemSelect != nil {
return lc.OnItemSelect()
2020-08-16 01:18:57 +02:00
}
return nil
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) menuListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "menu",
2020-08-16 06:16:28 +02:00
ContextKey: "menu",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return gui.getMenuView().LinesHeight() },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine },
OnFocus: gui.handleMenuSelect,
OnItemSelect: gui.handleMenuSelect,
2020-08-19 10:41:57 +02:00
// rendering the menu happens on creation
OnRender: func() error { return nil },
2020-08-16 01:18:57 +02:00
// need to add a layer of indirection here because the callback changes during runtime
2020-08-16 02:05:45 +02:00
OnClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) },
Gui: gui,
RendersToMainView: false,
2020-08-16 05:58:29 +02:00
Kind: PERSISTENT_POPUP,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) filesListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "files",
2020-08-16 06:16:28 +02:00
ContextKey: "files",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.Files) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine },
OnFocus: gui.focusAndSelectFile,
OnItemSelect: gui.focusAndSelectFile,
OnClickSelectedItem: gui.handleFilePress,
2020-08-19 10:41:57 +02:00
OnRender: gui.renderFiles,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: false,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) branchesListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "branches",
2020-08-16 06:16:28 +02:00
ContextKey: "local-branches",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.Branches) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Branches.SelectedLine },
OnFocus: gui.handleBranchSelect,
OnItemSelect: gui.handleBranchSelect,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderLocalBranchesContext,
2020-08-19 10:41:57 +02:00
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) remotesListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "branches",
2020-08-16 06:16:28 +02:00
ContextKey: "remotes",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.Remotes) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Remotes.SelectedLine },
2020-08-19 11:31:58 +02:00
OnFocus: gui.handleRemoteSelect,
2020-08-16 02:05:45 +02:00
OnItemSelect: gui.handleRemoteSelect,
OnClickSelectedItem: gui.handleRemoteEnter,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderRemotesContext,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) remoteBranchesListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "branches",
2020-08-16 06:16:28 +02:00
ContextKey: "remote-branches",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine },
OnFocus: gui.handleRemoteBranchSelect,
OnItemSelect: gui.handleRemoteBranchSelect,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderRemoteBranchesContext,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) tagsListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "branches",
2020-08-16 06:16:28 +02:00
ContextKey: "tags",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.Tags) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine },
OnFocus: gui.handleTagSelect,
OnItemSelect: gui.handleTagSelect,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderTagsContext,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) branchCommitsListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "commits",
2020-08-16 06:16:28 +02:00
ContextKey: "branch-commits",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.Commits) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine },
OnFocus: gui.handleCommitSelect,
OnItemSelect: gui.handleCommitSelect,
OnClickSelectedItem: gui.handleSwitchToCommitFilesPanel,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderBranchCommitsContext,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) reflogCommitsListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "commits",
2020-08-16 06:16:28 +02:00
ContextKey: "reflog-commits",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine },
OnFocus: gui.handleReflogCommitSelect,
OnItemSelect: gui.handleReflogCommitSelect,
2020-08-19 11:33:59 +02:00
OnRender: gui.renderReflogCommitsContext,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) stashListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "stash",
2020-08-16 06:16:28 +02:00
ContextKey: "stash",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.StashEntries) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine },
OnFocus: gui.handleStashEntrySelect,
OnItemSelect: gui.handleStashEntrySelect,
2020-08-19 10:41:57 +02:00
OnRender: gui.renderStash,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) commitFilesListContext() *ListContext {
return &ListContext{
2020-08-16 02:05:45 +02:00
ViewName: "commitFiles",
2020-08-16 06:16:28 +02:00
ContextKey: "commitFiles",
2020-08-16 02:05:45 +02:00
GetItemsLength: func() int { return len(gui.State.CommitFiles) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine },
OnFocus: gui.handleCommitFileSelect,
OnItemSelect: gui.handleCommitFileSelect,
2020-08-19 10:41:57 +02:00
OnRender: gui.renderCommitFiles,
2020-08-16 02:05:45 +02:00
Gui: gui,
RendersToMainView: true,
2020-08-16 05:58:29 +02:00
Kind: SIDE_CONTEXT,
2020-08-16 01:18:57 +02:00
}
}
2020-08-17 13:58:30 +02:00
func (gui *Gui) getListContexts() []*ListContext {
return []*ListContext{
gui.menuListContext(),
gui.filesListContext(),
gui.branchesListContext(),
gui.remotesListContext(),
gui.remoteBranchesListContext(),
gui.tagsListContext(),
gui.branchCommitsListContext(),
gui.reflogCommitsListContext(),
gui.stashListContext(),
gui.commitFilesListContext(),
2019-11-16 05:00:27 +02:00
}
}
2020-08-17 14:05:15 +02:00
func (gui *Gui) getListContextKeyBindings() []*Binding {
bindings := make([]*Binding, 0)
for _, listContext := range gui.getListContexts() {
bindings = append(bindings, []*Binding{
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.prevItem-alt"), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.prevItem"), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.nextItem-alt"), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.nextItem"), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.prevPage"), Modifier: gocui.ModNone, Handler: listContext.handlePrevPage, Description: gui.Tr.SLocalize("prevPage")},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.nextPage"), Modifier: gocui.ModNone, Handler: listContext.handleNextPage, Description: gui.Tr.SLocalize("nextPage")},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gui.getKey("universal.gotoTop"), Modifier: gocui.ModNone, Handler: listContext.handleGotoTop, Description: gui.Tr.SLocalize("gotoTop")},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
{ViewName: listContext.ViewName, Contexts: []string{listContext.ContextKey}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listContext.handleClick},
}...)
// the commits panel needs to lazyload things so it has a couple of its own handlers
openSearchHandler := gui.handleOpenSearch
gotoBottomHandler := listContext.handleGotoBottom
if listContext.ViewName == "commits" {
openSearchHandler = gui.handleOpenSearchForCommitsPanel
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
}
bindings = append(bindings, []*Binding{
{
ViewName: listContext.ViewName,
Contexts: []string{listContext.ContextKey},
Key: gui.getKey("universal.startSearch"),
Handler: openSearchHandler,
Description: gui.Tr.SLocalize("startSearch"),
},
{
ViewName: listContext.ViewName,
Contexts: []string{listContext.ContextKey},
Key: gui.getKey("universal.gotoBottom"),
Handler: gotoBottomHandler,
Description: gui.Tr.SLocalize("gotoBottom"),
},
}...)
}
return bindings
}