From 64996d12d9665ee50cedba6ee078649527960687 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 31 Mar 2026 17:58:05 +0200 Subject: [PATCH] Add Key type This bundles the keyName and a rune, so that we don't have to pass these around separately everywhere. This should make it easier to swap out the rune for a string when we upgrade to tcell v3. --- pkg/cheatsheet/generate.go | 2 +- pkg/cheatsheet/generate_test.go | 59 ++++++------- pkg/gocui/edit.go | 54 ++++++------ pkg/gocui/gui.go | 87 +++++++------------ pkg/gocui/key.go | 49 +++++++++++ pkg/gocui/keybinding.go | 24 +---- pkg/gocui/tcell_driver.go | 10 +-- pkg/gui/context/menu_context.go | 4 +- .../controllers/basic_commits_controller.go | 15 ++-- pkg/gui/controllers/bisect_controller.go | 17 ++-- pkg/gui/controllers/branches_controller.go | 16 ++-- .../commit_description_controller.go | 2 +- .../controllers/commit_message_controller.go | 2 +- .../controllers/commits_files_controller.go | 12 +-- .../custom_patch_options_menu_action.go | 18 ++-- pkg/gui/controllers/files_controller.go | 40 ++++----- pkg/gui/controllers/git_flow_controller.go | 9 +- pkg/gui/controllers/helpers/commits_helper.go | 6 +- .../helpers/merge_and_rebase_helper.go | 28 +++--- pkg/gui/controllers/helpers/refs_helper.go | 24 ++--- .../helpers/working_tree_helper.go | 9 +- .../controllers/local_commits_controller.go | 18 ++-- pkg/gui/controllers/prompt_controller.go | 2 +- .../controllers/search_prompt_controller.go | 2 +- pkg/gui/controllers/submodules_controller.go | 9 +- pkg/gui/controllers/tags_controller.go | 6 +- .../controllers/workspace_reset_controller.go | 14 +-- pkg/gui/editors.go | 22 ++--- pkg/gui/extras_panel.go | 5 +- pkg/gui/gui_driver.go | 12 +-- pkg/gui/keybindings.go | 20 ++--- pkg/gui/keybindings/keybindings.go | 50 +++++------ pkg/gui/menu_panel.go | 5 +- pkg/gui/options_map.go | 3 +- pkg/gui/types/common.go | 2 +- pkg/gui/types/context.go | 2 +- pkg/gui/types/keybindings.go | 4 +- pkg/integration/clients/tui.go | 32 +++---- 38 files changed, 346 insertions(+), 349 deletions(-) create mode 100644 pkg/gocui/key.go diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go index 4335e5ebf..36cbf3786 100644 --- a/pkg/cheatsheet/generate.go +++ b/pkg/cheatsheet/generate.go @@ -146,7 +146,7 @@ func getBindingSections(bindings []*types.Binding, tr *i18n.TranslationSet) []*b return false } - return (binding.Description != "" || binding.Alternative != "") && binding.Key != nil + return (binding.Description != "" || binding.Alternative != "") && binding.Key.IsSet() }) bindingsByHeader := lo.GroupBy(bindingsToDisplay, func(binding *types.Binding) header { diff --git a/pkg/cheatsheet/generate_test.go b/pkg/cheatsheet/generate_test.go index 4dbf7e3dd..bae2ec498 100644 --- a/pkg/cheatsheet/generate_test.go +++ b/pkg/cheatsheet/generate_test.go @@ -3,6 +3,7 @@ package cheatsheet import ( "testing" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/stretchr/testify/assert" @@ -27,7 +28,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, expected: []*bindingSection{ @@ -37,7 +38,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -49,7 +50,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "", Description: "quit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, expected: []*bindingSection{ @@ -59,7 +60,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "", Description: "quit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -71,17 +72,17 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "submodules", Description: "drop submodule", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, expected: []*bindingSection{ @@ -91,12 +92,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -106,7 +107,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "submodules", Description: "drop submodule", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -118,23 +119,23 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "scroll", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, { ViewName: "commits", Description: "revert commit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, expected: []*bindingSection{ @@ -144,7 +145,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "scroll", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, }, @@ -155,7 +156,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "commits", Description: "revert commit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -165,12 +166,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -182,34 +183,34 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "scroll", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, { ViewName: "commits", Description: "revert commit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "commits", Description: "scroll", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, { ViewName: "commits", Description: "page up", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, }, @@ -220,13 +221,13 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "scroll", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, { ViewName: "commits", Description: "page up", - Key: 'a', + Key: gocui.NewKeyRune('a'), Tag: "navigation", }, }, @@ -237,7 +238,7 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "commits", Description: "revert commit", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, @@ -247,12 +248,12 @@ func TestGetBindingSections(t *testing.T) { { ViewName: "files", Description: "stage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { ViewName: "files", Description: "unstage file", - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, }, diff --git a/pkg/gocui/edit.go b/pkg/gocui/edit.go index 79968357e..d941cd477 100644 --- a/pkg/gocui/edit.go +++ b/pkg/gocui/edit.go @@ -6,64 +6,64 @@ package gocui // Editor interface must be satisfied by gocui editors. type Editor interface { - Edit(v *View, key KeyName, ch rune, mod Modifier) bool + Edit(v *View, key Key, mod Modifier) bool } // The EditorFunc type is an adapter to allow the use of ordinary functions as // Editors. If f is a function with the appropriate signature, EditorFunc(f) // is an Editor object that calls f. -type EditorFunc func(v *View, key KeyName, ch rune, mod Modifier) bool +type EditorFunc func(v *View, key Key, mod Modifier) bool -// Edit calls f(v, key, ch, mod) -func (f EditorFunc) Edit(v *View, key KeyName, ch rune, mod Modifier) bool { - return f(v, key, ch, mod) +// Edit calls f(v, key, mod) +func (f EditorFunc) Edit(v *View, key Key, mod Modifier) bool { + return f(v, key, mod) } // DefaultEditor is the default editor. var DefaultEditor Editor = EditorFunc(SimpleEditor) // SimpleEditor is used as the default gocui editor. -func SimpleEditor(v *View, key KeyName, ch rune, mod Modifier) bool { +func SimpleEditor(v *View, key Key, mod Modifier) bool { switch { - case (key == KeyBackspace || key == KeyBackspace2) && (mod&ModAlt) != 0, - key == KeyCtrlW: + case (key.KeyName() == KeyBackspace || key.KeyName() == KeyBackspace2) && (mod&ModAlt) != 0, + key.KeyName() == KeyCtrlW: v.TextArea.BackSpaceWord() - case key == KeyBackspace || key == KeyBackspace2 || key == KeyCtrlH: + case key.KeyName() == KeyBackspace || key.KeyName() == KeyBackspace2 || key.KeyName() == KeyCtrlH: v.TextArea.BackSpaceChar() - case key == KeyCtrlD || key == KeyDelete: + case key.KeyName() == KeyCtrlD || key.KeyName() == KeyDelete: v.TextArea.DeleteChar() - case key == KeyArrowDown: + case key.KeyName() == KeyArrowDown: v.TextArea.MoveCursorDown() - case key == KeyArrowUp: + case key.KeyName() == KeyArrowUp: v.TextArea.MoveCursorUp() - case (key == KeyArrowLeft || ch == 'b') && (mod&ModAlt) != 0: + case (key.KeyName() == KeyArrowLeft || key.Equals(NewKeyRune('b'))) && (mod&ModAlt) != 0: v.TextArea.MoveLeftWord() - case key == KeyArrowLeft || key == KeyCtrlB: + case key.KeyName() == KeyArrowLeft || key.KeyName() == KeyCtrlB: v.TextArea.MoveCursorLeft() - case (key == KeyArrowRight || ch == 'f') && (mod&ModAlt) != 0: + case (key.KeyName() == KeyArrowRight || key.Equals(NewKeyRune('f'))) && (mod&ModAlt) != 0: v.TextArea.MoveRightWord() - case key == KeyArrowRight || key == KeyCtrlF: + case key.KeyName() == KeyArrowRight || key.KeyName() == KeyCtrlF: v.TextArea.MoveCursorRight() - case key == KeyEnter: + case key.KeyName() == KeyEnter: v.TextArea.TypeCharacter("\n") - case key == KeySpace: + case key.KeyName() == KeySpace: v.TextArea.TypeCharacter(" ") - case key == KeyInsert: + case key.KeyName() == KeyInsert: v.TextArea.ToggleOverwrite() - case key == KeyCtrlU: + case key.KeyName() == KeyCtrlU: v.TextArea.DeleteToStartOfLine() - case key == KeyCtrlK: + case key.KeyName() == KeyCtrlK: v.TextArea.DeleteToEndOfLine() - case key == KeyCtrlA || key == KeyHome: + case key.KeyName() == KeyCtrlA || key.KeyName() == KeyHome: v.TextArea.GoToStartOfLine() - case key == KeyCtrlE || key == KeyEnd: + case key.KeyName() == KeyCtrlE || key.KeyName() == KeyEnd: v.TextArea.GoToEndOfLine() - case key == KeyCtrlW: + case key.KeyName() == KeyCtrlW: v.TextArea.BackSpaceWord() - case key == KeyCtrlY: + case key.KeyName() == KeyCtrlY: v.TextArea.Yank() - case ch != 0: - v.TextArea.TypeCharacter(string(ch)) + case key.Ch() != 0: + v.TextArea.TypeCharacter(string(key.Ch())) default: return false } diff --git a/pkg/gocui/gui.go b/pkg/gocui/gui.go index 8f6429100..7b3d4dd7c 100644 --- a/pkg/gocui/gui.go +++ b/pkg/gocui/gui.go @@ -175,10 +175,10 @@ type Gui struct { Mutexes GuiMutexes OnSearchEscape func() error - // these keys must either be of type Key of rune - SearchEscapeKey any - NextSearchMatchKey any - PrevSearchMatchKey any + + SearchEscapeKey Key + NextSearchMatchKey Key + PrevSearchMatchKey Key ErrorHandler func(error) error @@ -254,9 +254,9 @@ func NewGui(opts NewGuiOpts) (*Gui, error) { g.SupportOverlaps = opts.SupportOverlaps // default keys for when searching strings in a view - g.SearchEscapeKey = KeyEsc - g.NextSearchMatchKey = 'n' - g.PrevSearchMatchKey = 'N' + g.SearchEscapeKey = NewKeyName(KeyEsc) + g.NextSearchMatchKey = NewKeyRune('n') + g.PrevSearchMatchKey = NewKeyRune('N') g.playRecording = opts.PlayRecording @@ -549,28 +549,16 @@ func (g *Gui) CurrentView() *View { // It behaves differently on different platforms. Somewhere it doesn't register Alt key press, // on others it might report Ctrl as Alt. It's not consistent and therefore it's not recommended // to use with mouse keys. -func (g *Gui) SetKeybinding(viewname string, key any, mod Modifier, handler func(*Gui, *View) error) error { - var kb *keybinding - - k, ch, err := getKey(key) - if err != nil { - return err - } - - kb = newKeybinding(viewname, k, ch, mod, handler) +func (g *Gui) SetKeybinding(viewname string, key Key, mod Modifier, handler func(*Gui, *View) error) error { + kb := newKeybinding(viewname, key, mod, handler) g.keybindings = append(g.keybindings, kb) return nil } // DeleteKeybinding deletes a keybinding. -func (g *Gui) DeleteKeybinding(viewname string, key any, mod Modifier) error { - k, ch, err := getKey(key) - if err != nil { - return err - } - +func (g *Gui) DeleteKeybinding(viewname string, key Key, mod Modifier) error { for i, kb := range g.keybindings { - if kb.viewName == viewname && kb.ch == ch && kb.key == k && kb.mod == mod { + if kb.viewName == viewname && kb.key.keyName == key.KeyName() && kb.key.ch == key.ch && kb.mod == mod { g.keybindings = append(g.keybindings[:i], g.keybindings[i+1:]...) return nil } @@ -628,21 +616,6 @@ func (g *Gui) SetRenderSearchStatusFunc(renderSearchStatusFunc func(*View, int, g.renderSearchStatusFunc = renderSearchStatusFunc } -// getKey takes an empty interface with a key and returns the corresponding -// typed Key or rune. -func getKey(key any) (KeyName, rune, error) { - switch t := key.(type) { - case nil: // Ignore keybinding if `nil` - return 0, 0, nil - case KeyName: - return t, 0, nil - case rune: - return 0, t, nil - default: - return 0, 0, errors.New("unknown type") - } -} - // userEvent represents an event triggered by the user. type userEvent struct { f func(*Gui) error @@ -1301,8 +1274,8 @@ func (g *Gui) onKey(ev *GocuiEvent) error { // seem harmful for other terminal emulators. // // KeyCtrlJ (int value 10) is '\r'. - if g.IsPasting && ev.Key == KeyCtrlJ { - ev.Key = KeyEnter + if g.IsPasting && ev.Key.KeyName() == KeyCtrlJ { + ev.Key = NewKeyName(KeyEnter) } err := g.execKeybindings(g.currentView, ev) @@ -1343,7 +1316,7 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } } - if ev.Key == MouseLeft && (ev.Mod&ModMotion) == 0 && !v.Editable && g.openHyperlink != nil { + if ev.Key.KeyName() == MouseLeft && (ev.Mod&ModMotion) == 0 && !v.Editable && g.openHyperlink != nil { if newY >= 0 && newY <= len(v.viewLines)-1 && newX >= 0 && newX <= len(v.viewLines[newY].line)-1 { if link := v.viewLines[newY].line[newX].hyperlink; link != "" { return g.openHyperlink(link, v.name) @@ -1352,14 +1325,14 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } if g.ShouldHandleMouseEvent != nil { - if !g.ShouldHandleMouseEvent(v, ev.Key) { + if !g.ShouldHandleMouseEvent(v, ev.Key.KeyName()) { // Give clients a chance to reject clicks, for example clicks in inactive views // when a modal panel is open. break } } - if !IsMouseScrollKey(ev.Key) { + if !IsMouseScrollKey(ev.Key.KeyName()) { v.SetCursor(newCx, newCy) if v.Editable { v.TextArea.SetCursor2D(newX, newY) @@ -1387,8 +1360,8 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } if IsMouseKey(ev.Key) { - isDoubleClick := g.recordClickInfo(newX, newY, ev.Key, v) - opts := ViewMouseBindingOpts{X: newX, Y: newY, Key: ev.Key, IsDoubleClick: isDoubleClick} + isDoubleClick := g.recordClickInfo(newX, newY, ev.Key.KeyName(), v) + opts := ViewMouseBindingOpts{X: newX, Y: newY, Key: ev.Key.KeyName(), IsDoubleClick: isDoubleClick} matched, err := g.execMouseKeybindings(v, ev, opts) if err != nil { return err @@ -1450,7 +1423,7 @@ func (g *Gui) recordClickInfo(x, y int, key KeyName, v *View) bool { func (g *Gui) execMouseKeybindings(view *View, ev *GocuiEvent, opts ViewMouseBindingOpts) (bool, error) { isMatch := func(binding *ViewMouseBinding) bool { return binding.ViewName == view.Name() && - ev.Key == binding.Key && + ev.Key.KeyName() == binding.Key && ev.Mod == binding.Modifier } @@ -1472,8 +1445,8 @@ func (g *Gui) execMouseKeybindings(view *View, ev *GocuiEvent, opts ViewMouseBin return false, nil } -func IsMouseKey(key any) bool { - switch key { +func IsMouseKey(key Key) bool { + switch key.KeyName() { case MouseLeft, MouseRight, @@ -1489,8 +1462,8 @@ func IsMouseKey(key any) bool { } } -func IsMouseScrollKey(key any) bool { - switch key { +func IsMouseScrollKey(keyName KeyName) bool { + switch keyName { case MouseWheelUp, MouseWheelDown, @@ -1514,11 +1487,11 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error { // if we're searching, and we've hit n/N/Esc, we ignore the default keybinding if v != nil && v.IsSearching() && ev.Mod == ModNone { - if eventMatchesKey(ev, g.NextSearchMatchKey) { + if ev.Key.Equals(g.NextSearchMatchKey) { return v.gotoNextMatch() - } else if eventMatchesKey(ev, g.PrevSearchMatchKey) { + } else if ev.Key.Equals(g.PrevSearchMatchKey) { return v.gotoPreviousMatch() - } else if eventMatchesKey(ev, g.SearchEscapeKey) { + } else if ev.Key.Equals(g.SearchEscapeKey) { v.searcher.clearSearch() if g.OnSearchEscape != nil { if err := g.OnSearchEscape(); err != nil { @@ -1535,7 +1508,7 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error { if kb.handler == nil { continue } - if !kb.matchKeypress(ev.Key, ev.Ch, ev.Mod) { + if !kb.matchKeypress(ev.Key, ev.Mod) { continue } if g.matchView(v, kb) { @@ -1550,7 +1523,7 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error { if v != nil && g.matchView(v.ParentView, kb) { matchingParentViewKb = kb } - if globalKb == nil && kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) { + if globalKb == nil && kb.viewName == "" && ((v != nil && !v.Editable) || (kb.key.keyName != KeyCtrlU && kb.key.keyName != KeyCtrlA && kb.key.keyName != KeyCtrlE)) { globalKb = kb } } @@ -1562,7 +1535,7 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error { } if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil { - matched := g.currentView.Editor.Edit(g.currentView, ev.Key, ev.Ch, ev.Mod) + matched := g.currentView.Editor.Edit(g.currentView, ev.Key, ev.Mod) if matched { return nil } @@ -1622,7 +1595,7 @@ func (g *Gui) matchView(v *View, kb *keybinding) bool { if v == nil { return false } - if v.Editable && kb.ch != 0 { + if v.Editable && kb.key.ch != 0 { return false } if kb.viewName != v.name { diff --git a/pkg/gocui/key.go b/pkg/gocui/key.go new file mode 100644 index 000000000..f5e787a22 --- /dev/null +++ b/pkg/gocui/key.go @@ -0,0 +1,49 @@ +// Copyright 2026 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocui + +import "github.com/gdamore/tcell/v2" + +type Key struct { + keyName KeyName + ch rune +} + +func NewKey(keyName KeyName, ch rune) Key { + return Key{ + keyName: keyName, + ch: ch, + } +} + +func NewKeyName(keyName KeyName) Key { + return Key{ + keyName: keyName, + ch: 0, + } +} + +func NewKeyRune(ch rune) Key { + return Key{ + keyName: KeyName(tcell.KeyRune), + ch: ch, + } +} + +func (k Key) KeyName() KeyName { + return k.keyName +} + +func (k Key) Ch() rune { + return k.ch +} + +func (k Key) IsSet() bool { + return k.keyName != 0 +} + +func (k Key) Equals(otherKey Key) bool { + return k.keyName == otherKey.keyName && k.ch == otherKey.ch +} diff --git a/pkg/gocui/keybinding.go b/pkg/gocui/keybinding.go index b50bccc0c..e05f7ce08 100644 --- a/pkg/gocui/keybinding.go +++ b/pkg/gocui/keybinding.go @@ -18,41 +18,25 @@ type Modifier tcell.ModMask // Keybindings are used to link a given key-press event with a handler. type keybinding struct { viewName string - key KeyName - ch rune + key Key mod Modifier handler func(*Gui, *View) error } // newKeybinding returns a new Keybinding object. -func newKeybinding(viewname string, key KeyName, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) { +func newKeybinding(viewname string, key Key, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) { kb = &keybinding{ viewName: viewname, key: key, - ch: ch, mod: mod, handler: handler, } return kb } -func eventMatchesKey(ev *GocuiEvent, key any) bool { - // assuming ModNone for now - if ev.Mod != ModNone { - return false - } - - k, ch, err := getKey(key) - if err != nil { - return false - } - - return k == ev.Key && ch == ev.Ch -} - // matchKeypress returns if the keybinding matches the keypress. -func (kb *keybinding) matchKeypress(keyName KeyName, ch rune, mod Modifier) bool { - return kb.key == keyName && kb.ch == ch && kb.mod == mod +func (kb *keybinding) matchKeypress(key Key, mod Modifier) bool { + return kb.key.Equals(key) && kb.mod == mod } // Special keys. diff --git a/pkg/gocui/tcell_driver.go b/pkg/gocui/tcell_driver.go index 89b8c77c6..333e7b9e9 100644 --- a/pkg/gocui/tcell_driver.go +++ b/pkg/gocui/tcell_driver.go @@ -162,8 +162,7 @@ type gocuiEventType uint8 type GocuiEvent struct { Type gocuiEventType Mod Modifier - Key KeyName - Ch rune + Key Key Width int Height int Err error @@ -292,7 +291,6 @@ func (g *Gui) pollEvent() GocuiEvent { k := tev.Key() ch := rune(0) if k == tcell.KeyRune { - k = 0 // if rune remove key (so it can match rune instead of key) ch = tev.Rune() if ch == ' ' { // special handling for spacebar @@ -329,8 +327,7 @@ func (g *Gui) pollEvent() GocuiEvent { return GocuiEvent{ Type: eventKey, - Key: KeyName(k), - Ch: ch, + Key: NewKey(KeyName(k), ch), Mod: Modifier(mod), } case *tcell.EventMouse: @@ -413,8 +410,7 @@ func (g *Gui) pollEvent() GocuiEvent { Type: eventMouse, MouseX: x, MouseY: y, - Key: mouseKey, - Ch: 0, + Key: NewKeyName(mouseKey), Mod: mouseMod, } case *tcell.EventFocus: diff --git a/pkg/gui/context/menu_context.go b/pkg/gui/context/menu_context.go index ced9666f9..e9b04061b 100644 --- a/pkg/gui/context/menu_context.go +++ b/pkg/gui/context/menu_context.go @@ -138,7 +138,7 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string { } keyLabel := "" - if item.Key != nil { + if item.Key.IsSet() { keyLabel = style.FgCyan.Sprint(keybindings.LabelFromKey(item.Key)) } @@ -205,7 +205,7 @@ func (self *MenuViewModel) GetNonModelItems() []*NonModelItem { func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { basicBindings := self.ListContextTrait.GetKeybindings(opts) menuItemsWithKeys := lo.Filter(self.menuItems, func(item *types.MenuItem, _ int) bool { - return item.Key != nil + return item.Key.IsSet() }) menuItemBindings := lo.Map(menuItemsWithKeys, func(item *types.MenuItem, _ int) *types.Binding { diff --git a/pkg/gui/controllers/basic_commits_controller.go b/pkg/gui/controllers/basic_commits_controller.go index a5bc8732e..f425addb5 100644 --- a/pkg/gui/controllers/basic_commits_controller.go +++ b/pkg/gui/controllers/basic_commits_controller.go @@ -6,6 +6,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context/traits" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" @@ -163,14 +164,14 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitSubjectToClipboard(commit) }, - Key: 's', + Key: gocui.NewKeyRune('s'), }, { Label: self.c.Tr.CommitMessage, OnPress: func() error { return self.copyCommitMessageToClipboard(commit) }, - Key: 'm', + Key: gocui.NewKeyRune('m'), }, { Label: self.c.Tr.CommitMessageBody, @@ -178,28 +179,28 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitMessageBodyToClipboard(commitMessageBody) }, - Key: 'b', + Key: gocui.NewKeyRune('b'), }, { Label: self.c.Tr.CommitURL, OnPress: func() error { return self.copyCommitURLToClipboard(commit) }, - Key: 'u', + Key: gocui.NewKeyRune('u'), }, { Label: self.c.Tr.CommitDiff, OnPress: func() error { return self.copyCommitDiffToClipboard(commit) }, - Key: 'd', + Key: gocui.NewKeyRune('d'), }, { Label: self.c.Tr.CommitAuthor, OnPress: func() error { return self.copyAuthorToClipboard(commit) }, - Key: 'a', + Key: gocui.NewKeyRune('a'), }, } @@ -208,7 +209,7 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e OnPress: func() error { return self.copyCommitTagsToClipboard(commit) }, - Key: 't', + Key: gocui.NewKeyRune('t'), } if len(commit.Tags) == 0 { diff --git a/pkg/gui/controllers/bisect_controller.go b/pkg/gui/controllers/bisect_controller.go index 9ae3eac09..15c37b88a 100644 --- a/pkg/gui/controllers/bisect_controller.go +++ b/pkg/gui/controllers/bisect_controller.go @@ -6,6 +6,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" @@ -101,7 +102,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: 'b', + Key: gocui.NewKeyRune('b'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, shortHashToMark, info.OldTerm()), @@ -114,7 +115,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: 'g', + Key: gocui.NewKeyRune('g'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.SkipCurrent, shortHashToMark), @@ -127,7 +128,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: singleItemIfNotBisecting, - Key: 's', + Key: gocui.NewKeyRune('s'), }, } if info.GetCurrentHash() != "" && info.GetCurrentHash() != commit.Hash() { @@ -142,7 +143,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c return self.afterMark(selectCurrentAfter, waitToReselect) }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'S', + Key: gocui.NewKeyRune('S'), })) } menuItems = append(menuItems, lo.ToPtr(types.MenuItem{ @@ -150,7 +151,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c OnPress: func() error { return self.c.Helpers().Bisect.Reset() }, - Key: 'r', + Key: gocui.NewKeyRune('r'), })) return self.c.Menu(types.CreateMenuOptions{ @@ -179,7 +180,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'b', + Key: gocui.NewKeyRune('b'), }, { Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortHash(), info.OldTerm()), @@ -197,7 +198,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'g', + Key: gocui.NewKeyRune('g'), }, { Label: self.c.Tr.Bisect.ChooseTerms, @@ -222,7 +223,7 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, }) return nil }, - Key: 't', + Key: gocui.NewKeyRune('t'), }, }, }) diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go index 2011bb970..268e94134 100644 --- a/pkg/gui/controllers/branches_controller.go +++ b/pkg/gui/controllers/branches_controller.go @@ -300,7 +300,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc ) viewDivergenceFromBaseBranchItem := &types.MenuItem{ LabelColumns: []string{label}, - Key: 'b', + Key: gocui.NewKeyRune('b'), OnPress: func() error { branch := self.context().GetSelected() if branch == nil { @@ -333,7 +333,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc }) return nil }, - Key: 'u', + Key: gocui.NewKeyRune('u'), } setUpstreamItem := &types.MenuItem{ @@ -358,7 +358,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }) }, - Key: 's', + Key: gocui.NewKeyRune('s'), } upstreamResetOptions := utils.ResolvePlaceholderString( @@ -391,7 +391,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }, Tooltip: upstreamResetTooltip, - Key: 'g', + Key: gocui.NewKeyRune('g'), } upstreamRebaseItem := &types.MenuItem{ @@ -404,7 +404,7 @@ func (self *BranchesController) viewUpstreamOptions(selectedBranch *models.Branc return nil }, Tooltip: upstreamRebaseTooltip, - Key: 'r', + Key: gocui.NewKeyRune('r'), } if !selectedBranch.IsTrackingRemote() { @@ -624,7 +624,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { localDeleteItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteLocalBranches, self.c.Tr.DeleteLocalBranch), - Key: 'c', + Key: gocui.NewKeyRune('c'), OnPress: func() error { return self.localDelete(branches) }, @@ -635,7 +635,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { remoteDeleteItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteRemoteBranches, self.c.Tr.DeleteRemoteBranch), - Key: 'r', + Key: gocui.NewKeyRune('r'), OnPress: func() error { return self.remoteDelete(branches) }, @@ -648,7 +648,7 @@ func (self *BranchesController) delete(branches []*models.Branch) error { deleteBothItem := &types.MenuItem{ Label: lo.Ternary(len(branches) > 1, self.c.Tr.DeleteLocalAndRemoteBranches, self.c.Tr.DeleteLocalAndRemoteBranch), - Key: 'b', + Key: gocui.NewKeyRune('b'), OnPress: func() error { return self.localAndRemoteDelete(branches) }, diff --git a/pkg/gui/controllers/commit_description_controller.go b/pkg/gui/controllers/commit_description_controller.go index 87e54cba0..09b518659 100644 --- a/pkg/gui/controllers/commit_description_controller.go +++ b/pkg/gui/controllers/commit_description_controller.go @@ -119,7 +119,7 @@ func (self *CommitDescriptionController) handleTogglePanel() error { // which is common in pasted code snippets. view := self.Context().GetView() for range 4 { - view.Editor.Edit(view, gocui.KeySpace, ' ', 0) + view.Editor.Edit(view, gocui.NewKeyRune(' '), 0) } return nil } diff --git a/pkg/gui/controllers/commit_message_controller.go b/pkg/gui/controllers/commit_message_controller.go index 53c7e761e..97865f741 100644 --- a/pkg/gui/controllers/commit_message_controller.go +++ b/pkg/gui/controllers/commit_message_controller.go @@ -130,7 +130,7 @@ func (self *CommitMessageController) handleTogglePanel() error { // switch to the description panel. view := self.context().GetView() for range 4 { - view.Editor.Edit(view, gocui.KeySpace, ' ', 0) + view.Editor.Edit(view, gocui.NewKeyRune(' '), 0) } return nil } diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 7312e148d..7cde6c15d 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -230,7 +230,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'n', + Key: gocui.NewKeyRune('n'), } copyRelativePathItem := &types.MenuItem{ Label: self.c.Tr.CopyRelativeFilePath, @@ -242,7 +242,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'p', + Key: gocui.NewKeyRune('p'), } copyAbsolutePathItem := &types.MenuItem{ Label: self.c.Tr.CopyAbsoluteFilePath, @@ -258,7 +258,7 @@ func (self *CommitFilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'P', + Key: gocui.NewKeyRune('P'), } copyFileDiffItem := &types.MenuItem{ Label: self.c.Tr.CopySelectedDiff, @@ -266,7 +266,7 @@ func (self *CommitFilesController) openCopyMenu() error { return self.copyDiffToClipboard(node.GetPath(), self.c.Tr.FileDiffCopiedToast) }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 's', + Key: gocui.NewKeyRune('s'), } copyAllDiff := &types.MenuItem{ Label: self.c.Tr.CopyAllFilesDiff, @@ -274,7 +274,7 @@ func (self *CommitFilesController) openCopyMenu() error { return self.copyDiffToClipboard(".", self.c.Tr.AllFilesDiffCopiedToast) }, DisabledReason: self.require(self.itemsSelected())(), - Key: 'a', + Key: gocui.NewKeyRune('a'), } copyFileContentItem := &types.MenuItem{ Label: self.c.Tr.CopyFileContent, @@ -295,7 +295,7 @@ func (self *CommitFilesController) openCopyMenu() error { } return nil }))(), - Key: 'c', + Key: gocui.NewKeyRune('c'), } return self.c.Menu(types.CreateMenuOptions{ diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go index 6d9074802..9ef990f79 100644 --- a/pkg/gui/controllers/custom_patch_options_menu_action.go +++ b/pkg/gui/controllers/custom_patch_options_menu_action.go @@ -31,19 +31,19 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: self.c.Tr.ResetPatch, Tooltip: self.c.Tr.ResetPatchTooltip, OnPress: self.c.Helpers().PatchBuilding.Reset, - Key: 'c', + Key: gocui.NewKeyRune('c'), }, { Label: self.c.Tr.ApplyPatch, Tooltip: self.c.Tr.ApplyPatchTooltip, OnPress: func() error { return self.handleApplyPatch(false) }, - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { Label: self.c.Tr.ApplyPatchInReverse, Tooltip: self.c.Tr.ApplyPatchInReverseTooltip, OnPress: func() error { return self.handleApplyPatch(true) }, - Key: 'r', + Key: gocui.NewKeyRune('r'), }, } @@ -53,25 +53,25 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, utils.ShortHash(self.c.Git().Patch.PatchBuilder.To)), Tooltip: self.c.Tr.RemovePatchFromOriginalCommitTooltip, OnPress: self.handleDeletePatchFromCommit, - Key: 'd', + Key: gocui.NewKeyRune('d'), }, { Label: self.c.Tr.MovePatchOutIntoIndex, Tooltip: self.c.Tr.MovePatchOutIntoIndexTooltip, OnPress: self.handleMovePatchIntoWorkingTree, - Key: 'i', + Key: gocui.NewKeyRune('i'), }, { Label: self.c.Tr.MovePatchIntoNewCommit, Tooltip: self.c.Tr.MovePatchIntoNewCommitTooltip, OnPress: self.handlePullPatchIntoNewCommit, - Key: 'n', + Key: gocui.NewKeyRune('n'), }, { Label: self.c.Tr.MovePatchIntoNewCommitBefore, Tooltip: self.c.Tr.MovePatchIntoNewCommitBeforeTooltip, OnPress: self.handlePullPatchIntoNewCommitBefore, - Key: 'N', + Key: gocui.NewKeyRune('N'), }, }...) @@ -93,7 +93,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error { Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Hash()), Tooltip: self.c.Tr.MovePatchToSelectedCommitTooltip, OnPress: self.handleMovePatchToSelectedCommit, - Key: 'm', + Key: gocui.NewKeyRune('m'), DisabledReason: disabledReason, }, }, menuItems[1:]..., @@ -107,7 +107,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error { { Label: self.c.Tr.CopyPatchToClipboard, OnPress: func() error { return self.copyPatchToClipboard() }, - Key: 'y', + Key: gocui.NewKeyRune('y'), }, }...) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 234a2b65c..e75a5ee3f 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -671,14 +671,14 @@ func (self *FilesController) handleNonInlineConflict(file *models.File) error { OnPress: func() error { return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile) }, - Key: 'k', + Key: gocui.NewKeyRune('k'), } deleteItem := &types.MenuItem{ Label: self.c.Tr.MergeConflictDeleteFile, OnPress: func() error { return handle(self.c.Git().WorkingTree.RemoveConflictedFile, self.c.Tr.Actions.ResolveConflictByDeletingFile) }, - Key: 'd', + Key: gocui.NewKeyRune('d'), } items := []*types.MenuItem{} switch file.ShortStatus { @@ -856,7 +856,7 @@ func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error } return nil }, - Key: 'i', + Key: gocui.NewKeyRune('i'), }, { LabelColumns: []string{self.c.Tr.ExcludeFile}, @@ -866,7 +866,7 @@ func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error } return nil }, - Key: 'e', + Key: gocui.NewKeyRune('e'), }, }, }) @@ -950,7 +950,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayStaged) }, - Key: 's', + Key: gocui.NewKeyRune('s'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayStaged), }, { @@ -958,7 +958,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUnstaged) }, - Key: 'u', + Key: gocui.NewKeyRune('u'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUnstaged), }, { @@ -966,7 +966,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayTracked) }, - Key: 't', + Key: gocui.NewKeyRune('t'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayTracked), }, { @@ -974,7 +974,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUntracked) }, - Key: 'T', + Key: gocui.NewKeyRune('T'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUntracked), }, { @@ -982,7 +982,7 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayAll) }, - Key: 'r', + Key: gocui.NewKeyRune('r'), Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayAll), }, }, @@ -1092,7 +1092,7 @@ func (self *FilesController) createStashMenu() error { } return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashAllChanges) }, - Key: 'a', + Key: gocui.NewKeyRune('a'), }, { Label: self.c.Tr.StashAllChangesKeepIndex, @@ -1103,14 +1103,14 @@ func (self *FilesController) createStashMenu() error { // if there are no staged files it behaves the same as Stash.Save return self.handleStashSave(self.c.Git().Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex) }, - Key: 'i', + Key: gocui.NewKeyRune('i'), }, { Label: self.c.Tr.StashIncludeUntrackedChanges, OnPress: func() error { return self.handleStashSave(self.c.Git().Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges) }, - Key: 'U', + Key: gocui.NewKeyRune('U'), }, { Label: self.c.Tr.StashStagedChanges, @@ -1121,7 +1121,7 @@ func (self *FilesController) createStashMenu() error { } return self.handleStashSave(self.c.Git().Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges) }, - Key: 's', + Key: gocui.NewKeyRune('s'), }, { Label: self.c.Tr.StashUnstagedChanges, @@ -1135,7 +1135,7 @@ func (self *FilesController) createStashMenu() error { // ordinary stash return self.handleStashSave(self.c.Git().Stash.Push, self.c.Tr.Actions.StashUnstagedChanges) }, - Key: 'u', + Key: gocui.NewKeyRune('u'), }, }, }) @@ -1182,7 +1182,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'n', + Key: gocui.NewKeyRune('n'), } copyRelativePathItem := &types.MenuItem{ Label: self.c.Tr.CopyRelativeFilePath, @@ -1194,7 +1194,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'p', + Key: gocui.NewKeyRune('p'), } copyAbsolutePathItem := &types.MenuItem{ Label: self.c.Tr.CopyAbsoluteFilePath, @@ -1210,7 +1210,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, DisabledReason: self.require(self.singleItemSelected())(), - Key: 'P', + Key: gocui.NewKeyRune('P'), } copyFileDiffItem := &types.MenuItem{ Label: self.c.Tr.CopySelectedDiff, @@ -1236,7 +1236,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, ))(), - Key: 's', + Key: gocui.NewKeyRune('s'), } copyAllDiff := &types.MenuItem{ Label: self.c.Tr.CopyAllFilesDiff, @@ -1261,7 +1261,7 @@ func (self *FilesController) openCopyMenu() error { return nil }, )(), - Key: 'a', + Key: gocui.NewKeyRune('a'), } return self.c.Menu(types.CreateMenuOptions{ @@ -1534,7 +1534,7 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) return nil }, - Key: 'u', + Key: gocui.NewKeyRune('u'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.DiscardUnstagedTooltip, map[string]string{ diff --git a/pkg/gui/controllers/git_flow_controller.go b/pkg/gui/controllers/git_flow_controller.go index cf996e5d9..2fcb4e5f5 100644 --- a/pkg/gui/controllers/git_flow_controller.go +++ b/pkg/gui/controllers/git_flow_controller.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" ) @@ -82,22 +83,22 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er { Label: "start feature", OnPress: startHandler("feature"), - Key: 'f', + Key: gocui.NewKeyRune('f'), }, { Label: "start hotfix", OnPress: startHandler("hotfix"), - Key: 'h', + Key: gocui.NewKeyRune('h'), }, { Label: "start bugfix", OnPress: startHandler("bugfix"), - Key: 'b', + Key: gocui.NewKeyRune('b'), }, { Label: "start release", OnPress: startHandler("release"), - Key: 'r', + Key: gocui.NewKeyRune('r'), }, }, }) diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 55b759b48..810861401 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -228,7 +228,7 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S OnPress: func() error { return self.SwitchToEditor() }, - Key: 'e', + Key: gocui.NewKeyRune('e'), DisabledReason: disabledReasonForOpenInEditor, }, { @@ -236,14 +236,14 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S OnPress: func() error { return self.addCoAuthor(suggestionFunc) }, - Key: 'c', + Key: gocui.NewKeyRune('c'), }, { Label: self.c.Tr.PasteCommitMessageFromClipboard, OnPress: func() error { return self.pasteCommitMessageFromClipboard() }, - Key: 'p', + Key: gocui.NewKeyRune('p'), }, } return self.c.Menu(types.CreateMenuOptions{ diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index 8b8d1fb7c..986f61b16 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -39,17 +39,17 @@ const ( func (self *MergeAndRebaseHelper) CreateRebaseOptionsMenu() error { type optionAndKey struct { option string - key types.Key + key gocui.Key } options := []optionAndKey{ - {option: REBASE_OPTION_CONTINUE, key: 'c'}, - {option: REBASE_OPTION_ABORT, key: 'a'}, + {option: REBASE_OPTION_CONTINUE, key: gocui.NewKeyRune('c')}, + {option: REBASE_OPTION_ABORT, key: gocui.NewKeyRune('a')}, } if self.c.Git().Status.WorkingTreeState().CanSkip() { options = append(options, optionAndKey{ - option: REBASE_OPTION_SKIP, key: 's', + option: REBASE_OPTION_SKIP, key: gocui.NewKeyRune('s'), }) } @@ -198,7 +198,7 @@ func (self *MergeAndRebaseHelper) PromptForConflictHandling() error { OnPress: func() error { return self.genericMergeCommand(REBASE_OPTION_ABORT) }, - Key: 'a', + Key: gocui.NewKeyRune('a'), }, }, HideCancel: true, @@ -284,7 +284,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.SimpleRebase, map[string]string{"ref": ref}, ), - Key: 's', + Key: gocui.NewKeyRune('s'), DisabledReason: disabledReason, OnPress: func() error { self.c.LogAction(self.c.Tr.Actions.RebaseBranch) @@ -308,7 +308,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.InteractiveRebase, map[string]string{"ref": ref}, ), - Key: 'i', + Key: gocui.NewKeyRune('i'), DisabledReason: disabledReason, Tooltip: self.c.Tr.InteractiveRebaseTooltip, OnPress: func() error { @@ -334,7 +334,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { Label: utils.ResolvePlaceholderString(self.c.Tr.RebaseOntoBaseBranch, map[string]string{"baseBranch": ShortBranchName(baseBranch)}, ), - Key: 'b', + Key: gocui.NewKeyRune('b'), DisabledReason: baseBranchDisabledReason, Tooltip: self.c.Tr.RebaseOntoBaseBranchTooltip, OnPress: func() error { @@ -392,7 +392,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e firstRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), - Key: 'm', + Key: gocui.NewKeyRune('m'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeFastForwardTooltip, map[string]string{ @@ -406,7 +406,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e secondRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeNonFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_NON_FAST_FORWARD), - Key: 'n', + Key: gocui.NewKeyRune('n'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeNonFastForwardTooltip, map[string]string{ @@ -419,7 +419,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e firstRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeNonFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), - Key: 'm', + Key: gocui.NewKeyRune('m'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeNonFastForwardTooltip, map[string]string{ @@ -432,7 +432,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e secondRegularMergeItem = &types.MenuItem{ Label: self.c.Tr.RegularMergeFastForward, OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_FAST_FORWARD), - Key: 'f', + Key: gocui.NewKeyRune('f'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.RegularMergeFastForwardTooltip, map[string]string{ @@ -464,7 +464,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e { Label: self.c.Tr.SquashMergeUncommitted, OnPress: self.SquashMergeUncommitted(refName), - Key: 's', + Key: gocui.NewKeyRune('s'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.SquashMergeUncommittedTooltip, map[string]string{ @@ -475,7 +475,7 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e { Label: self.c.Tr.SquashMergeCommitted, OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName), - Key: 'S', + Key: gocui.NewKeyRune('S'), Tooltip: utils.ResolvePlaceholderString( self.c.Tr.SquashMergeCommittedTooltip, map[string]string{ diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index c10fad81d..43c8c93c6 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -216,15 +216,15 @@ func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string func (self *RefsHelper) CreateSortOrderMenu(sortOptionsOrder []string, menuPrompt string, onSelected func(sortOrder string) error, currentValue string) error { type sortMenuOption struct { - key types.Key + key gocui.Key label string description string sortOrder string } availableSortOptions := map[string]sortMenuOption{ - "recency": {label: self.c.Tr.SortByRecency, description: self.c.Tr.SortBasedOnReflog, key: 'r'}, - "alphabetical": {label: self.c.Tr.SortAlphabetical, description: "--sort=refname", key: 'a'}, - "date": {label: self.c.Tr.SortByDate, description: "--sort=-committerdate", key: 'd'}, + "recency": {label: self.c.Tr.SortByRecency, description: self.c.Tr.SortBasedOnReflog, key: gocui.NewKeyRune('r')}, + "alphabetical": {label: self.c.Tr.SortAlphabetical, description: "--sort=refname", key: gocui.NewKeyRune('a')}, + "date": {label: self.c.Tr.SortByDate, description: "--sort=-committerdate", key: gocui.NewKeyRune('d')}, } sortOptions := make([]sortMenuOption, 0, len(sortOptionsOrder)) for _, key := range sortOptionsOrder { @@ -260,14 +260,14 @@ func (self *RefsHelper) CreateGitResetMenu(name string, ref string) error { type strengthWithKey struct { strength string label string - key types.Key + key gocui.Key tooltip string } strengths := []strengthWithKey{ // not i18'ing because it's git terminology - {strength: "mixed", label: "Mixed reset", key: 'm', tooltip: self.c.Tr.ResetMixedTooltip}, - {strength: "soft", label: "Soft reset", key: 's', tooltip: self.c.Tr.ResetSoftTooltip}, - {strength: "hard", label: "Hard reset", key: 'h', tooltip: self.c.Tr.ResetHardTooltip}, + {strength: "mixed", label: "Mixed reset", key: gocui.NewKeyRune('m'), tooltip: self.c.Tr.ResetMixedTooltip}, + {strength: "soft", label: "Soft reset", key: gocui.NewKeyRune('s'), tooltip: self.c.Tr.ResetSoftTooltip}, + {strength: "hard", label: "Hard reset", key: gocui.NewKeyRune('h'), tooltip: self.c.Tr.ResetHardTooltip}, } menuItems := lo.Map(strengths, func(row strengthWithKey, _ int) *types.MenuItem { @@ -312,15 +312,15 @@ func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error { self.c.LogAction(self.c.Tr.Actions.CheckoutCommit) return self.CheckoutRef(hash, types.CheckoutRefOptions{}) }, - Key: 'd', + Key: gocui.NewKeyRune('d'), }, } if len(branches) > 0 { menuItems = append(menuItems, lo.Map(branches, func(branch *models.Branch, index int) *types.MenuItem { - var key types.Key + var key gocui.Key if index < 9 { - key = rune(index + 1 + '0') // Convert 1-based index to key + key = gocui.NewKeyRune(rune(index + 1 + '0')) // Convert 1-based index to key } return &types.MenuItem{ LabelColumns: []string{fmt.Sprintf(self.c.Tr.Actions.CheckoutBranchAtCommit, branch.Name)}, @@ -336,7 +336,7 @@ func (self *RefsHelper) CreateCheckoutMenu(commit *models.Commit) error { LabelColumns: []string{self.c.Tr.Actions.CheckoutBranch}, OnPress: func() error { return nil }, DisabledReason: &types.DisabledReason{Text: self.c.Tr.NoBranchesFoundAtCommitTooltip}, - Key: '1', + Key: gocui.NewKeyRune('1'), }) } diff --git a/pkg/gui/controllers/helpers/working_tree_helper.go b/pkg/gui/controllers/helpers/working_tree_helper.go index b6a5407bb..28aeb56f6 100644 --- a/pkg/gui/controllers/helpers/working_tree_helper.go +++ b/pkg/gui/controllers/helpers/working_tree_helper.go @@ -10,6 +10,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -382,7 +383,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--ours") }, - Key: 'c', + Key: gocui.NewKeyRune('c'), }, { LabelColumns: []string{ @@ -392,7 +393,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--theirs") }, - Key: 'i', + Key: gocui.NewKeyRune('i'), }, { LabelColumns: []string{ @@ -402,7 +403,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin OnPress: func() error { return onMergeStrategySelected("--union") }, - Key: 'b', + Key: gocui.NewKeyRune('b'), }, { LabelColumns: []string{ @@ -410,7 +411,7 @@ func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []strin cmdColor.Sprint("git mergetool"), }, OnPress: self.OpenMergeTool, - Key: 'm', + Key: gocui.NewKeyRune('m'), }, }, }) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index c8a8045e8..31c746750 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -361,7 +361,7 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star Items: []*types.MenuItem{ { Label: self.c.Tr.Fixup, - Key: 'f', + Key: gocui.NewKeyRune('f'), OnPress: func() error { return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommit) @@ -372,7 +372,7 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star }, { Label: self.c.Tr.FixupKeepMessage, - Key: 'c', + Key: gocui.NewKeyRune('c'), OnPress: func() error { return self.c.WithWaitingStatus(self.c.Tr.FixingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommitKeepMessage) @@ -403,7 +403,7 @@ func (self *LocalCommitsController) setFixupMessage(commit *models.Commit) error Items: []*types.MenuItem{ { Label: self.c.Tr.FixupDiscardMessage, - Key: 'f', + Key: gocui.NewKeyRune('f'), OnPress: func() error { return self.updateTodosWithFlag(todo.Fixup, []*models.Commit{commit}, "") }, @@ -411,7 +411,7 @@ func (self *LocalCommitsController) setFixupMessage(commit *models.Commit) error }, { Label: self.c.Tr.FixupKeepMessage, - Key: 'c', + Key: gocui.NewKeyRune('c'), OnPress: func() error { return self.updateTodosWithFlag(todo.Fixup, []*models.Commit{commit}, "-C") }, @@ -1000,7 +1000,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err Items: []*types.MenuItem{ { Label: self.c.Tr.FixupMenu_Fixup, - Key: 'f', + Key: gocui.NewKeyRune('f'), OnPress: func() error { return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error { self.c.LogAction(self.c.Tr.Actions.CreateFixupCommit) @@ -1024,7 +1024,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err }, { Label: self.c.Tr.FixupMenu_AmendWithChanges, - Key: 'a', + Key: gocui.NewKeyRune('a'), OnPress: func() error { return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error { return self.createAmendCommit(commit, true) @@ -1035,7 +1035,7 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err }, { Label: self.c.Tr.FixupMenu_AmendWithoutChanges, - Key: 'r', + Key: gocui.NewKeyRune('r'), OnPress: func() error { return self.createAmendCommit(commit, false) }, Tooltip: self.c.Tr.FixupMenu_AmendWithoutChangesTooltip, }, @@ -1134,14 +1134,14 @@ func (self *LocalCommitsController) squashFixupCommits() error { Label: self.c.Tr.SquashCommitsInCurrentBranch, OnPress: self.squashAllFixupsInCurrentBranch, DisabledReason: self.canFindCommitForSquashFixupsInCurrentBranch(), - Key: 'b', + Key: gocui.NewKeyRune('b'), Tooltip: self.c.Tr.SquashCommitsInCurrentBranchTooltip, }, { Label: self.c.Tr.SquashCommitsAboveSelectedCommit, OnPress: self.withItem(self.squashAllFixupsAboveSelectedCommit), DisabledReason: self.singleItemSelected()(), - Key: 'a', + Key: gocui.NewKeyRune('a'), Tooltip: self.c.Tr.SquashCommitsAboveSelectedTooltip, }, }, diff --git a/pkg/gui/controllers/prompt_controller.go b/pkg/gui/controllers/prompt_controller.go index 1ad19fa97..1ff40a0ef 100644 --- a/pkg/gui/controllers/prompt_controller.go +++ b/pkg/gui/controllers/prompt_controller.go @@ -27,7 +27,7 @@ func NewPromptController( func (self *PromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { bindings := []*types.Binding{ { - Key: gocui.KeyEnter, + Key: gocui.NewKeyName(gocui.KeyEnter), Handler: func() error { return self.context().State.OnConfirm() }, Description: self.c.Tr.Confirm, DisplayOnScreen: true, diff --git a/pkg/gui/controllers/search_prompt_controller.go b/pkg/gui/controllers/search_prompt_controller.go index 6e0d0b824..e141ca42a 100644 --- a/pkg/gui/controllers/search_prompt_controller.go +++ b/pkg/gui/controllers/search_prompt_controller.go @@ -24,7 +24,7 @@ func NewSearchPromptController( func (self *SearchPromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { return []*types.Binding{ { - Key: gocui.KeyEnter, + Key: gocui.NewKeyName(gocui.KeyEnter), Modifier: gocui.ModNone, Handler: self.confirm, }, diff --git a/pkg/gui/controllers/submodules_controller.go b/pkg/gui/controllers/submodules_controller.go index c3f758bbe..f6e12cfe4 100644 --- a/pkg/gui/controllers/submodules_controller.go +++ b/pkg/gui/controllers/submodules_controller.go @@ -94,7 +94,6 @@ func (self *SubmodulesController) GetKeybindings(opts types.KeybindingsOpts) []* OpensMenu: true, }, { - Key: nil, Handler: self.easterEgg, Description: self.c.Tr.EasterEgg, }, @@ -234,7 +233,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: 'i', + Key: gocui.NewKeyRune('i'), }, { LabelColumns: []string{self.c.Tr.BulkUpdateSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateCmdObj().ToString())}, @@ -249,7 +248,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: 'u', + Key: gocui.NewKeyRune('u'), }, { LabelColumns: []string{self.c.Tr.BulkUpdateRecursiveSubmodules, style.FgYellow.Sprint(self.c.Git().Submodule.BulkUpdateRecursivelyCmdObj().ToString())}, @@ -264,7 +263,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: 'r', + Key: gocui.NewKeyRune('r'), }, { LabelColumns: []string{self.c.Tr.BulkDeinitSubmodules, style.FgRed.Sprint(self.c.Git().Submodule.BulkDeinitCmdObj().ToString())}, @@ -279,7 +278,7 @@ func (self *SubmodulesController) openBulkActionsMenu() error { return nil }) }, - Key: 'd', + Key: gocui.NewKeyRune('d'), }, }, }) diff --git a/pkg/gui/controllers/tags_controller.go b/pkg/gui/controllers/tags_controller.go index 22c208a38..1e274c077 100644 --- a/pkg/gui/controllers/tags_controller.go +++ b/pkg/gui/controllers/tags_controller.go @@ -282,14 +282,14 @@ func (self *TagsController) delete(tag *models.Tag) error { menuItems := []*types.MenuItem{ { Label: self.c.Tr.DeleteLocalTag, - Key: 'c', + Key: gocui.NewKeyRune('c'), OnPress: func() error { return self.localDelete(tag) }, }, { Label: self.c.Tr.DeleteRemoteTag, - Key: 'r', + Key: gocui.NewKeyRune('r'), OpensMenu: true, OnPress: func() error { return self.remoteDelete(tag) @@ -297,7 +297,7 @@ func (self *TagsController) delete(tag *models.Tag) error { }, { Label: self.c.Tr.DeleteLocalAndRemoteTag, - Key: 'b', + Key: gocui.NewKeyRune('b'), OpensMenu: true, OnPress: func() error { return self.localAndRemoteDelete(tag) diff --git a/pkg/gui/controllers/workspace_reset_controller.go b/pkg/gui/controllers/workspace_reset_controller.go index c19d12377..48ccfd298 100644 --- a/pkg/gui/controllers/workspace_reset_controller.go +++ b/pkg/gui/controllers/workspace_reset_controller.go @@ -53,7 +53,7 @@ func (self *FilesController) createResetMenu() error { }) return nil }, - Key: 'x', + Key: gocui.NewKeyRune('x'), Tooltip: self.c.Tr.NukeDescription, }, { @@ -72,7 +72,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: 'u', + Key: gocui.NewKeyRune('u'), }, { LabelColumns: []string{ @@ -90,7 +90,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: 'c', + Key: gocui.NewKeyRune('c'), }, { LabelColumns: []string{ @@ -115,7 +115,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: 'S', + Key: gocui.NewKeyRune('S'), }, { LabelColumns: []string{ @@ -133,7 +133,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: 's', + Key: gocui.NewKeyRune('s'), }, { LabelColumns: []string{ @@ -151,7 +151,7 @@ func (self *FilesController) createResetMenu() error { ) return nil }, - Key: 'm', + Key: gocui.NewKeyRune('m'), }, { LabelColumns: []string{ @@ -176,7 +176,7 @@ func (self *FilesController) createResetMenu() error { }, }) }, - Key: 'h', + Key: gocui.NewKeyRune('h'), }, } diff --git a/pkg/gui/editors.go b/pkg/gui/editors.go index aeddda625..bf633db9e 100644 --- a/pkg/gui/editors.go +++ b/pkg/gui/editors.go @@ -4,33 +4,33 @@ import ( "github.com/jesseduffield/lazygit/pkg/gocui" ) -func (gui *Gui) handleEditorKeypress(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier, allowMultiline bool) bool { - if key == gocui.KeyEnter && allowMultiline { +func (gui *Gui) handleEditorKeypress(v *gocui.View, key gocui.Key, mod gocui.Modifier, allowMultiline bool) bool { + if key.KeyName() == gocui.KeyEnter && allowMultiline { v.TextArea.TypeCharacter("\n") v.RenderTextArea() return true } - return gocui.DefaultEditor.Edit(v, key, ch, mod) + return gocui.DefaultEditor.Edit(v, key, mod) } // we've just copy+pasted the editor from gocui to here so that we can also re- // render the commit message length on each keypress -func (gui *Gui) commitMessageEditor(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - matched := gui.handleEditorKeypress(v, key, ch, mod, false) +func (gui *Gui) commitMessageEditor(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + matched := gui.handleEditorKeypress(v, key, mod, false) v.RenderTextArea() gui.c.Contexts().CommitMessage.RenderSubtitle() return matched } -func (gui *Gui) commitDescriptionEditor(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - matched := gui.handleEditorKeypress(v, key, ch, mod, true) +func (gui *Gui) commitDescriptionEditor(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + matched := gui.handleEditorKeypress(v, key, mod, true) v.RenderTextArea() return matched } -func (gui *Gui) promptEditor(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - matched := gui.handleEditorKeypress(v, key, ch, mod, false) +func (gui *Gui) promptEditor(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + matched := gui.handleEditorKeypress(v, key, mod, false) v.RenderTextArea() @@ -46,8 +46,8 @@ func (gui *Gui) promptEditor(v *gocui.View, key gocui.KeyName, ch rune, mod gocu return matched } -func (gui *Gui) searchEditor(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - matched := gui.handleEditorKeypress(v, key, ch, mod, false) +func (gui *Gui) searchEditor(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + matched := gui.handleEditorKeypress(v, key, mod, false) v.RenderTextArea() searchString := v.TextArea.GetContent() diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go index 5efd1eb51..03e82345a 100644 --- a/pkg/gui/extras_panel.go +++ b/pkg/gui/extras_panel.go @@ -3,6 +3,7 @@ package gui import ( "io" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -14,7 +15,7 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error { Items: []*types.MenuItem{ { Label: gui.c.Tr.ToggleShowCommandLog, - Key: 't', + Key: gocui.NewKeyRune('t'), OnPress: func() error { currentContext := gui.c.Context().CurrentStatic() if gui.c.State().GetShowExtrasWindow() && currentContext.GetKey() == context.COMMAND_LOG_CONTEXT_KEY { @@ -29,7 +30,7 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error { }, { Label: gui.c.Tr.FocusCommandLog, - Key: 'f', + Key: gocui.NewKeyRune('f'), OnPress: gui.handleFocusCommandLog, }, }, diff --git a/pkg/gui/gui_driver.go b/pkg/gui/gui_driver.go index fee9dfe7f..01d4c99a7 100644 --- a/pkg/gui/gui_driver.go +++ b/pkg/gui/gui_driver.go @@ -31,18 +31,8 @@ func (self *GuiDriver) PressKey(keyStr string) { key := keybindings.GetKey(keyStr) - var r rune - var tcellKey tcell.Key - switch v := key.(type) { - case rune: - r = v - tcellKey = tcell.KeyRune - case gocui.KeyName: - tcellKey = tcell.Key(v) - } - self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( - tcell.NewEventKey(tcellKey, r, tcell.ModNone), + tcell.NewEventKey(tcell.Key(key.KeyName()), key.Ch(), tcell.ModNone), 0, ) diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 132f68a54..7780f74b0 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -180,7 +180,7 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin }, { ViewName: "information", - Key: gocui.MouseLeft, + Key: gocui.NewKeyName(gocui.MouseLeft), Modifier: gocui.ModNone, Handler: gui.handleInfoClick, }, @@ -201,27 +201,27 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin }, { ViewName: "main", - Key: gocui.MouseWheelDown, + Key: gocui.NewKeyName(gocui.MouseWheelDown), Handler: gui.scrollDownMain, Description: gui.c.Tr.ScrollDown, Alternative: "fn+up", }, { ViewName: "main", - Key: gocui.MouseWheelUp, + Key: gocui.NewKeyName(gocui.MouseWheelUp), Handler: gui.scrollUpMain, Description: gui.c.Tr.ScrollUp, Alternative: "fn+down", }, { ViewName: "secondary", - Key: gocui.MouseWheelDown, + Key: gocui.NewKeyName(gocui.MouseWheelDown), Modifier: gocui.ModNone, Handler: gui.scrollDownSecondary, }, { ViewName: "secondary", - Key: gocui.MouseWheelUp, + Key: gocui.NewKeyName(gocui.MouseWheelUp), Modifier: gocui.ModNone, Handler: gui.scrollUpSecondary, }, @@ -251,12 +251,12 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin }, { ViewName: "confirmation", - Key: gocui.MouseWheelUp, + Key: gocui.NewKeyName(gocui.MouseWheelUp), Handler: gui.scrollUpConfirmationPanel, }, { ViewName: "confirmation", - Key: gocui.MouseWheelDown, + Key: gocui.NewKeyName(gocui.MouseWheelDown), Handler: gui.scrollDownConfirmationPanel, }, { @@ -304,12 +304,12 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin }, { ViewName: "extras", - Key: gocui.MouseWheelUp, + Key: gocui.NewKeyName(gocui.MouseWheelUp), Handler: gui.scrollUpExtra, }, { ViewName: "extras", - Key: gocui.MouseWheelDown, + Key: gocui.NewKeyName(gocui.MouseWheelDown), Handler: gui.scrollDownExtra, }, { @@ -379,7 +379,7 @@ func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBin { ViewName: "extras", Tag: "navigation", - Key: gocui.MouseLeft, + Key: gocui.NewKeyName(gocui.MouseLeft), Modifier: gocui.ModNone, Handler: gui.handleFocusCommandLog, }, diff --git a/pkg/gui/keybindings/keybindings.go b/pkg/gui/keybindings/keybindings.go index 333c0569c..4bd2f4e75 100644 --- a/pkg/gui/keybindings/keybindings.go +++ b/pkg/gui/keybindings/keybindings.go @@ -1,50 +1,50 @@ package keybindings import ( - "fmt" "log" "strings" "unicode/utf8" + "github.com/gdamore/tcell/v2" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/constants" "github.com/jesseduffield/lazygit/pkg/gocui" - "github.com/jesseduffield/lazygit/pkg/gui/types" ) -func LabelFromKey(key types.Key) string { - if key == nil { +func LabelFromKey(key gocui.Key) string { + if !key.IsSet() { return "" } - keyInt := 0 - - switch key := key.(type) { - case rune: - keyInt = int(key) - case gocui.KeyName: - value, ok := config.LabelByKey[key] - if ok { - return value - } - keyInt = int(key) + if key.KeyName() == gocui.KeyName(tcell.KeyRune) { + return string(key.Ch()) } - return fmt.Sprintf("%c", keyInt) + value, ok := config.LabelByKey[key.KeyName()] + if ok { + return value + } + + return "unknown" } -func GetKey(key string) types.Key { - runeCount := utf8.RuneCountInString(key) +func GetKey(key string) gocui.Key { if key == "" { - return nil - } else if runeCount > 1 { - binding, ok := config.KeyByLabel[strings.ToLower(key)] + return gocui.Key{} + } + + runeCount := utf8.RuneCountInString(key) + if runeCount > 1 { + keyName, ok := config.KeyByLabel[strings.ToLower(key)] if !ok { log.Fatalf("Unrecognized key %s for keybinding. For permitted values see %s", strings.ToLower(key), constants.Links.Docs.CustomKeybindings) } - return binding - } else if runeCount == 1 { - return []rune(key)[0] + return gocui.NewKeyName(keyName) } - return nil + + if runeCount == 1 { + return gocui.NewKeyRune([]rune(key)[0]) + } + + return gocui.Key{} } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 4c6a44667..e410f934f 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -3,6 +3,7 @@ package gui import ( "fmt" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/theme" @@ -26,7 +27,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { maxColumnSize := 1 - essentialKeys := []types.Key{ + essentialKeys := []gocui.Key{ keybindings.GetKey(gui.c.UserConfig().Keybinding.Universal.ConfirmMenu), keybindings.GetKey(gui.c.UserConfig().Keybinding.Universal.Return), keybindings.GetKey(gui.c.UserConfig().Keybinding.Universal.PrevItem), @@ -46,7 +47,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { // Remove all item keybindings that are the same as one of the essential bindings if !opts.KeepConflictingKeybindings && lo.Contains(essentialKeys, item.Key) { - item.Key = nil + item.Key = gocui.Key{} } } diff --git a/pkg/gui/options_map.go b/pkg/gui/options_map.go index 94bf64a40..f43b63a27 100644 --- a/pkg/gui/options_map.go +++ b/pkg/gui/options_map.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/jesseduffield/generics/set" + "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" @@ -40,7 +41,7 @@ func (self *OptionsMapMgr) renderContextOptionsMap() { globalBindings := self.c.Contexts().Global.GetKeybindings(self.c.KeybindingsOpts()) currentContextKeys := set.NewFromSlice( - lo.Map(currentContextBindings, func(binding *types.Binding, _ int) types.Key { + lo.Map(currentContextBindings, func(binding *types.Binding, _ int) gocui.Key { return binding.Key })) diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 1b06baa16..0afbd5ed8 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -257,7 +257,7 @@ type MenuItem struct { // If Key is defined it allows the user to press the key to invoke the menu // item, as opposed to having to navigate to it - Key Key + Key gocui.Key // A widget to show in front of the menu item. Supported widget types are // checkboxes and radio buttons, diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 4f1b90c64..35201c6cf 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -239,7 +239,7 @@ type OnFocusLostOpts struct { type ContextKey string type KeybindingsOpts struct { - GetKey func(key string) Key + GetKey func(key string) gocui.Key Config config.KeybindingConfig Guards KeybindingGuards } diff --git a/pkg/gui/types/keybindings.go b/pkg/gui/types/keybindings.go index aa3a95e7e..cc5fa2cf5 100644 --- a/pkg/gui/types/keybindings.go +++ b/pkg/gui/types/keybindings.go @@ -5,15 +5,13 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/style" ) -type Key any // FIXME: find out how to get `gocui.Key | rune` - // Binding - a keybinding mapping a key and modifier to a handler. The keypress // is only handled if the given view has focus, or handled globally if the view // is "" type Binding struct { ViewName string Handler func() error - Key Key + Key gocui.Key Modifier gocui.Modifier Description string // DescriptionFunc is used instead of Description if non-nil, and is useful for dynamic diff --git a/pkg/integration/clients/tui.go b/pkg/integration/clients/tui.go index 2a00b3f9c..426f633de 100644 --- a/pkg/integration/clients/tui.go +++ b/pkg/integration/clients/tui.go @@ -43,7 +43,7 @@ func RunTUI(raceDetector bool) { g.SetManagerFunc(app.layout) - if err := g.SetKeybinding("list", gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowUp), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { if app.itemIdx > 0 { app.itemIdx-- } @@ -57,7 +57,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyArrowDown), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { if app.itemIdx < len(app.filteredTests)-1 { app.itemIdx++ } @@ -72,15 +72,15 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyCtrlC), gocui.ModNone, quit); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("list", 'q', gocui.ModNone, quit); err != nil { + if err := g.SetKeybinding("list", gocui.NewKeyRune('q'), gocui.ModNone, quit); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("list", 's', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('s'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -93,7 +93,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyName(gocui.KeyEnter), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -106,7 +106,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", 't', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('t'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -119,7 +119,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", 'd', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('d'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -132,7 +132,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", 'o', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('o'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -148,7 +148,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", 'O', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('O'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { currentTest := app.getCurrentTest() if currentTest == nil { return nil @@ -164,7 +164,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("list", '/', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("list", gocui.NewKeyRune('/'), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { app.filtering = true if _, err := g.SetCurrentView("editor"); err != nil { return err @@ -181,7 +181,7 @@ func RunTUI(raceDetector bool) { } // not using the editor yet, but will use it to help filter the list - if err := g.SetKeybinding("editor", gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEsc), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { app.filtering = false if _, err := g.SetCurrentView("list"); err != nil { return err @@ -198,7 +198,7 @@ func RunTUI(raceDetector bool) { log.Panicln(err) } - if err := g.SetKeybinding("editor", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if err := g.SetKeybinding("editor", gocui.NewKeyName(gocui.KeyEnter), gocui.ModNone, func(*gocui.Gui, *gocui.View) error { app.filtering = false if _, err := g.SetCurrentView("list"); err != nil { @@ -273,9 +273,9 @@ func (self *app) renderTests() { } } -func (self *app) wrapEditor(f func(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool) func(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - return func(v *gocui.View, key gocui.KeyName, ch rune, mod gocui.Modifier) bool { - matched := f(v, key, ch, mod) +func (self *app) wrapEditor(f func(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool) func(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + return func(v *gocui.View, key gocui.Key, mod gocui.Modifier) bool { + matched := f(v, key, mod) if matched { self.filterWithString(v.TextArea.GetContent()) }