ad_editor/mode/
mod.rs

1//! Modal editing support.
2use crate::{
3    config::KeyBindings,
4    editor::Actions,
5    key::Input,
6    term::CurShape,
7    trie::{QueryResult, Trie},
8};
9use std::fmt;
10use tracing::error;
11
12mod insert;
13mod normal;
14
15/// The modes available for ad
16pub(crate) fn modes(custom_bindings: &KeyBindings) -> Vec<Mode> {
17    let mut normal = normal::normal_mode().0;
18    if let Err(e) = normal.with_overrides(custom_bindings.normal.clone()) {
19        error!("unable to apply keymap overrides for NORMAL mode: {e}");
20    }
21
22    let mut insert = insert::insert_mode().0;
23    if let Err(e) = insert.with_overrides(custom_bindings.insert.clone()) {
24        error!("unable to apply keymap overrides for INSERT mode: {e}");
25    }
26
27    vec![normal, insert]
28}
29
30/// Docs for the different keybindings available in each mode
31pub(crate) fn keybindings() -> Vec<(&'static str, Vec<(String, &'static str)>)> {
32    vec![
33        ("NORMAL", normal::normal_mode().1),
34        ("INSERT", insert::insert_mode().1),
35    ]
36}
37
38#[derive(Debug)]
39pub(crate) struct Mode {
40    pub(crate) name: String,
41    pub(crate) cur_shape: CurShape,
42    pub(crate) keymap: Trie<Input, Actions>,
43    handle_expired_pending: fn(&[Input]) -> Option<Actions>,
44}
45
46impl fmt::Display for Mode {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "{}", self.name)
49    }
50}
51
52impl Mode {
53    pub(crate) fn ephemeral_mode(name: &str) -> Self {
54        Mode {
55            name: name.to_string(),
56            cur_shape: CurShape::Block,
57            keymap: Trie::try_from_iter(Vec::new()).unwrap(),
58            handle_expired_pending: |_| None,
59        }
60    }
61
62    fn with_overrides(&mut self, overrides: Trie<Input, Actions>) -> Result<(), &'static str> {
63        self.keymap = self.keymap.clone().merge_overriding(overrides)?;
64
65        Ok(())
66    }
67
68    pub fn handle_keys(&self, keys: &mut Vec<Input>) -> Option<Actions> {
69        match self.keymap.get(keys) {
70            QueryResult::Val(actions) => {
71                keys.clear();
72                Some(actions.clone())
73            }
74            QueryResult::Partial => None,
75            QueryResult::Missing => {
76                let res = (self.handle_expired_pending)(keys);
77                keys.clear();
78                res
79            }
80        }
81    }
82}
83
84/// Construct a new [Trie] based keymap
85#[macro_export]
86macro_rules! keymap {
87    ($($docs:expr; [$($k:expr),+] => [ $($v:expr),+ ]),+,) => {
88        {
89            let mut pairs = Vec::new();
90            let mut docs = Vec::new();
91
92            $(
93                let key = vec![$($k),+];
94                let value = $crate::keymap!(@action $($v),+);
95                pairs.push((key, value));
96
97                let doc_key = vec![$($k.to_string()),+].join(" ");
98                docs.push((doc_key, $docs));
99            )+
100
101            ($crate::trie::Trie::try_from_iter(pairs).unwrap(), docs)
102        }
103    };
104
105    (@action $v:expr) => { $crate::editor::Actions::Single($v) };
106    (@action $($v:expr),+) => { $crate::editor::Actions::Multi(vec![$($v),+]) };
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    // This test will panic if any of the default keymaps end up with mappings that
114    // collide internally. The Trie struct rejects overlapping or duplicate keys on
115    // creation which will just panic the editor if this happens so it's worthwhile
116    // making sure we've not messed anything up.
117    #[test]
118    fn mode_keymaps_have_no_collisions() {
119        _ = modes(&KeyBindings::default());
120    }
121}