mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Support two-stroke TUI key chords (#36511)
## What changed - Accept two-stroke bindings such as `ctrl-x ctrl-s` in TUI keymap configuration while preserving arrays as alternative bindings. - Route chords through the active TUI context, show pending and configured chord hints, and cancel pending chords on `esc`, context changes, or a one-second timeout. - Extend `/keymap` to capture, add, replace, and display key chords. - Reject chords that conflict with active single-key bindings, overlapping-context chords, or reserved terminal shortcuts. ## Testing - Add coverage for chord parsing, validation, matching, timeout and cancellation behavior, context dispatch, key capture, and rendered shortcut hints. GitOrigin-RevId: dfdede03f518d4936ad8621ebf5537026f2b282e
This commit is contained in:
1 parent
a1dd74b535
commit
1e85ca099e
61 files changed
+3503
-660
No files matched your search
@@ -27,8 +27,9 @@ use crate::bottom_pane::unified_exec_footer::UnifiedExecFooter;
|
||||
use crate::key_hint;
|
||||
use crate::key_hint::KeyBinding;
|
||||
use crate::key_hint::KeyBindingListExt;
|
||||
use crate::keymap::KeymapContext;
|
||||
use crate::keymap::KeymapContextSet;
|
||||
use crate::keymap::RuntimeKeymap;
|
||||
use crate::keymap::primary_binding;
|
||||
use crate::render::renderable::FlexRenderable;
|
||||
use crate::render::renderable::Renderable;
|
||||
use crate::render::renderable::RenderableItem;
|
||||
@@ -398,7 +399,7 @@ impl BottomPane {
|
||||
pub fn set_keymap_bindings(&mut self, keymap: &RuntimeKeymap) {
|
||||
self.keymap = keymap.clone();
|
||||
self.composer.set_keymap_bindings(keymap);
|
||||
let interrupt_binding = primary_binding(&keymap.chat.interrupt_turn);
|
||||
let interrupt_binding = keymap.primary_hint(KeymapContext::Chat, "interrupt_turn");
|
||||
self.pending_input_preview
|
||||
.set_interrupt_binding(interrupt_binding);
|
||||
if let Some(status) = self.status.as_mut() {
|
||||
@@ -485,7 +486,10 @@ impl BottomPane {
|
||||
|
||||
/// Update the key hint shown next to queued messages so it matches the
|
||||
/// binding that `ChatWidget` actually listens for.
|
||||
pub(crate) fn set_queued_message_edit_binding(&mut self, binding: Option<KeyBinding>) {
|
||||
pub(crate) fn set_queued_message_edit_binding(
|
||||
&mut self,
|
||||
binding: Option<crate::key_hint::ShortcutHint>,
|
||||
) {
|
||||
self.pending_input_preview.set_edit_binding(binding);
|
||||
self.request_redraw();
|
||||
}
|
||||
@@ -693,6 +697,15 @@ impl BottomPane {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the contexts whose ordinary handlers can consume the next key.
|
||||
pub(crate) fn keymap_contexts(&self) -> KeymapContextSet {
|
||||
if let Some(view) = self.view_stack.last() {
|
||||
view.keymap_contexts()
|
||||
} else {
|
||||
self.composer.keymap_contexts()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a Ctrl+C press within the bottom pane.
|
||||
///
|
||||
/// An active modal view is given the first chance to consume the key (typically to dismiss
|
||||
@@ -1047,7 +1060,10 @@ impl BottomPane {
|
||||
}
|
||||
if let Some(status) = self.status.as_mut() {
|
||||
status.set_interrupt_hint_visible(/*visible*/ true);
|
||||
status.set_interrupt_binding(primary_binding(&self.keymap.chat.interrupt_turn));
|
||||
status.set_interrupt_binding(
|
||||
self.keymap
|
||||
.primary_hint(KeymapContext::Chat, "interrupt_turn"),
|
||||
);
|
||||
}
|
||||
self.sync_status_inline_message();
|
||||
self.request_redraw();
|
||||
@@ -1077,7 +1093,10 @@ impl BottomPane {
|
||||
self.animations_enabled,
|
||||
));
|
||||
if let Some(status) = self.status.as_mut() {
|
||||
status.set_interrupt_binding(primary_binding(&self.keymap.chat.interrupt_turn));
|
||||
status.set_interrupt_binding(
|
||||
self.keymap
|
||||
.primary_hint(KeymapContext::Chat, "interrupt_turn"),
|
||||
);
|
||||
}
|
||||
self.sync_status_inline_message();
|
||||
self.request_redraw();
|
||||
@@ -1553,7 +1572,7 @@ impl BottomPane {
|
||||
self.has_input_focus,
|
||||
self.enhanced_keys_supported,
|
||||
self.disable_paste_burst,
|
||||
self.keymap.list.clone(),
|
||||
self.keymap.clone(),
|
||||
);
|
||||
self.pause_status_timer_for_modal();
|
||||
self.set_composer_input_enabled(
|
||||
|
||||
Reference in new issue
Block a user