1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-23 12:18:51 +02:00
This commit is contained in:
Jesse Duffield 2020-08-20 08:24:35 +10:00
parent 9ee7793782
commit 99707a527d
6 changed files with 98 additions and 47 deletions

View File

@ -23,6 +23,7 @@ type Context interface {
GetKind() int GetKind() int
GetViewName() string GetViewName() string
GetKey() string GetKey() string
GetSelectedItemId() string
} }
type BasicContext struct { type BasicContext struct {
@ -34,6 +35,11 @@ type BasicContext struct {
ViewName string ViewName string
} }
// TODO: think about whether we need this on the Context interface or if it should just be on the ListContext struct
func (c BasicContext) GetSelectedItemId() string {
return ""
}
func (c BasicContext) HandleRender() error { func (c BasicContext) HandleRender() error {
if c.OnRender != nil { if c.OnRender != nil {
return c.OnRender() return c.OnRender()

View File

@ -222,7 +222,7 @@ type guiState struct {
Remotes []*commands.Remote Remotes []*commands.Remote
RemoteBranches []*commands.RemoteBranch RemoteBranches []*commands.RemoteBranch
Tags []*commands.Tag Tags []*commands.Tag
MenuItemCount int // can't store the actual list because it's of interface{} type MenuItems []*menuItem
Updating bool Updating bool
Panels *panelStates Panels *panelStates
MainContext string // used to keep the main and secondary views' contexts in sync MainContext string // used to keep the main and secondary views' contexts in sync

View File

@ -1383,6 +1383,24 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollDownConfirmationPanel, Handler: gui.scrollDownConfirmationPanel,
}, },
{
ViewName: "menu",
Key: gui.getKey("universal.select"),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.onMenuPress),
},
{
ViewName: "menu",
Key: gui.getKey("universal.confirm"),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.onMenuPress),
},
{
ViewName: "menu",
Key: gui.getKey("universal.confirm-alt1"),
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.onMenuPress),
},
} }
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} { for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {

View File

@ -283,36 +283,33 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
type listContextState struct { type listContextState struct {
selectedLine int view *gocui.View
lineCount int listContext *ListContext
view *gocui.View
contextKey string
listContext *ListContext
} }
listContextStates := []listContextState{ listContextStates := []listContextState{
{view: filesView, contextKey: "files", selectedLine: gui.State.Panels.Files.SelectedLine, lineCount: len(gui.State.Files), listContext: gui.filesListContext()}, {view: filesView, listContext: gui.filesListContext()},
{view: branchesView, contextKey: "local-branches", selectedLine: gui.State.Panels.Branches.SelectedLine, lineCount: len(gui.State.Branches), listContext: gui.branchesListContext()}, {view: branchesView, listContext: gui.branchesListContext()},
{view: branchesView, contextKey: "remotes", selectedLine: gui.State.Panels.Remotes.SelectedLine, lineCount: len(gui.State.Remotes), listContext: gui.remotesListContext()}, {view: branchesView, listContext: gui.remotesListContext()},
{view: branchesView, contextKey: "remote-branches", selectedLine: gui.State.Panels.RemoteBranches.SelectedLine, lineCount: len(gui.State.Remotes), listContext: gui.remoteBranchesListContext()}, {view: branchesView, listContext: gui.remoteBranchesListContext()},
{view: branchesView, contextKey: "tags", selectedLine: gui.State.Panels.Tags.SelectedLine, lineCount: len(gui.State.Tags), listContext: gui.tagsListContext()}, {view: branchesView, listContext: gui.tagsListContext()},
{view: commitsView, contextKey: "branch-commits", selectedLine: gui.State.Panels.Commits.SelectedLine, lineCount: len(gui.State.Commits), listContext: gui.branchCommitsListContext()}, {view: commitsView, listContext: gui.branchCommitsListContext()},
{view: commitsView, contextKey: "reflog-commits", selectedLine: gui.State.Panels.ReflogCommits.SelectedLine, lineCount: len(gui.State.FilteredReflogCommits), listContext: gui.reflogCommitsListContext()}, {view: commitsView, listContext: gui.reflogCommitsListContext()},
{view: stashView, contextKey: "stash", selectedLine: gui.State.Panels.Stash.SelectedLine, lineCount: len(gui.State.StashEntries), listContext: gui.stashListContext()}, {view: stashView, listContext: gui.stashListContext()},
{view: commitFilesView, contextKey: "commitFiles", selectedLine: gui.State.Panels.CommitFiles.SelectedLine, lineCount: len(gui.State.CommitFiles), listContext: gui.commitFilesListContext()}, {view: commitFilesView, listContext: gui.commitFilesListContext()},
} }
// menu view might not exist so we check to be safe // menu view might not exist so we check to be safe
if menuView, err := gui.g.View("menu"); err == nil { if menuView, err := gui.g.View("menu"); err == nil {
listContextStates = append(listContextStates, listContextState{view: menuView, contextKey: "menu", selectedLine: gui.State.Panels.Menu.SelectedLine, lineCount: gui.State.MenuItemCount, listContext: gui.menuListContext()}) listContextStates = append(listContextStates, listContextState{view: menuView, listContext: gui.menuListContext()})
} }
for _, listContextState := range listContextStates { for _, listContextState := range listContextStates {
// ignore contexts whose view is owned by another context right now // ignore contexts whose view is owned by another context right now
if listContextState.view.Context != listContextState.contextKey { if listContextState.view.Context != listContextState.listContext.GetKey() {
continue continue
} }
// check if the selected line is now out of view and if so refocus it // check if the selected line is now out of view and if so refocus it
listContextState.view.FocusPoint(0, listContextState.selectedLine) listContextState.view.FocusPoint(0, *listContextState.listContext.GetSelectedLineIdxPtr())
listContextState.view.SelBgColor = theme.GocuiSelectedLineBgColor listContextState.view.SelBgColor = theme.GocuiSelectedLineBgColor

View File

@ -14,12 +14,46 @@ type ListContext struct {
OnFocus func() error OnFocus func() error
OnFocusLost func() error OnFocusLost func() error
OnClickSelectedItem func() error OnClickSelectedItem func() error
GetItems func() []ListItem
Gui *Gui Gui *Gui
RendersToMainView bool RendersToMainView bool
Kind int Kind int
} }
type ListItem interface {
ID() string
}
func (lc *ListContext) GetSelectedItem() ListItem {
items := lc.GetItems()
if len(items) == 0 {
return nil
}
selectedLineIdx := *lc.GetSelectedLineIdxPtr()
if selectedLineIdx > len(items)-1 {
return nil
}
item := items[selectedLineIdx]
return item
}
func (lc *ListContext) GetSelectedItemId() string {
item := lc.GetSelectedItem()
if item == nil {
return ""
}
return item.ID()
}
// 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 // 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 (lc *ListContext) OnRender() error { func (lc *ListContext) OnRender() error {
view, err := lc.Gui.g.View(lc.ViewName) view, err := lc.Gui.g.View(lc.ViewName)
@ -64,6 +98,10 @@ func (lc *ListContext) HandleFocus() error {
return lc.Gui.renderDiff() return lc.Gui.renderDiff()
} }
// every time you select an item we need to store that item's ID on the context (a string). After a state refresh, after we update the selected line, we need to check if the selected item is new, in which case we will reset the origin. In the case of the merge panel we set the origin in a custom way, so it can't be as simple as just resetting the origin. for files we need to know whether we're dealing with a file with merge conflicts, and if so, we need to scroll to the file in a custom way, after rendering to the main view.
// we can use this id to know what to do once we're actually in the merging context, so that we're not affected by outside state changes.
if lc.OnFocus != nil { if lc.OnFocus != nil {
return lc.OnFocus() return lc.OnFocus()
} }
@ -186,6 +224,7 @@ func (gui *Gui) menuListContext() *ListContext {
Gui: gui, Gui: gui,
RendersToMainView: false, RendersToMainView: false,
Kind: PERSISTENT_POPUP, Kind: PERSISTENT_POPUP,
// GetItems:
// no GetDisplayStrings field because we do a custom render on menu creation // no GetDisplayStrings field because we do a custom render on menu creation
} }

View File

@ -2,6 +2,7 @@ package gui
import ( import (
"fmt" "fmt"
"strings"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/theme"
@ -14,6 +15,15 @@ type menuItem struct {
onPress func() error onPress func() error
} }
// every item in a list context needs an ID
func (i *menuItem) ID() string {
if i.displayString != "" {
return i.displayString
}
return strings.Join(i.displayStrings, "-")
}
// list panel functions // list panel functions
func (gui *Gui) handleMenuSelect() error { func (gui *Gui) handleMenuSelect() error {
@ -31,16 +41,7 @@ func (gui *Gui) renderMenuOptions() error {
return gui.renderOptionsMap(optionsMap) return gui.renderOptionsMap(optionsMap)
} }
func (gui *Gui) menuConfirmationKeys() []interface{} {
return []interface{}{gui.getKey("universal.select"), gui.getKey("universal.confirm"), gui.getKey("universal.confirm-alt1")}
}
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
for _, key := range gui.menuConfirmationKeys() {
if err := g.DeleteKeybinding("menu", key, gocui.ModNone); err != nil {
return err
}
}
err := g.DeleteView("menu") err := g.DeleteView("menu")
if err != nil { if err != nil {
return err return err
@ -63,7 +64,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
}) })
} }
gui.State.MenuItemCount = len(items) gui.State.MenuItems = items
stringArrays := make([][]string, len(items)) stringArrays := make([][]string, len(items))
for i, item := range items { for i, item := range items {
@ -88,27 +89,17 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
fmt.Fprint(menuView, list) fmt.Fprint(menuView, list)
gui.State.Panels.Menu.SelectedLine = 0 gui.State.Panels.Menu.SelectedLine = 0
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
selectedLine := gui.State.Panels.Menu.SelectedLine
if err := items[selectedLine].onPress(); err != nil {
return err
}
return gui.returnFromContext()
}
gui.State.Panels.Menu.OnPress = wrappedHandlePress
for _, key := range gui.menuConfirmationKeys() {
_ = gui.g.DeleteKeybinding("menu", key, gocui.ModNone)
if err := gui.g.SetKeybinding("menu", nil, key, gocui.ModNone, wrappedHandlePress); err != nil {
return err
}
}
gui.g.Update(func(g *gocui.Gui) error { gui.g.Update(func(g *gocui.Gui) error {
return gui.switchContext(gui.Contexts.Menu.Context) return gui.switchContext(gui.Contexts.Menu.Context)
}) })
return nil return nil
} }
func (gui *Gui) onMenuPress() error {
selectedLine := gui.State.Panels.Menu.SelectedLine
if err := gui.State.MenuItems[selectedLine].onPress(); err != nil {
return err
}
return gui.returnFromContext()
}