assets/keymaps/vim.json 🔗
@@ -1097,4 +1097,10 @@
"ctrl-e": "markdown::ScrollDown",
},
},
+ {
+ "context": "pending",
+ "bindings": {
+ "backspace": "zed::PopKey"
+ }
+ },
]
Julia Ryan created
assets/keymaps/vim.json | 6 ++++++
crates/gpui/src/key_dispatch.rs | 15 ++++++++++++---
crates/gpui/src/window.rs | 21 +++++++++++++++++++++
crates/zed/src/zed.rs | 5 +++++
4 files changed, 44 insertions(+), 3 deletions(-)
@@ -1097,4 +1097,10 @@
"ctrl-e": "markdown::ScrollDown",
},
},
+ {
+ "context": "pending",
+ "bindings": {
+ "backspace": "zed::PopKey"
+ }
+ },
]
@@ -449,12 +449,19 @@ impl DispatchTree {
&self,
input: &[Keystroke],
dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
+ has_pending_keystrokes: bool,
) -> (SmallVec<[KeyBinding; 1]>, bool, Vec<KeyContext>) {
- let context_stack: Vec<KeyContext> = dispatch_path
+ let mut context_stack: Vec<KeyContext> = dispatch_path
.iter()
.filter_map(|node_id| self.node(*node_id).context.clone())
.collect();
+ if has_pending_keystrokes {
+ let mut pending_context = KeyContext::default();
+ pending_context.add("pending");
+ context_stack.push(pending_context);
+ }
+
let (bindings, partial) = self
.keymap
.borrow()
@@ -486,8 +493,10 @@ impl DispatchTree {
keystroke: Keystroke,
dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
) -> DispatchResult {
+ let has_pending_keystrokes = !input.is_empty();
input.push(keystroke.clone());
- let (bindings, pending, context_stack) = self.bindings_for_input(&input, dispatch_path);
+ let (bindings, pending, context_stack) =
+ self.bindings_for_input(&input, dispatch_path, has_pending_keystrokes);
if pending {
return DispatchResult {
@@ -542,7 +551,7 @@ impl DispatchTree {
) -> (SmallVec<[Keystroke; 1]>, SmallVec<[Replay; 1]>) {
let mut to_replay: SmallVec<[Replay; 1]> = Default::default();
for last in (0..input.len()).rev() {
- let (bindings, _, _) = self.bindings_for_input(&input[0..=last], dispatch_path);
+ let (bindings, _, _) = self.bindings_for_input(&input[0..=last], dispatch_path, false);
if !bindings.is_empty() {
to_replay.push(Replay {
keystroke: input.drain(0..=last).next_back().unwrap(),
@@ -4098,6 +4098,27 @@ impl Window {
self.pending_input.take();
}
+ /// Removes the last pending keystroke from the pending input sequence.
+ /// Returns true if a keystroke was removed, false if there were no pending keystrokes.
+ pub fn pop_pending_keystroke(&mut self, cx: &mut App) -> bool {
+ let Some(pending) = self.pending_input.as_mut() else {
+ return false;
+ };
+
+ if pending.keystrokes.is_empty() {
+ return false;
+ }
+
+ pending.keystrokes.pop();
+
+ if pending.keystrokes.is_empty() {
+ self.pending_input.take();
+ }
+
+ self.pending_input_changed(cx);
+ true
+ }
+
/// Returns the currently pending input keystrokes that might result in a multi-stroke key binding.
pub fn pending_input_keystrokes(&self) -> Option<&[Keystroke]> {
self.pending_input
@@ -130,6 +130,8 @@ actions!(
TestPanic,
/// Triggers a hard crash for debugging.
TestCrash,
+ /// Removes the latest pending keystroke.
+ PopKey,
]
);
@@ -816,6 +818,9 @@ fn register_actions(
.register_action(|_, _: &ToggleFullScreen, window, _| {
window.toggle_fullscreen();
})
+ .register_action(|_, _: &PopKey, window, cx| {
+ window.pop_pending_keystroke(cx);
+ })
.register_action(|_, action: &OpenZedUrl, _, cx| {
OpenListener::global(cx).open(RawOpenRequest {
urls: vec![action.url.clone()],