diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 79b62e23..2563880b 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1091,27 +1091,18 @@ fn extend_next_long_word_end(cx: &mut Context) {
     extend_word_impl(cx, movement::move_next_long_word_end)
 }
 
-fn will_find_char<F>(
-    cx: &mut Context,
-    search_fn: F,
-    inclusive: bool,
-    extend: bool,
-    pseudo_pending: &str,
-) where
+fn will_find_char<F>(cx: &mut Context, search_fn: F, inclusive: bool, extend: bool)
+where
     F: Fn(RopeSlice, char, usize, usize, bool) -> Option<usize> + 'static,
 {
     // TODO: count is reset to 1 before next key so we move it into the closure here.
     // Would be nice to carry over.
     let count = cx.count();
 
-    cx.editor.pseudo_pending = Some(pseudo_pending.to_string());
-
     // need to wait for next key
     // TODO: should this be done by grapheme rather than char?  For example,
     // we can't properly handle the line-ending CRLF case here in terms of char.
     cx.on_next_key(move |cx, event| {
-        cx.editor.pseudo_pending = None;
-
         let ch = match event {
             KeyEvent {
                 code: KeyCode::Enter,
@@ -1214,35 +1205,35 @@ fn find_prev_char_impl(
 }
 
 fn find_till_char(cx: &mut Context) {
-    will_find_char(cx, find_next_char_impl, false, false, "t")
+    will_find_char(cx, find_next_char_impl, false, false)
 }
 
 fn find_next_char(cx: &mut Context) {
-    will_find_char(cx, find_next_char_impl, true, false, "f")
+    will_find_char(cx, find_next_char_impl, true, false)
 }
 
 fn extend_till_char(cx: &mut Context) {
-    will_find_char(cx, find_next_char_impl, false, true, "t")
+    will_find_char(cx, find_next_char_impl, false, true)
 }
 
 fn extend_next_char(cx: &mut Context) {
-    will_find_char(cx, find_next_char_impl, true, true, "f")
+    will_find_char(cx, find_next_char_impl, true, true)
 }
 
 fn till_prev_char(cx: &mut Context) {
-    will_find_char(cx, find_prev_char_impl, false, false, "T")
+    will_find_char(cx, find_prev_char_impl, false, false)
 }
 
 fn find_prev_char(cx: &mut Context) {
-    will_find_char(cx, find_prev_char_impl, true, false, "F")
+    will_find_char(cx, find_prev_char_impl, true, false)
 }
 
 fn extend_till_prev_char(cx: &mut Context) {
-    will_find_char(cx, find_prev_char_impl, false, true, "T")
+    will_find_char(cx, find_prev_char_impl, false, true)
 }
 
 fn extend_prev_char(cx: &mut Context) {
-    will_find_char(cx, find_prev_char_impl, true, true, "F")
+    will_find_char(cx, find_prev_char_impl, true, true)
 }
 
 fn repeat_last_motion(cx: &mut Context) {
@@ -4392,7 +4383,6 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
 
     cx.on_next_key(move |cx, event| {
         cx.editor.autoinfo = None;
-        cx.editor.pseudo_pending = None;
         if let Some(ch) = event.char() {
             let textobject = move |editor: &mut Editor| {
                 let (view, doc) = current!(editor);
@@ -4441,33 +4431,31 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
         }
     });
 
-    if let Some((title, abbrev)) = match objtype {
-        textobject::TextObject::Inside => Some(("Match inside", "mi")),
-        textobject::TextObject::Around => Some(("Match around", "ma")),
+    let title = match objtype {
+        textobject::TextObject::Inside => "Match inside",
+        textobject::TextObject::Around => "Match around",
         _ => return,
-    } {
-        let help_text = [
-            ("w", "Word"),
-            ("W", "WORD"),
-            ("p", "Paragraph"),
-            ("c", "Class (tree-sitter)"),
-            ("f", "Function (tree-sitter)"),
-            ("a", "Argument/parameter (tree-sitter)"),
-            ("o", "Comment (tree-sitter)"),
-            ("t", "Test (tree-sitter)"),
-            ("m", "Closest surrounding pair to cursor"),
-            (" ", "... or any character acting as a pair"),
-        ];
-
-        cx.editor.autoinfo = Some(Info::new(
-            title,
-            help_text
-                .into_iter()
-                .map(|(col1, col2)| (col1.to_string(), col2.to_string()))
-                .collect(),
-        ));
-        cx.editor.pseudo_pending = Some(abbrev.to_string());
     };
+    let help_text = [
+        ("w", "Word"),
+        ("W", "WORD"),
+        ("p", "Paragraph"),
+        ("c", "Class (tree-sitter)"),
+        ("f", "Function (tree-sitter)"),
+        ("a", "Argument/parameter (tree-sitter)"),
+        ("o", "Comment (tree-sitter)"),
+        ("t", "Test (tree-sitter)"),
+        ("m", "Closest surrounding pair to cursor"),
+        (" ", "... or any character acting as a pair"),
+    ];
+
+    cx.editor.autoinfo = Some(Info::new(
+        title,
+        help_text
+            .into_iter()
+            .map(|(col1, col2)| (col1.to_string(), col2.to_string()))
+            .collect(),
+    ));
 }
 
 fn surround_add(cx: &mut Context) {
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index e5dc3c95..97639ff2 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -33,6 +33,7 @@ use super::statusline;
 pub struct EditorView {
     pub keymaps: Keymaps,
     on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
+    pseudo_pending: Vec<KeyEvent>,
     last_insert: (commands::MappableCommand, Vec<InsertEvent>),
     pub(crate) completion: Option<Completion>,
     spinners: ProgressSpinners,
@@ -56,6 +57,7 @@ impl EditorView {
         Self {
             keymaps,
             on_next_key: None,
+            pseudo_pending: Vec::new(),
             last_insert: (commands::MappableCommand::normal_mode, Vec::new()),
             completion: None,
             spinners: ProgressSpinners::default(),
@@ -839,6 +841,7 @@ impl EditorView {
         event: KeyEvent,
     ) -> Option<KeymapResult> {
         let mut last_mode = mode;
+        self.pseudo_pending.extend(self.keymaps.pending());
         let key_result = self.keymaps.get(mode, event);
         cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox());
 
@@ -1316,6 +1319,11 @@ impl Component for EditorView {
                 }
 
                 self.on_next_key = cx.on_next_key_callback.take();
+                match self.on_next_key {
+                    Some(_) => self.pseudo_pending.push(key),
+                    None => self.pseudo_pending.clear(),
+                }
+
                 // appease borrowck
                 let callback = cx.callback.take();
 
@@ -1416,8 +1424,8 @@ impl Component for EditorView {
             for key in self.keymaps.pending() {
                 disp.push_str(&key.key_sequence_format());
             }
-            if let Some(pseudo_pending) = &cx.editor.pseudo_pending {
-                disp.push_str(pseudo_pending.as_str())
+            for key in &self.pseudo_pending {
+                disp.push_str(&key.key_sequence_format());
             }
             let style = cx.editor.theme.get("ui.text");
             let macro_width = if cx.editor.macro_recording.is_some() {
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 89759439..d09d0ac3 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -677,7 +677,6 @@ pub struct Editor {
 
     pub idle_timer: Pin<Box<Sleep>>,
     pub last_motion: Option<Motion>,
-    pub pseudo_pending: Option<String>,
 
     pub last_completion: Option<CompleteAction>,
 
@@ -758,7 +757,6 @@ impl Editor {
             idle_timer: Box::pin(sleep(conf.idle_timeout)),
             last_motion: None,
             last_completion: None,
-            pseudo_pending: None,
             config,
             auto_pairs,
             exit_code: 0,