on master help sign in
Automatically track pseudo-pending text (#4077)

This change automatically tracks pending text for for commands which use on-next-key callbacks. For example, `t` will await the next key event and "t" will be shown in the bottom right-hand corner to show that we're in a pending state. Previously, the text for these on-next-key commands needed to be hard-coded into the command definition which had some drawbacks: * It was easy to forget to write and clear the pending text. * If a command was remapped in a custom config, the pending text would still show the old key. With this change, pending text is automatically tracked based on the key events that lead to the command being executed. This works even when the command is remapped in config and when the on-next-key callback is nested under some key sequence (for example `mi`).

commit6cca7375ec1965e61c791bbe738c617ea43c6dce authorMichael Davis <mcarsondavis@gmail.com> dateMon 3 Oct 2022, 10:14 (4 years ago) parent9124c23
3 files changed+43−49
helix-term/src/commands.rs+33−45 helix-term/src/ui/editor.rs+10−2 helix-view/src/editor.rs−2
helix-term/src/commands.rs+33−45
@@ -1091,27 +1091,18 @@ fn extend_next_long_word_end(cx: &mut Context) {
10911091 extend_word_impl(cx, movement::move_next_long_word_end)
10921092}
10931093
1094−fn will_find_char<F>(
1095− cx: &mut Context,
1096− search_fn: F,
1097− inclusive: bool,
1098− extend: bool,
1099− pseudo_pending: &str,
1100−) where
1094+fn will_find_char<F>(cx: &mut Context, search_fn: F, inclusive: bool, extend: bool)
1095+where
11011096 F: Fn(RopeSlice, char, usize, usize, bool) -> Option<usize> + 'static,
11021097{
11031098 // TODO: count is reset to 1 before next key so we move it into the closure here.
11041099 // Would be nice to carry over.
11051100 let count = cx.count();
11061101
1107− cx.editor.pseudo_pending = Some(pseudo_pending.to_string());
1108−
11091102 // need to wait for next key
11101103 // TODO: should this be done by grapheme rather than char? For example,
11111104 // we can't properly handle the line-ending CRLF case here in terms of char.
11121105 cx.on_next_key(move |cx, event| {
1113− cx.editor.pseudo_pending = None;
1114−
11151106 let ch = match event {
11161107 KeyEvent {
11171108 code: KeyCode::Enter,
@@ -1214,35 +1205,35 @@ fn find_prev_char_impl(
12141205}
12151206
12161207fn find_till_char(cx: &mut Context) {
1217− will_find_char(cx, find_next_char_impl, false, false, "t")
1208+ will_find_char(cx, find_next_char_impl, false, false)
12181209}
12191210
12201211fn find_next_char(cx: &mut Context) {
1221− will_find_char(cx, find_next_char_impl, true, false, "f")
1212+ will_find_char(cx, find_next_char_impl, true, false)
12221213}
12231214
12241215fn extend_till_char(cx: &mut Context) {
1225− will_find_char(cx, find_next_char_impl, false, true, "t")
1216+ will_find_char(cx, find_next_char_impl, false, true)
12261217}
12271218
12281219fn extend_next_char(cx: &mut Context) {
1229− will_find_char(cx, find_next_char_impl, true, true, "f")
1220+ will_find_char(cx, find_next_char_impl, true, true)
12301221}
12311222
12321223fn till_prev_char(cx: &mut Context) {
1233− will_find_char(cx, find_prev_char_impl, false, false, "T")
1224+ will_find_char(cx, find_prev_char_impl, false, false)
12341225}
12351226
12361227fn find_prev_char(cx: &mut Context) {
1237− will_find_char(cx, find_prev_char_impl, true, false, "F")
1228+ will_find_char(cx, find_prev_char_impl, true, false)
12381229}
12391230
12401231fn extend_till_prev_char(cx: &mut Context) {
1241− will_find_char(cx, find_prev_char_impl, false, true, "T")
1232+ will_find_char(cx, find_prev_char_impl, false, true)
12421233}
12431234
12441235fn extend_prev_char(cx: &mut Context) {
1245− will_find_char(cx, find_prev_char_impl, true, true, "F")
1236+ will_find_char(cx, find_prev_char_impl, true, true)
12461237}
12471238
12481239fn repeat_last_motion(cx: &mut Context) {
@@ -4392,7 +4383,6 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
43924383
43934384 cx.on_next_key(move |cx, event| {
43944385 cx.editor.autoinfo = None;
4395− cx.editor.pseudo_pending = None;
43964386 if let Some(ch) = event.char() {
43974387 let textobject = move |editor: &mut Editor| {
43984388 let (view, doc) = current!(editor);
@@ -4441,33 +4431,31 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
44414431 }
44424432 });
44434433
4444− if let Some((title, abbrev)) = match objtype {
4445− textobject::TextObject::Inside => Some(("Match inside", "mi")),
4446− textobject::TextObject::Around => Some(("Match around", "ma")),
4434+ let title = match objtype {
4435+ textobject::TextObject::Inside => "Match inside",
4436+ textobject::TextObject::Around => "Match around",
44474437 _ => return,
4448− } {
4449− let help_text = [
4450− ("w", "Word"),
4451− ("W", "WORD"),
4452− ("p", "Paragraph"),
4453− ("c", "Class (tree-sitter)"),
4454− ("f", "Function (tree-sitter)"),
4455− ("a", "Argument/parameter (tree-sitter)"),
4456− ("o", "Comment (tree-sitter)"),
4457− ("t", "Test (tree-sitter)"),
4458− ("m", "Closest surrounding pair to cursor"),
4459− (" ", "... or any character acting as a pair"),
4460− ];
4461−
4462− cx.editor.autoinfo = Some(Info::new(
4463− title,
4464− help_text
4465− .into_iter()
4466− .map(|(col1, col2)| (col1.to_string(), col2.to_string()))
4467− .collect(),
4468− ));
4469− cx.editor.pseudo_pending = Some(abbrev.to_string());
44704438 };
4439+ let help_text = [
4440+ ("w", "Word"),
4441+ ("W", "WORD"),
4442+ ("p", "Paragraph"),
4443+ ("c", "Class (tree-sitter)"),
4444+ ("f", "Function (tree-sitter)"),
4445+ ("a", "Argument/parameter (tree-sitter)"),
4446+ ("o", "Comment (tree-sitter)"),
4447+ ("t", "Test (tree-sitter)"),
4448+ ("m", "Closest surrounding pair to cursor"),
4449+ (" ", "... or any character acting as a pair"),
4450+ ];
4451+
4452+ cx.editor.autoinfo = Some(Info::new(
4453+ title,
4454+ help_text
4455+ .into_iter()
4456+ .map(|(col1, col2)| (col1.to_string(), col2.to_string()))
4457+ .collect(),
4458+ ));
44714459}
44724460
44734461fn surround_add(cx: &mut Context) {
helix-term/src/ui/editor.rs+10−2
@@ -33,6 +33,7 @@ use super::statusline;
3333pub struct EditorView {
3434 pub keymaps: Keymaps,
3535 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
36+ pseudo_pending: Vec<KeyEvent>,
3637 last_insert: (commands::MappableCommand, Vec<InsertEvent>),
3738 pub(crate) completion: Option<Completion>,
3839 spinners: ProgressSpinners,
@@ -56,6 +57,7 @@ impl EditorView {
5657 Self {
5758 keymaps,
5859 on_next_key: None,
60+ pseudo_pending: Vec::new(),
5961 last_insert: (commands::MappableCommand::normal_mode, Vec::new()),
6062 completion: None,
6163 spinners: ProgressSpinners::default(),
@@ -839,6 +841,7 @@ impl EditorView {
839841 event: KeyEvent,
840842 ) -> Option<KeymapResult> {
841843 let mut last_mode = mode;
844+ self.pseudo_pending.extend(self.keymaps.pending());
842845 let key_result = self.keymaps.get(mode, event);
843846 cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox());
844847
@@ -1316,6 +1319,11 @@ impl Component for EditorView {
13161319 }
13171320
13181321 self.on_next_key = cx.on_next_key_callback.take();
1322+ match self.on_next_key {
1323+ Some(_) => self.pseudo_pending.push(key),
1324+ None => self.pseudo_pending.clear(),
1325+ }
1326+
13191327 // appease borrowck
13201328 let callback = cx.callback.take();
13211329
@@ -1416,8 +1424,8 @@ impl Component for EditorView {
14161424 for key in self.keymaps.pending() {
14171425 disp.push_str(&key.key_sequence_format());
14181426 }
1419− if let Some(pseudo_pending) = &cx.editor.pseudo_pending {
1420− disp.push_str(pseudo_pending.as_str())
1427+ for key in &self.pseudo_pending {
1428+ disp.push_str(&key.key_sequence_format());
14211429 }
14221430 let style = cx.editor.theme.get("ui.text");
14231431 let macro_width = if cx.editor.macro_recording.is_some() {
helix-view/src/editor.rs−2
@@ -677,7 +677,6 @@ pub struct Editor {
677677
678678 pub idle_timer: Pin<Box<Sleep>>,
679679 pub last_motion: Option<Motion>,
680− pub pseudo_pending: Option<String>,
681680
682681 pub last_completion: Option<CompleteAction>,
683682
@@ -758,7 +757,6 @@ impl Editor {
758757 idle_timer: Box::pin(sleep(conf.idle_timeout)),
759758 last_motion: None,
760759 last_completion: None,
761− pseudo_pending: None,
762760 config,
763761 auto_pairs,
764762 exit_code: 0,