mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-27 12:32:37 +02:00
add basic vim keybindings
add keybinding for the tab key to begin a newline when writing a commit message it would have been shift+enter but currently that is not supported by gocui
This commit is contained in:
parent
44eaccfd14
commit
bcad80250a
@ -55,7 +55,7 @@ func getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int,
|
|||||||
height/2 + panelHeight/2
|
height/2 + panelHeight/2
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleYes func(*gocui.Gui, *gocui.View) error) error {
|
func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
|
||||||
// only need to fit one line
|
// only need to fit one line
|
||||||
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, "")
|
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, "")
|
||||||
if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
|
if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
|
||||||
@ -69,12 +69,12 @@ func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, hand
|
|||||||
confirmationView.Title = title
|
confirmationView.Title = title
|
||||||
confirmationView.FgColor = gocui.ColorWhite
|
confirmationView.FgColor = gocui.ColorWhite
|
||||||
switchFocus(g, currentView, confirmationView)
|
switchFocus(g, currentView, confirmationView)
|
||||||
return setKeyBindings(g, handleYes, nil)
|
return setKeyBindings(g, handleConfirm, nil)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleYes, handleNo func(*gocui.Gui, *gocui.View) error) error {
|
func createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||||
g.Update(func(g *gocui.Gui) error {
|
g.Update(func(g *gocui.Gui) error {
|
||||||
// delete the existing confirmation panel if it exists
|
// delete the existing confirmation panel if it exists
|
||||||
if view, _ := g.View("confirmation"); view != nil {
|
if view, _ := g.View("confirmation"); view != nil {
|
||||||
@ -91,20 +91,36 @@ func createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, promp
|
|||||||
confirmationView.FgColor = gocui.ColorWhite
|
confirmationView.FgColor = gocui.ColorWhite
|
||||||
renderString(g, "confirmation", prompt)
|
renderString(g, "confirmation", prompt)
|
||||||
switchFocus(g, currentView, confirmationView)
|
switchFocus(g, currentView, confirmationView)
|
||||||
return setKeyBindings(g, handleYes, handleNo)
|
return setKeyBindings(g, handleConfirm, handleClose)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setKeyBindings(g *gocui.Gui, handleYes, handleNo func(*gocui.Gui, *gocui.View) error) error {
|
func handleNewline(g *gocui.Gui, v *gocui.View) error {
|
||||||
renderString(g, "options", "esc: close, enter: confirm")
|
// resising ahead of time so that the top line doesn't get hidden to make
|
||||||
if err := g.SetKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone, wrappedConfirmationFunction(handleYes)); err != nil {
|
// room for the cursor on the second line
|
||||||
return err
|
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, v.Buffer())
|
||||||
|
if _, err := g.SetView("confirmation", x0, y0, x1, y1+1, 0); err != nil {
|
||||||
|
if err != gocui.ErrUnknownView {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return g.SetKeybinding("confirmation", gocui.KeyEsc, gocui.ModNone, wrappedConfirmationFunction(handleNo))
|
v.EditNewLine()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setKeyBindings(g *gocui.Gui, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||||
|
renderString(g, "options", "esc: close, enter: confirm")
|
||||||
|
if err := g.SetKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone, wrappedConfirmationFunction(handleConfirm)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := g.SetKeybinding("confirmation", gocui.KeyTab, gocui.ModNone, handleNewline); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return g.SetKeybinding("confirmation", gocui.KeyEsc, gocui.ModNone, wrappedConfirmationFunction(handleClose))
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, prompt string) error {
|
func createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, prompt string) error {
|
||||||
@ -117,3 +133,23 @@ func createErrorPanel(g *gocui.Gui, message string) error {
|
|||||||
coloredMessage := colorFunction(strings.TrimSpace(message))
|
coloredMessage := colorFunction(strings.TrimSpace(message))
|
||||||
return createConfirmationPanel(g, currentView, "Error", coloredMessage, nil, nil)
|
return createConfirmationPanel(g, currentView, "Error", coloredMessage, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trimTrailingNewline(str string) string {
|
||||||
|
if strings.HasSuffix(str, "\n") {
|
||||||
|
return str[:len(str)-1]
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func resizeConfirmationPanel(g *gocui.Gui) error {
|
||||||
|
// If the confirmation panel is already displayed, just resize the width,
|
||||||
|
// otherwise continue
|
||||||
|
if v, err := g.View("confirmation"); err == nil {
|
||||||
|
content := trimTrailingNewline(v.Buffer())
|
||||||
|
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, content)
|
||||||
|
if _, err = g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
## Global:
|
## Global:
|
||||||
|
|
||||||
← → ↑ ↓: navigate
|
← → ↑ ↓/h j k l: navigate
|
||||||
PgUp/PgDn: scroll diff panel (use fn+up/down on osx)
|
PgUp/PgDn: scroll diff panel (use fn+up/down on osx)
|
||||||
q: quit
|
q: quit
|
||||||
p: pull
|
p: pull
|
||||||
shift+P: push
|
shift+P: push
|
||||||
|
|
||||||
## Files Panel:
|
## Files Panel:
|
||||||
|
|
||||||
@ -39,7 +39,7 @@
|
|||||||
## Stash Panel:
|
## Stash Panel:
|
||||||
|
|
||||||
space: apply
|
space: apply
|
||||||
k: pop
|
g: pop
|
||||||
d: drop
|
d: drop
|
||||||
|
|
||||||
## Popup Panel:
|
## Popup Panel:
|
||||||
@ -49,8 +49,8 @@
|
|||||||
|
|
||||||
## Resolving Merge Conflicts (Diff Panel):
|
## Resolving Merge Conflicts (Diff Panel):
|
||||||
|
|
||||||
← →: navigate conflicts
|
← →/h l: navigate conflicts
|
||||||
↑ ↓: select hunk
|
↑ ↓/ k j: select hunk
|
||||||
space: pick hunk
|
space: pick hunk
|
||||||
b: pick both hunks
|
b: pick both hunks
|
||||||
z: undo (only available while still inside diff panel)
|
z: undo (only available while still inside diff panel)
|
||||||
|
8
gui.go
8
gui.go
@ -204,12 +204,8 @@ func layout(g *gocui.Gui) error {
|
|||||||
v.Frame = false
|
v.Frame = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the confirmation panel is already displayed, just resize the width,
|
if err = resizeConfirmationPanel(g); err != nil {
|
||||||
// otherwise continue
|
return err
|
||||||
if v, err := g.View("confirmation"); err == nil {
|
|
||||||
_, y := v.Size()
|
|
||||||
x0, y0, x1, _ := getConfirmationPanelDimensions(g, "")
|
|
||||||
g.SetView("confirmation", x0, y0, x1, y0+y+1, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("version", width-len(version)-1, optionsTop, width, optionsTop+2, 0); err != nil {
|
if v, err := g.SetView("version", width-len(version)-1, optionsTop, width, optionsTop+2, 0); err != nil {
|
||||||
|
@ -14,13 +14,8 @@ type Binding struct {
|
|||||||
|
|
||||||
func keybindings(g *gocui.Gui) error {
|
func keybindings(g *gocui.Gui) error {
|
||||||
bindings := []Binding{
|
bindings := []Binding{
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
|
|
||||||
Binding{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: quit},
|
Binding{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: quit},
|
||||||
Binding{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: quit},
|
Binding{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: quit},
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
|
|
||||||
Binding{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: scrollUpMain},
|
Binding{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: scrollUpMain},
|
||||||
Binding{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
|
Binding{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
|
||||||
Binding{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: pushFiles},
|
Binding{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: pushFiles},
|
||||||
@ -40,13 +35,17 @@ func keybindings(g *gocui.Gui) error {
|
|||||||
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
|
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
|
||||||
Binding{ViewName: "files", Key: 't', Modifier: gocui.ModNone, Handler: handleAddPatch},
|
Binding{ViewName: "files", Key: 't', Modifier: gocui.ModNone, Handler: handleAddPatch},
|
||||||
Binding{ViewName: "files", Key: 'D', Modifier: gocui.ModNone, Handler: handleResetHard},
|
Binding{ViewName: "files", Key: 'D', Modifier: gocui.ModNone, Handler: handleResetHard},
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
|
|
||||||
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},
|
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},
|
||||||
Binding{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
|
Binding{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
|
||||||
Binding{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: handlePickBothHunks},
|
Binding{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: handlePickBothHunks},
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
|
Binding{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
|
||||||
Binding{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
|
Binding{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
|
||||||
|
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
|
||||||
|
Binding{ViewName: "main", Key: 'h', Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
|
||||||
|
Binding{ViewName: "main", Key: 'l', Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
|
||||||
|
Binding{ViewName: "main", Key: 'k', Modifier: gocui.ModNone, Handler: handleSelectTop},
|
||||||
|
Binding{ViewName: "main", Key: 'j', Modifier: gocui.ModNone, Handler: handleSelectBottom},
|
||||||
Binding{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: handlePopFileSnapshot},
|
Binding{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: handlePopFileSnapshot},
|
||||||
Binding{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
|
Binding{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
|
||||||
Binding{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: handleCheckoutByName},
|
Binding{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: handleCheckoutByName},
|
||||||
@ -57,9 +56,26 @@ func keybindings(g *gocui.Gui) error {
|
|||||||
Binding{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
|
Binding{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
|
||||||
Binding{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
|
Binding{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
|
||||||
Binding{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
|
Binding{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
|
||||||
Binding{ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: handleStashPop},
|
Binding{ViewName: "stash", Key: 'g', Modifier: gocui.ModNone, Handler: handleStashPop},
|
||||||
Binding{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
|
Binding{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Would make these keybindings global but that interferes with editing
|
||||||
|
// input in the confirmation panel
|
||||||
|
for _, viewName := range []string{"files", "branches", "commits", "stash"} {
|
||||||
|
bindings = append(bindings, []Binding{
|
||||||
|
Binding{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
|
||||||
|
Binding{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
|
||||||
|
Binding{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
|
||||||
|
Binding{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
|
||||||
|
Binding{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
|
||||||
|
Binding{ViewName: viewName, Key: 'h', Modifier: gocui.ModNone, Handler: previousView},
|
||||||
|
Binding{ViewName: viewName, Key: 'l', Modifier: gocui.ModNone, Handler: nextView},
|
||||||
|
Binding{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: cursorUp},
|
||||||
|
Binding{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: cursorDown},
|
||||||
|
}...)
|
||||||
|
}
|
||||||
|
|
||||||
for _, binding := range bindings {
|
for _, binding := range bindings {
|
||||||
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -33,7 +33,7 @@ func getSelectedStashEntry(v *gocui.View) *StashEntry {
|
|||||||
func renderStashOptions(g *gocui.Gui) error {
|
func renderStashOptions(g *gocui.Gui) error {
|
||||||
return renderOptionsMap(g, map[string]string{
|
return renderOptionsMap(g, map[string]string{
|
||||||
"space": "apply",
|
"space": "apply",
|
||||||
"k": "pop",
|
"g": "pop",
|
||||||
"d": "drop",
|
"d": "drop",
|
||||||
"← → ↑ ↓": "navigate",
|
"← → ↑ ↓": "navigate",
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user