duat_core/mode/
remap.rs

1use std::{any::TypeId, sync::LazyLock};
2
3use crossterm::event::KeyEvent;
4use parking_lot::Mutex;
5
6pub use self::global::*;
7use super::Mode;
8use crate::{
9    context,
10    data::RwData,
11    mode,
12    text::{Key, Tag, text},
13    ui::Ui,
14    widgets::Widget,
15};
16
17mod global {
18    use std::str::Chars;
19
20    use crossterm::event::{KeyCode, KeyEvent, KeyModifiers as KeyMod};
21    use parking_lot::Mutex;
22
23    use super::{Gives, Remapper};
24    use crate::{
25        data::DataMap,
26        mode::Mode,
27        text::{Text, text},
28        ui::Ui,
29    };
30
31    static REMAPPER: Remapper = Remapper::new();
32    static SEND_KEY: Mutex<fn(KeyEvent)> = Mutex::new(empty);
33
34    /// Maps a sequence of keys to another
35    ///
36    /// The keys follow the same rules as Vim, so regular, standalone
37    /// characters are mapped verbatim, while "`<{mod}-{key}>`" and
38    /// "`<{special}>`" sequences are mapped like in Vim.
39    ///
40    /// Here are the available special keys:
41    ///
42    /// - `<Enter> => Enter`,
43    /// - `<Tab> => Tab`,
44    /// - `<Bspc> => Backspace`,
45    /// - `<Del> => Delete`,
46    /// - `<Esc> => Esc`,
47    /// - `<Up> => Up`,
48    /// - `<Down> => Down`,
49    /// - `<Left> => Left`,
50    /// - `<Right> => Right`,
51    /// - `<PageU> => PageUp`,
52    /// - `<PageD> => PageDown`,
53    /// - `<Home> => Home`,
54    /// - `<End> => End`,
55    /// - `<Ins> => Insert`,
56    /// - `<F{1-12}> => F({1-12})`,
57    ///
58    /// And the following modifiers are available:
59    ///
60    /// - `C => Control`,
61    /// - `A => Alt`,
62    /// - `S => Shift`,
63    /// - `M => Meta`,
64    /// - `super => Super`,
65    /// - `hyper => Hyper`,
66    ///
67    /// If another sequence already exists on the same mode, which
68    /// would intersect with this one, the new sequence will not be
69    /// added.
70    pub fn map<M: Mode<U>, U: Ui>(take: &str, give: impl AsGives<U>) {
71        REMAPPER.remap::<M, U>(str_to_keys(take), give.into_gives(), false);
72    }
73
74    /// Aliases a sequence of keys to another
75    ///
76    /// The difference between aliasing and mapping is that an alias
77    /// will be displayed on the text as a [ghost text], making it
78    /// seem like you are typing normally. This text will be printed
79    /// with the `Alias` [form].
80    ///
81    /// If another sequence already exists on the same mode, which
82    /// would intersect with this one, the new sequence will not be
83    /// added.
84    ///
85    /// # Note
86    ///
87    /// This sequence is not like Vim's `alias`, in that if you make a
88    /// mistake while typing the sequence, the alias is undone, and
89    /// you will be just typing normally.
90    ///
91    /// The alias command also works on any [`Mode`], not just
92    /// "insert like" modes. You can also use any key in the input or
93    /// output of this `alias`
94    ///
95    /// [ghost text]: crate::text::Tag::GhostText
96    /// [form]: crate::form::Form
97    pub fn alias<M: Mode<U>, U: Ui>(take: &str, give: impl AsGives<U>) {
98        REMAPPER.remap::<M, U>(str_to_keys(take), give.into_gives(), true);
99    }
100
101    pub fn cur_sequence() -> DataMap<(Vec<KeyEvent>, bool), (Vec<KeyEvent>, bool)> {
102        REMAPPER.cur_seq.map(|seq| seq.clone())
103    }
104
105    /// Turns a sequence of [`KeyEvent`]s into a [`Text`]
106    pub fn keys_to_text(keys: &[KeyEvent]) -> Text {
107        use crossterm::event::KeyCode::*;
108        let mut seq = Text::builder();
109
110        for key in keys {
111            match key.code {
112                Backspace => text!(seq, [SeqSpecialKey] "BS"),
113                Enter => text!(seq, [SeqSpecialKey] "Enter"),
114                Left => text!(seq, [SeqSpecialKey] "Left"),
115                Right => text!(seq, [SeqSpecialKey] "Right"),
116                Up => text!(seq, [SeqSpecialKey] "Up"),
117                Down => text!(seq, [SeqSpecialKey] "Down"),
118                Home => text!(seq, [SeqSpecialKey] "Home"),
119                End => text!(seq, [SeqSpecialKey] "End"),
120                PageUp => text!(seq, [SeqSpecialKey] "PageU"),
121                PageDown => text!(seq, [SeqSpecialKey] "PageD"),
122                Tab => text!(seq, [SeqSpecialKey] "Tab"),
123                BackTab => text!(seq, [SeqSpecialKey] "BTab"),
124                Delete => text!(seq, [SeqSpecialKey] "Del"),
125                Insert => text!(seq, [SeqSpecialKey] "Ins"),
126                F(num) => text!(seq, [SeqSpecialKey] "F" num),
127                Char(char) => text!(seq, [SeqCharKey] char),
128                Null => text!(seq, [SeqSpecialKey] "Null"),
129                Esc => text!(seq, [SeqSpecialKey] "Esc"),
130                CapsLock => text!(seq, [SeqSpecialKey] "CapsL"),
131                ScrollLock => text!(seq, [SeqSpecialKey] "ScrollL"),
132                NumLock => text!(seq, [SeqSpecialKey] "NumL"),
133                PrintScreen => text!(seq, [SeqSpecialKey] "PrSc"),
134                Pause => text!(seq, [SeqSpecialKey] "Pause"),
135                Menu => text!(seq, [SeqSpecialKey] "Menu"),
136                KeypadBegin => text!(seq, [SeqSpecialKey] "KeypadBeg"),
137                Media(m_code) => text!(seq, [SeqSpecialKey] "Media" m_code),
138                Modifier(m_code) => text!(seq, [SeqSpecialKey] "Mod" m_code),
139            }
140        }
141
142        seq.finish()
143    }
144
145    /// Turns a string of [`KeyEvent`]s into a [`String`]
146    pub fn keys_to_string(keys: &[KeyEvent]) -> String {
147        use std::fmt::Write;
148
149        use crossterm::event::KeyCode::*;
150        let mut seq = String::new();
151
152        for key in keys {
153            match key.code {
154                Backspace => seq.push_str("<BS>"),
155                Enter => seq.push_str("<Enter>"),
156                Left => seq.push_str("<Left>"),
157                Right => seq.push_str("<Right>"),
158                Up => seq.push_str("<Up>"),
159                Down => seq.push_str("<Down>"),
160                Home => seq.push_str("<Home>"),
161                End => seq.push_str("<End>"),
162                PageUp => seq.push_str("<PageU>"),
163                PageDown => seq.push_str("<PageD>"),
164                Tab => seq.push_str("<Tab>"),
165                BackTab => seq.push_str("<BTab>"),
166                Delete => seq.push_str("<Del>"),
167                Insert => seq.push_str("<Ins>"),
168                F(num) => write!(seq, "<F{num}>").unwrap(),
169                Char(char) => write!(seq, "{char}").unwrap(),
170                Null => seq.push_str("<Null>"),
171                Esc => seq.push_str("<Esc>"),
172                CapsLock => seq.push_str("<CapsL>"),
173                ScrollLock => seq.push_str("<ScrollL>"),
174                NumLock => seq.push_str("<NumL>"),
175                PrintScreen => seq.push_str("<PrSc>"),
176                Pause => seq.push_str("<Pause>"),
177                Menu => seq.push_str("<Menu>"),
178                KeypadBegin => seq.push_str("<KeypadBeg>"),
179                Media(m_code) => write!(seq, "<Media{m_code}>").unwrap(),
180                Modifier(m_code) => write!(seq, "<Mod{m_code}>").unwrap(),
181            }
182        }
183
184        seq
185    }
186
187    /// Converts an `&str` to a sequence of [`KeyEvent`]s
188    ///
189    /// The conversion follows the same rules as remaps in Vim, that
190    /// is:
191    pub fn str_to_keys(str: &str) -> Vec<KeyEvent> {
192        const SPECIAL: &[(&str, KeyCode)] = &[
193            ("Enter", KeyCode::Enter),
194            ("Tab", KeyCode::Tab),
195            ("Bspc", KeyCode::Backspace),
196            ("Del", KeyCode::Delete),
197            ("Esc", KeyCode::Esc),
198            ("Up", KeyCode::Up),
199            ("Down", KeyCode::Down),
200            ("Left", KeyCode::Left),
201            ("Right", KeyCode::Right),
202            ("PageU", KeyCode::PageUp),
203            ("PageD", KeyCode::PageDown),
204            ("Home", KeyCode::Home),
205            ("End", KeyCode::End),
206            ("Ins", KeyCode::Insert),
207            ("F1", KeyCode::F(1)),
208            ("F2", KeyCode::F(2)),
209            ("F3", KeyCode::F(3)),
210            ("F4", KeyCode::F(4)),
211            ("F5", KeyCode::F(5)),
212            ("F6", KeyCode::F(6)),
213            ("F7", KeyCode::F(7)),
214            ("F8", KeyCode::F(8)),
215            ("F9", KeyCode::F(9)),
216            ("F10", KeyCode::F(10)),
217            ("F11", KeyCode::F(11)),
218            ("F12", KeyCode::F(12)),
219        ];
220        const MODS: &[(&str, KeyMod)] = &[
221            ("C", KeyMod::CONTROL),
222            ("A", KeyMod::ALT),
223            ("S", KeyMod::SHIFT),
224            ("M", KeyMod::META),
225            ("super", KeyMod::SUPER),
226            ("hyper", KeyMod::HYPER),
227        ];
228        fn match_key(chars: Chars) -> Option<(KeyEvent, Chars)> {
229            let matched_mods = {
230                let mut chars = chars.clone();
231                let mut mods = KeyMod::empty();
232                let mut seq = String::new();
233
234                loop {
235                    let char = chars.next()?;
236                    if char == '-' {
237                        if mods.is_empty() {
238                            break None;
239                        } else {
240                            break Some((mods, chars));
241                        }
242                    }
243
244                    seq.push(char);
245
246                    if let Some((_, m)) = MODS.iter().find(|(str, _)| str == &seq)
247                        && !mods.contains(*m)
248                    {
249                        mods = mods.union(*m);
250                        seq.clear();
251                    } else if !MODS[4..6].iter().any(|(str, _)| str.starts_with(&seq)) {
252                        break None;
253                    }
254                }
255            };
256
257            let (mut mods, mut chars) = match matched_mods {
258                Some((mods, chars)) => (mods, chars),
259                None => (KeyMod::empty(), chars),
260            };
261
262            let mut code = Some(chars.next().map(KeyCode::Char)?);
263            let mut seq = code.unwrap().to_string();
264
265            loop {
266                if let Some(c) = code.take() {
267                    match chars.next()? {
268                        '>' if seq.len() > 1 || !mods.is_empty() => {
269                            // Characters are sent as-is, no shifting required.
270                            if let KeyCode::Char(_) = c {
271                                mods.remove(KeyMod::SHIFT);
272                            }
273                            break Some((KeyEvent::new(c, mods), chars));
274                        }
275                        _ if seq.len() > 1 => break None,
276                        char => seq.push(char),
277                    }
278                }
279
280                if let Some((str, c)) = SPECIAL.iter().find(|(str, _)| str.starts_with(&seq)) {
281                    if str == &seq {
282                        code = Some(*c);
283                    } else {
284                        seq.push(chars.next()?);
285                    }
286                } else {
287                    break None;
288                }
289            }
290        }
291
292        let mut keys = Vec::new();
293        let mut chars = str.chars();
294        let mut next = chars.next();
295
296        while let Some(char) = next {
297            if char == '<'
298                && let Some((key, ahead)) = match_key(chars.clone())
299            {
300                keys.push(key);
301                chars = ahead;
302            } else {
303                keys.push(KeyEvent::from(KeyCode::Char(char)));
304            }
305
306            next = chars.next();
307        }
308
309        keys
310    }
311
312    pub trait AsGives<U> {
313        fn into_gives(self) -> Gives;
314    }
315
316    impl<M: Mode<U>, U: Ui> AsGives<U> for M {
317        fn into_gives(self) -> Gives {
318            if let Some(keys) = self.just_keys() {
319                Gives::Keys(str_to_keys(keys))
320            } else {
321                Gives::Mode(Box::new(move || crate::mode::set(self.clone())))
322            }
323        }
324    }
325
326    /// Sends a key to be remapped
327    pub(crate) fn send_key(mut key: KeyEvent) {
328        // No need to send shift to, for example, Char('L').
329        if let KeyCode::Char(_) = key.code {
330            key.modifiers.remove(KeyMod::SHIFT);
331        }
332        let f = { *SEND_KEY.lock() };
333        f(key)
334    }
335
336    /// Sets the key sending function
337    pub(in crate::mode) fn set_send_key<M: Mode<U>, U: Ui>() {
338        *SEND_KEY.lock() = send_key_fn::<M, U>;
339    }
340
341    /// The key sending function, to be used as a pointer
342    fn send_key_fn<M: Mode<U>, U: Ui>(key: KeyEvent) {
343        REMAPPER.send_key::<M, U>(key);
344    }
345
346    /// Null key sending function
347    fn empty(_: KeyEvent) {}
348}
349
350/// The structure responsible for remapping sequences of characters
351struct Remapper {
352    remaps: Mutex<Vec<(TypeId, Vec<Remap>)>>,
353    cur_seq: LazyLock<RwData<(Vec<KeyEvent>, bool)>>,
354}
355
356impl Remapper {
357    /// Returns a new instance of [`Remapper`]
358    const fn new() -> Self {
359        Remapper {
360            remaps: Mutex::new(Vec::new()),
361            cur_seq: LazyLock::new(RwData::default),
362        }
363    }
364
365    /// Maps a sequence of characters to another
366    fn remap<M: Mode<U>, U: Ui>(&self, take: Vec<KeyEvent>, give: Gives, is_alias: bool) {
367        fn remap_inner(
368            remapper: &Remapper,
369            type_id: TypeId,
370            take: Vec<KeyEvent>,
371            give: Gives,
372            is_alias: bool,
373        ) {
374            let remap = Remap::new(take, give, is_alias);
375
376            let mut remaps = remapper.remaps.lock();
377
378            if let Some((_, remaps)) = remaps.iter_mut().find(|(m, _)| type_id == *m) {
379                if remaps.iter().all(|r| {
380                    !(r.takes.starts_with(&remap.takes) || remap.takes.starts_with(&r.takes))
381                }) {
382                    remaps.push(remap);
383                }
384            } else {
385                remaps.push((type_id, vec![remap]));
386            }
387        }
388
389        remap_inner(self, TypeId::of::<M>(), take, give, is_alias);
390    }
391
392    /// Sends a key to be remapped or not
393    fn send_key<M: Mode<U>, U: Ui>(&self, key: KeyEvent) {
394        fn send_key_inner<U: Ui>(remapper: &Remapper, type_id: TypeId, key: KeyEvent) {
395            let remaps = remapper.remaps.lock();
396            let Some((_, remaps)) = remaps.iter().find(|(m, _)| type_id == *m) else {
397                mode::send_keys_to(vec![key]);
398                return;
399            };
400
401            let mut cur_seq = remapper.cur_seq.write();
402            let (cur_seq, is_alias) = &mut *cur_seq;
403            cur_seq.push(key);
404
405            if let Some(remap) = remaps.iter().find(|r| r.takes.starts_with(cur_seq)) {
406                *is_alias = remap.is_alias;
407                if remap.takes.len() == cur_seq.len() {
408                    if remap.is_alias {
409                        remove_alias_and::<U>(|_, _, _| {});
410                    }
411
412                    *is_alias = false;
413
414                    cur_seq.clear();
415                    match &remap.gives {
416                        Gives::Keys(keys) => mode::send_keys_to(keys.clone()),
417                        Gives::Mode(f) => f(),
418                    }
419                } else if *is_alias {
420                    remove_alias_and::<U>(|widget, area, main| {
421                        widget.text_mut().insert_tag(
422                            Key::for_alias(),
423                            Tag::Ghost(main, text!([Alias] { keys_to_string(cur_seq) })),
424                        );
425
426                        let cfg = widget.print_cfg();
427                        widget.text_mut().add_cursors(area, cfg);
428                        widget.update(area);
429                        widget.print(area);
430                    })
431                }
432            } else if *is_alias {
433                remove_alias_and::<U>(|_, _, _| {});
434                *is_alias = false;
435                mode::send_keys_to(std::mem::take(cur_seq));
436            } else {
437                mode::send_keys_to(std::mem::take(cur_seq));
438            }
439        }
440
441        send_key_inner::<U>(self, TypeId::of::<M>(), key);
442    }
443}
444
445/// A sequence of characters that should be turned into another
446/// sequence of characters
447struct Remap {
448    takes: Vec<KeyEvent>,
449    gives: Gives,
450    is_alias: bool,
451}
452
453impl Remap {
454    pub fn new(takes: Vec<KeyEvent>, gives: Gives, is_alias: bool) -> Self {
455        Self { takes, gives, is_alias }
456    }
457}
458
459pub enum Gives {
460    Keys(Vec<KeyEvent>),
461    Mode(Box<dyn Fn() + Send>),
462}
463
464fn remove_alias_and<U: Ui>(f: impl FnOnce(&mut dyn Widget<U>, &U::Area, usize)) {
465    let widget = context::cur_widget::<U>().unwrap();
466    widget.mutate_data(|widget, area, _| {
467        let mut widget = widget.write();
468        let cfg = widget.print_cfg();
469        widget.text_mut().remove_cursors(area, cfg);
470
471        if let Some(main) = widget.cursors().unwrap().get_main() {
472            let main = main.byte();
473            widget.text_mut().remove_tags(main, Key::for_alias());
474            f(&mut *widget, area, main)
475        }
476    })
477}