summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2018-08-09 23:26:31 +1000
committerJesse Duffield <jessedduffield@gmail.com>2018-08-09 23:26:31 +1000
commitbcad80250a8a53574e86df4b708d8acdb828d6d3 (patch)
tree4a9f194b79322a70bc28c62ec6af03c1157d80e7
parent44eaccfd1490db54a419b9a367d10e05f94927e1 (diff)
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
-rw-r--r--confirmation_panel.go52
-rw-r--r--docs/Keybindings.md22
-rw-r--r--gui.go8
-rw-r--r--keybindings.go32
-rw-r--r--stash_panel.go2
5 files changed, 82 insertions, 34 deletions
diff --git a/confirmation_panel.go b/confirmation_panel.go
index 20d0ff821..362b75c49 100644
--- a/confirmation_panel.go
+++ b/confirmation_panel.go
@@ -55,7 +55,7 @@ func getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int,
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
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, "")
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.FgColor = gocui.ColorWhite
switchFocus(g, currentView, confirmationView)
- return setKeyBindings(g, handleYes, nil)
+ return setKeyBindings(g, handleConfirm, 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 {
// delete the existing confirmation panel if it exists
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
renderString(g, "confirmation", prompt)
switchFocus(g, currentView, confirmationView)
- return setKeyBindings(g, handleYes, handleNo)
+ return setKeyBindings(g, handleConfirm, handleClose)
}
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 {
+ // resising ahead of time so that the top line doesn't get hidden to make
+ // room for the cursor on the second line
+ 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
+ }
+ }
+
+ 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(handleYes)); err != nil {
+ if err := g.SetKeybinding("confirmation", gocui.KeyEnter, gocui.ModNone, wrappedConfirmationFunction(handleConfirm)); err != nil {
return err
}
-
- return g.SetKeybinding("confirmation", gocui.KeyEsc, gocui.ModNone, wrappedConfirmationFunction(handleNo))
+ 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 {
@@ -117,3 +133,23 @@ func createErrorPanel(g *gocui.Gui, message string) error {
coloredMessage := colorFunction(strings.TrimSpace(message))
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
+}
diff --git a/docs/Keybindings.md b/docs/Keybindings.md
index e15bce3b9..cefafeba5 100644
--- a/docs/Keybindings.md
+++ b/docs/Keybindings.md
@@ -2,11 +2,11 @@
## Global:
- ← → ↑ ↓: navigate
- PgUp/PgDn: scroll diff panel (use fn+up/down on osx)
- q: quit
- p: pull
- shift+P: push
+ ← → ↑ ↓/h j k l: navigate
+ PgUp/PgDn: scroll diff panel (use fn+up/down on osx)
+ q: quit
+ p: pull
+ shift+P: push
## Files Panel:
@@ -39,7 +39,7 @@
## Stash Panel:
space: apply
- k: pop
+ g: pop
d: drop
## Popup Panel:
@@ -49,8 +49,8 @@
## Resolving Merge Conflicts (Diff Panel):
- ← →: navigate conflicts
- ↑ ↓: select hunk
- space: pick hunk
- b: pick both hunks
- z: undo (only available while still inside diff panel)
+ ← →/h l: navigate conflicts
+ ↑ ↓/ k j: select hunk
+ space: pick hunk
+ b: pick both hunks
+ z: undo (only available while still inside diff panel)
diff --git a/gui.go b/gui.go
index 4765423f1..4d5d0b679 100644
--- a/gui.go
+++ b/gui.go
@@ -204,12 +204,8 @@ func layout(g *gocui.Gui) error {
v.Frame = false
}
- // If the confirmation panel is already displayed, just resize the width,
- // otherwise continue
- 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 err = resizeConfirmationPanel(g); err != nil {
+ return err
}
if v, err := g.SetView("version", width-len(version)-1, optionsTop, width, optionsTop+2, 0); err != nil {
diff --git a/keybindings.go b/keybindings.go
index 5ab572d81..0fb800d6e 100644
--- a/keybindings.go
+++ b/keybindings.go
@@ -14,13 +14,8 @@ type Binding struct {
func keybindings(g *gocui.Gui) error {
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: 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.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
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: 't', Modifier: gocui.ModNone, Handler: handleAddPatch},
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.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
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.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: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
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: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
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},
}
+
+ // 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 {
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
return err
diff --git a/stash_panel.go b/stash_panel.go
index 9fe386ac7..33c7e297b 100644
--- a/stash_panel.go
+++ b/stash_panel.go
@@ -33,7 +33,7 @@ func getSelectedStashEntry(v *gocui.View) *StashEntry {
func renderStashOptions(g *gocui.Gui) error {
return renderOptionsMap(g, map[string]string{
"space": "apply",
- "k": "pop",
+ "g": "pop",
"d": "drop",
"← → ↑ ↓": "navigate",
})