Share editor keymaps across TUI composer components (#38837)

## What changed

- Store the editor portion of `RuntimeKeymap` in an `Arc` so the chat composer
  and its embedded textarea use the same keymap snapshot.
- Initialize the textarea from the composer's default runtime keymap and keep
  custom binding updates shared between both components.
- Use copy-on-write when tests and binding resolution need to mutate editor
  bindings.

## Testing

- Verify that applying custom editor bindings shares the same keymap allocation
  with the composer and textarea.

GitOrigin-RevId: 7f3b0afd6cc784e23deee8819e5c2c37a3709c68
This commit is contained in:
Benjamin Carlsson authored and copyberry committed 2026-08-16 05:59:34 +00:00
1 parent 73abda8bfe
commit 9bfaf7a076
5 files changed
+36 -19

No files matched your search

@@ -322,6 +322,7 @@ use std::collections::HashSet;
use std::collections::VecDeque;
use std::ops::Range;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
@@ -515,7 +516,7 @@ pub(crate) struct ChatComposer {
toggle_shortcuts_keys: Vec<KeyBinding>,
history_search_previous_keys: Vec<KeyBinding>,
history_search_next_keys: Vec<KeyBinding>,
editor_keymap: EditorKeymap,
editor_keymap: Arc<EditorKeymap>,
vim_normal_keymap: VimNormalKeymap,
}
@@ -707,6 +708,7 @@ impl ChatComposer {
editor_keymap: default_editor_keymap,
vim_normal_keymap: default_vim_normal_keymap,
};
this.draft.textarea.set_keymap_bindings(&default_keymap);
// Apply configuration via the setter to keep side-effects centralized.
this.set_disable_paste_burst(disable_paste_burst);
this
@@ -11222,8 +11224,10 @@ mod tests {
assert!(matches!(result, InputResult::Submitted { .. }));
let mut keymap = RuntimeKeymap::defaults();
keymap.editor.move_up = vec![key_hint::plain(KeyCode::F(2))];
Arc::make_mut(&mut keymap.editor).move_up = vec![key_hint::plain(KeyCode::F(2))];
composer.set_keymap_bindings(&keymap);
assert!(Arc::ptr_eq(&composer.editor_keymap, &keymap.editor));
assert_eq!(Arc::strong_count(&keymap.editor), 3);
let _ = composer.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
assert!(composer.draft.textarea.is_empty());
+8 -7
View File
@@ -39,6 +39,7 @@ use std::borrow::Cow;
use std::cell::Ref;
use std::cell::RefCell;
use std::ops::Range;
use std::sync::Arc;
use unicode_segmentation::UnicodeSegmentation;
mod vim;
@@ -127,7 +128,7 @@ pub(crate) struct TextArea {
vim_enabled: bool,
vim_mode: VimMode,
vim_pending: VimPending,
editor_keymap: EditorKeymap,
editor_keymap: Arc<EditorKeymap>,
vim_normal_keymap: VimNormalKeymap,
vim_operator_keymap: VimOperatorKeymap,
vim_text_object_keymap: VimTextObjectKeymap,
@@ -182,7 +183,7 @@ impl TextArea {
/// the kill buffer, so callers can safely apply a live config update while
/// preserving the current draft exactly as typed.
pub fn set_keymap_bindings(&mut self, keymap: &RuntimeKeymap) {
self.editor_keymap = keymap.editor.clone();
self.editor_keymap = Arc::clone(&keymap.editor);
self.vim_normal_keymap = keymap.vim_normal.clone();
self.vim_operator_keymap = keymap.vim_operator.clone();
self.vim_text_object_keymap = keymap.vim_text_object.clone();
@@ -2971,8 +2972,8 @@ mod tests {
let mut t = ta_with("abc\ndef\nghi");
t.set_cursor(/*pos*/ 5);
let mut keymap = RuntimeKeymap::defaults().editor;
keymap.kill_line_start.clear();
keymap.kill_whole_line = vec![key_hint::ctrl(KeyCode::Char('u'))];
Arc::make_mut(&mut keymap).kill_line_start.clear();
Arc::make_mut(&mut keymap).kill_whole_line = vec![key_hint::ctrl(KeyCode::Char('u'))];
t.input_with_keymap(
KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL),
@@ -3222,7 +3223,7 @@ mod tests {
let mut t = ta_with("a\nb");
t.set_cursor(/*pos*/ 2);
let mut keymap = RuntimeKeymap::defaults().editor;
keymap.move_up.clear();
Arc::make_mut(&mut keymap).move_up.clear();
t.input_with_keymap(
KeyEvent::new(KeyCode::Char('\u{0010}'), KeyModifiers::NONE),
@@ -3237,8 +3238,8 @@ mod tests {
let mut t = ta_with("a\nb");
t.set_cursor(/*pos*/ 0);
let mut keymap = RuntimeKeymap::defaults().editor;
keymap.move_up.clear();
keymap.move_down = vec![crate::key_hint::ctrl(KeyCode::Char('p'))];
Arc::make_mut(&mut keymap).move_up.clear();
Arc::make_mut(&mut keymap).move_down = vec![crate::key_hint::ctrl(KeyCode::Char('p'))];
t.input_with_keymap(
KeyEvent::new(KeyCode::Char('\u{0010}'), KeyModifiers::NONE),