aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/input/keybinds.rs
blob: 3da34e4b8b248fda3b556d1839050443159b0d67 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::collections::{BTreeMap, HashMap};

use super::actions::Action;
use crate::data::{BareKey, InputMode, KeyWithModifier, KeybindsVec};

use serde::{Deserialize, Serialize};
use std::fmt;

/// Used in the config struct
#[derive(Clone, PartialEq, Deserialize, Serialize, Default)]
pub struct Keybinds(pub HashMap<InputMode, HashMap<KeyWithModifier, Vec<Action>>>);

impl fmt::Debug for Keybinds {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut stable_sorted = BTreeMap::new();
        for (mode, keybinds) in self.0.iter() {
            let mut stable_sorted_mode_keybinds = BTreeMap::new();
            for (key, actions) in keybinds {
                stable_sorted_mode_keybinds.insert(key, actions);
            }
            stable_sorted.insert(mode, stable_sorted_mode_keybinds);
        }
        write!(f, "{:#?}", stable_sorted)
    }
}

impl Keybinds {
    pub fn get_actions_for_key_in_mode(
        &self,
        mode: &InputMode,
        key: &KeyWithModifier,
    ) -> Option<&Vec<Action>> {
        self.0
            .get(mode)
            .and_then(|normal_mode_keybindings| normal_mode_keybindings.get(key))
    }
    pub fn get_actions_for_key_in_mode_or_default_action(
        &self,
        mode: &InputMode,
        key_with_modifier: &KeyWithModifier,
        raw_bytes: Vec<u8>,
        default_input_mode: InputMode,
        key_is_kitty_protocol: bool,
    ) -> Vec<Action> {
        self.0
            .get(mode)
            .and_then(|mode_keybindings| {
                if raw_bytes == &[10] {
                    handle_ctrl_j(&mode_keybindings, &raw_bytes, key_is_kitty_protocol)
                } else {
                    mode_keybindings.get(key_with_modifier).cloned()
                }
            })
            .unwrap_or_else(|| {
                vec![self.default_action_for_mode(
                    mode,
                    Some(key_with_modifier),
                    raw_bytes,
                    default_input_mode,
                    key_is_kitty_protocol,
                )]
            })
    }
    pub fn get_input_mode_mut(
        &mut self,
        input_mode: &InputMode,
    ) -> &mut HashMap<KeyWithModifier, Vec<Action>> {
        self.0.entry(*input_mode).or_insert_with(HashMap::new)
    }
    pub fn default_action_for_mode(
        &self,
        mode: &InputMode,
        key_with_modifier: Option<&KeyWithModifier>,
        raw_bytes: Vec<u8>,
        default_input_mode: InputMode,
        key_is_kitty_protocol: bool,
    ) -> Action {
        match *mode {
            InputMode::Locked => {
                Action::Write(key_with_modifier.cloned(), raw_bytes, key_is_kitty_protocol)
            },
            mode if mode == default_input_mode => {
                Action::Write(key_with_modifier.cloned(), raw_bytes, key_is_kitty_protocol)
            },
            InputMode::RenameTab => Action::TabNameInput(raw_bytes),
            InputMode::RenamePane => Action::PaneNameInput(raw_bytes),
            InputMode::EnterSearch => Action::SearchInput(raw_bytes),
            _ => Action::NoOp,
        }
    }
    pub fn to_keybinds_vec(&self) -> KeybindsVec {
        let mut ret = vec![];
        for (mode, mode_binds) in &self.0 {
            let mut mode_binds_vec: Vec<(KeyWithModifier, Vec<Action>)> = vec![];
            for (key, actions) in mode_binds {
                mode_binds_vec.push((key.clone(), actions.clone()));
            }
            ret.push((*mode, mode_binds_vec))
        }
        ret
    }
    pub fn merge(&mut self, mut other: Keybinds) {
        for (other_input_mode, mut other_input_mode_keybinds) in other.0.drain() {
            let input_mode_keybinds = self
                .0
                .entry(other_input_mode)
                .or_insert_with(|| Default::default());
            for (other_action, other_action_keybinds) in other_input_mode_keybinds.drain() {
                input_mode_keybinds.insert(other_action, other_action_keybinds);
            }
        }
    }
}

// we need to do this because [10] in standard STDIN, [10] is both Enter (without a carriage
// return) and ctrl-j - so here, if ctrl-j is bound we return its bound action, and otherwise we
// just write the raw bytes to the terminal and let whichever program is there decide what they are
fn handle_ctrl_j(
    mode_keybindings: &HashMap<KeyWithModifier, Vec<Action>>,
    raw_bytes: &[u8],
    key_is_kitty_protocol: bool,
) -> Option<Vec<Action>> {
    let ctrl_j = KeyWithModifier::new(BareKey::Char('j')).with_ctrl_modifier();
    if mode_keybindings.get(&ctrl_j).is_some() {
        mode_keybindings.get(&ctrl_j).cloned()
    } else {
        Some(vec![Action::Write(
            Some(ctrl_j),
            raw_bytes.to_vec().clone(),
            key_is_kitty_protocol,
        )])
    }
}

// The unit test location.
#[cfg(test)]
#[path = "./unit/keybinds_test.rs"]
mod keybinds_test;