detached 39222bf

refactor(input): pass context as first parameter in keyboard and modal handlers

Remove stored ctx and logger fields from KeyboardHandler and ModalState.
Update all methods (EnterTabMode, EnterPaneMode, ExitMode, AttachTo) to
accept context as first parameter, following project convention of using
log := logging.FromContext(ctx) in each method. KeyboardHandler keeps ctx
field for GTK callback requirements.
commit
39222bfc29e7df6c74d98b9bc55c1a8d860faeff
author
brice · · 2025-12-17 06:44 UTC
parent
7a7144e
signature
unsigned
2 files · +27 -24
M internal/ui/input/keyboard.go +18 -15
@@ -30,9 +30,8 @@ type KeyboardHandler struct {
3030 // GTK controller (nil until attached)
3131 controller *gtk.EventControllerKey
3232
33- ctx context.Context
34- logger zerolog.Logger
35- mu sync.RWMutex
33+ ctx context.Context
34+ mu sync.RWMutex
3635 }
3736
3837 // NewKeyboardHandler creates a new keyboard handler.
@@ -42,12 +41,13 @@ func NewKeyboardHandler(
4241 ) *KeyboardHandler {
4342 log := logging.FromContext(ctx)
4443
44+ log.Debug().Msg("creating keyboard handler")
45+
4546 h := &KeyboardHandler{
4647 shortcuts: NewShortcutSet(ctx, cfg),
4748 modal: NewModalState(ctx),
4849 cfg: cfg,
4950 ctx: ctx,
50- logger: log.With().Str("component", "keyboard-handler").Logger(),
5151 }
5252
5353 return h
@@ -73,14 +73,16 @@ func (h *KeyboardHandler) Mode() Mode {
7373 // AttachTo attaches the keyboard handler to a GTK window.
7474 // The handler will intercept key events in the capture phase.
7575 func (h *KeyboardHandler) AttachTo(window *gtk.ApplicationWindow) {
76+ log := logging.FromContext(h.ctx)
77+
7678 if window == nil {
77- h.logger.Error().Msg("cannot attach keyboard handler to nil window")
79+ log.Error().Msg("cannot attach keyboard handler to nil window")
7880 return
7981 }
8082
8183 h.controller = gtk.NewEventControllerKey()
8284 if h.controller == nil {
83- h.logger.Error().Msg("failed to create event controller key")
85+ log.Error().Msg("failed to create event controller key")
8486 return
8587 }
8688
@@ -96,7 +98,7 @@ func (h *KeyboardHandler) AttachTo(window *gtk.ApplicationWindow) {
9698 // Add controller to window
9799 window.AddController(&h.controller.EventController)
98100
99- h.logger.Debug().Msg("keyboard handler attached to window")
101+ log.Debug().Msg("keyboard handler attached to window")
100102 }
101103
102104 // Detach removes the keyboard handler.
@@ -134,16 +136,16 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
134136 switch action {
135137 case ActionEnterTabMode:
136138 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond
137- h.modal.EnterTabMode(timeout)
139+ h.modal.EnterTabMode(h.ctx, timeout)
138140 return true
139141
140142 case ActionEnterPaneMode:
141143 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond
142- h.modal.EnterPaneMode(timeout)
144+ h.modal.EnterPaneMode(h.ctx, timeout)
143145 return true
144146
145147 case ActionExitMode:
146- h.modal.ExitMode()
148+ h.modal.ExitMode(h.ctx)
147149 return true
148150 }
149151
@@ -154,7 +156,8 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
154156
155157 if handler != nil {
156158 if err := handler(h.ctx, action); err != nil {
157- h.logger.Error().
159+ log := logging.FromContext(h.ctx)
160+ log.Error().
158161 Err(err).
159162 Str("action", string(action)).
160163 Msg("action handler error")
@@ -163,7 +166,7 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
163166
164167 // Auto-exit modal mode after certain actions
165168 if mode != ModeNormal && ShouldAutoExitMode(action) {
166- h.modal.ExitMode()
169+ h.modal.ExitMode(h.ctx)
167170 }
168171
169172 return true // Consumed the key
@@ -173,18 +176,18 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
173176 // Useful for testing or programmatic mode changes.
174177 func (h *KeyboardHandler) EnterTabMode() {
175178 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond
176- h.modal.EnterTabMode(timeout)
179+ h.modal.EnterTabMode(h.ctx, timeout)
177180 }
178181
179182 // EnterPaneMode programmatically enters pane mode.
180183 // Useful for testing or programmatic mode changes.
181184 func (h *KeyboardHandler) EnterPaneMode() {
182185 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond
183- h.modal.EnterPaneMode(timeout)
186+ h.modal.EnterPaneMode(h.ctx, timeout)
184187 }
185188
186189 // ExitMode programmatically exits modal mode.
187190 // Useful for testing or programmatic mode changes.
188191 func (h *KeyboardHandler) ExitMode() {
189- h.modal.ExitMode()
192+ h.modal.ExitMode(h.ctx)
190193 }
@@ -30,9 +30,8 @@ type KeyboardHandler struct {
30 // GTK controller (nil until attached)30 // GTK controller (nil until attached)
31 controller *gtk.EventControllerKey31 controller *gtk.EventControllerKey
32 32
33- ctx context.Context33+ ctx context.Context
34- logger zerolog.Logger34+ mu sync.RWMutex
35- mu sync.RWMutex
36 }35 }
37 36
38 // NewKeyboardHandler creates a new keyboard handler.37 // NewKeyboardHandler creates a new keyboard handler.
@@ -42,12 +41,13 @@ func NewKeyboardHandler(
42 ) *KeyboardHandler {41 ) *KeyboardHandler {
43 log := logging.FromContext(ctx)42 log := logging.FromContext(ctx)
44 43
44+ log.Debug().Msg("creating keyboard handler")
45+
45 h := &KeyboardHandler{46 h := &KeyboardHandler{
46 shortcuts: NewShortcutSet(ctx, cfg),47 shortcuts: NewShortcutSet(ctx, cfg),
47 modal: NewModalState(ctx),48 modal: NewModalState(ctx),
48 cfg: cfg,49 cfg: cfg,
49 ctx: ctx,50 ctx: ctx,
50- logger: log.With().Str("component", "keyboard-handler").Logger(),
51 }51 }
52 52
53 return h53 return h
@@ -73,14 +73,16 @@ func (h *KeyboardHandler) Mode() Mode {
73 // AttachTo attaches the keyboard handler to a GTK window.73 // AttachTo attaches the keyboard handler to a GTK window.
74 // The handler will intercept key events in the capture phase.74 // The handler will intercept key events in the capture phase.
75 func (h *KeyboardHandler) AttachTo(window *gtk.ApplicationWindow) {75 func (h *KeyboardHandler) AttachTo(window *gtk.ApplicationWindow) {
76+ log := logging.FromContext(h.ctx)
77+
76 if window == nil {78 if window == nil {
77- h.logger.Error().Msg("cannot attach keyboard handler to nil window")79+ log.Error().Msg("cannot attach keyboard handler to nil window")
78 return80 return
79 }81 }
80 82
81 h.controller = gtk.NewEventControllerKey()83 h.controller = gtk.NewEventControllerKey()
82 if h.controller == nil {84 if h.controller == nil {
83- h.logger.Error().Msg("failed to create event controller key")85+ log.Error().Msg("failed to create event controller key")
84 return86 return
85 }87 }
86 88
@@ -96,7 +98,7 @@ func (h *KeyboardHandler) AttachTo(window *gtk.ApplicationWindow) {
96 // Add controller to window98 // Add controller to window
97 window.AddController(&h.controller.EventController)99 window.AddController(&h.controller.EventController)
98 100
99- h.logger.Debug().Msg("keyboard handler attached to window")101+ log.Debug().Msg("keyboard handler attached to window")
100 }102 }
101 103
102 // Detach removes the keyboard handler.104 // Detach removes the keyboard handler.
@@ -134,16 +136,16 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
134 switch action {136 switch action {
135 case ActionEnterTabMode:137 case ActionEnterTabMode:
136 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond138 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond
137- h.modal.EnterTabMode(timeout)139+ h.modal.EnterTabMode(h.ctx, timeout)
138 return true140 return true
139 141
140 case ActionEnterPaneMode:142 case ActionEnterPaneMode:
141 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond143 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond
142- h.modal.EnterPaneMode(timeout)144+ h.modal.EnterPaneMode(h.ctx, timeout)
143 return true145 return true
144 146
145 case ActionExitMode:147 case ActionExitMode:
146- h.modal.ExitMode()148+ h.modal.ExitMode(h.ctx)
147 return true149 return true
148 }150 }
149 151
@@ -154,7 +156,8 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
154 156
155 if handler != nil {157 if handler != nil {
156 if err := handler(h.ctx, action); err != nil {158 if err := handler(h.ctx, action); err != nil {
157- h.logger.Error().159+ log := logging.FromContext(h.ctx)
160+ log.Error().
158 Err(err).161 Err(err).
159 Str("action", string(action)).162 Str("action", string(action)).
160 Msg("action handler error")163 Msg("action handler error")
@@ -163,7 +166,7 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
163 166
164 // Auto-exit modal mode after certain actions167 // Auto-exit modal mode after certain actions
165 if mode != ModeNormal && ShouldAutoExitMode(action) {168 if mode != ModeNormal && ShouldAutoExitMode(action) {
166- h.modal.ExitMode()169+ h.modal.ExitMode(h.ctx)
167 }170 }
168 171
169 return true // Consumed the key172 return true // Consumed the key
@@ -173,18 +176,18 @@ func (h *KeyboardHandler) handleKeyPress(keyval uint, keycode uint, state gdk.Mo
173 // Useful for testing or programmatic mode changes.176 // Useful for testing or programmatic mode changes.
174 func (h *KeyboardHandler) EnterTabMode() {177 func (h *KeyboardHandler) EnterTabMode() {
175 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond178 timeout := time.Duration(h.cfg.TabMode.TimeoutMilliseconds) * time.Millisecond
176- h.modal.EnterTabMode(timeout)179+ h.modal.EnterTabMode(h.ctx, timeout)
177 }180 }
178 181
179 // EnterPaneMode programmatically enters pane mode.182 // EnterPaneMode programmatically enters pane mode.
180 // Useful for testing or programmatic mode changes.183 // Useful for testing or programmatic mode changes.
181 func (h *KeyboardHandler) EnterPaneMode() {184 func (h *KeyboardHandler) EnterPaneMode() {
182 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond185 timeout := time.Duration(h.cfg.PaneMode.TimeoutMilliseconds) * time.Millisecond
183- h.modal.EnterPaneMode(timeout)186+ h.modal.EnterPaneMode(h.ctx, timeout)
184 }187 }
185 188
186 // ExitMode programmatically exits modal mode.189 // ExitMode programmatically exits modal mode.
187 // Useful for testing or programmatic mode changes.190 // Useful for testing or programmatic mode changes.
188 func (h *KeyboardHandler) ExitMode() {191 func (h *KeyboardHandler) ExitMode() {
189- h.modal.ExitMode()192+ h.modal.ExitMode(h.ctx)
190 }193 }
M internal/ui/input/modal.go +9 -9
@@ -43,15 +43,15 @@ type ModalState struct {
4343 // Callback for mode changes (called synchronously under lock).
4444 onModeChange func(from, to Mode)
4545
46- ctx context.Context
47- mu sync.RWMutex
46+ mu sync.RWMutex
4847 }
4948
5049 // NewModalState creates a new modal state manager.
5150 func NewModalState(ctx context.Context) *ModalState {
51+ log := logging.FromContext(ctx)
52+ log.Debug().Msg("creating modal state")
5253 return &ModalState{
5354 mode: ModeNormal,
54- ctx: ctx,
5555 }
5656 }
5757
@@ -64,8 +64,8 @@ func (m *ModalState) Mode() Mode {
6464
6565 // EnterTabMode switches to tab mode with an optional timeout.
6666 // If timeout is 0, the mode stays until explicitly exited.
67-func (m *ModalState) EnterTabMode(timeout time.Duration) {
68- log := logging.FromContext(m.ctx)
67+func (m *ModalState) EnterTabMode(ctx context.Context, timeout time.Duration) {
68+ log := logging.FromContext(ctx)
6969 m.mu.Lock()
7070 defer m.mu.Unlock()
7171
@@ -99,8 +99,8 @@ func (m *ModalState) EnterTabMode(timeout time.Duration) {
9999
100100 // EnterPaneMode switches to pane mode with an optional timeout.
101101 // If timeout is 0, the mode stays until explicitly exited.
102-func (m *ModalState) EnterPaneMode(timeout time.Duration) {
103- log := logging.FromContext(m.ctx)
102+func (m *ModalState) EnterPaneMode(ctx context.Context, timeout time.Duration) {
103+ log := logging.FromContext(ctx)
104104 m.mu.Lock()
105105 defer m.mu.Unlock()
106106
@@ -133,8 +133,8 @@ func (m *ModalState) EnterPaneMode(timeout time.Duration) {
133133 }
134134
135135 // ExitMode returns to normal mode.
136-func (m *ModalState) ExitMode() {
137- log := logging.FromContext(m.ctx)
136+func (m *ModalState) ExitMode(ctx context.Context) {
137+ log := logging.FromContext(ctx)
138138 m.mu.Lock()
139139 defer m.mu.Unlock()
140140
@@ -43,15 +43,15 @@ type ModalState struct {
43 // Callback for mode changes (called synchronously under lock).43 // Callback for mode changes (called synchronously under lock).
44 onModeChange func(from, to Mode)44 onModeChange func(from, to Mode)
45 45
46- ctx context.Context46+ mu sync.RWMutex
47- mu sync.RWMutex
48 }47 }
49 48
50 // NewModalState creates a new modal state manager.49 // NewModalState creates a new modal state manager.
51 func NewModalState(ctx context.Context) *ModalState {50 func NewModalState(ctx context.Context) *ModalState {
51+ log := logging.FromContext(ctx)
52+ log.Debug().Msg("creating modal state")
52 return &ModalState{53 return &ModalState{
53 mode: ModeNormal,54 mode: ModeNormal,
54- ctx: ctx,
55 }55 }
56 }56 }
57 57
@@ -64,8 +64,8 @@ func (m *ModalState) Mode() Mode {
64 64
65 // EnterTabMode switches to tab mode with an optional timeout.65 // EnterTabMode switches to tab mode with an optional timeout.
66 // If timeout is 0, the mode stays until explicitly exited.66 // If timeout is 0, the mode stays until explicitly exited.
67-func (m *ModalState) EnterTabMode(timeout time.Duration) {67+func (m *ModalState) EnterTabMode(ctx context.Context, timeout time.Duration) {
68- log := logging.FromContext(m.ctx)68+ log := logging.FromContext(ctx)
69 m.mu.Lock()69 m.mu.Lock()
70 defer m.mu.Unlock()70 defer m.mu.Unlock()
71 71
@@ -99,8 +99,8 @@ func (m *ModalState) EnterTabMode(timeout time.Duration) {
99 99
100 // EnterPaneMode switches to pane mode with an optional timeout.100 // EnterPaneMode switches to pane mode with an optional timeout.
101 // If timeout is 0, the mode stays until explicitly exited.101 // If timeout is 0, the mode stays until explicitly exited.
102-func (m *ModalState) EnterPaneMode(timeout time.Duration) {102+func (m *ModalState) EnterPaneMode(ctx context.Context, timeout time.Duration) {
103- log := logging.FromContext(m.ctx)103+ log := logging.FromContext(ctx)
104 m.mu.Lock()104 m.mu.Lock()
105 defer m.mu.Unlock()105 defer m.mu.Unlock()
106 106
@@ -133,8 +133,8 @@ func (m *ModalState) EnterPaneMode(timeout time.Duration) {
133 }133 }
134 134
135 // ExitMode returns to normal mode.135 // ExitMode returns to normal mode.
136-func (m *ModalState) ExitMode() {136+func (m *ModalState) ExitMode(ctx context.Context) {
137- log := logging.FromContext(m.ctx)137+ log := logging.FromContext(ctx)
138 m.mu.Lock()138 m.mu.Lock()
139 defer m.mu.Unlock()139 defer m.mu.Unlock()
140 140

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close