edtui_papier/
input.rs

1//! Handles key input events
2pub mod key;
3pub mod register;
4
5use std::fmt::Debug;
6
7use crossterm::event::{KeyCode, KeyEvent};
8use serde::{Deserialize, Serialize};
9
10use self::{
11    key::Key,
12    register::{Register, RegisterKey},
13};
14use crate::{
15    actions::{
16        motion::MoveWordForwardEnd, search::StartSearch, Action, Append, AppendCharToSearch, AppendNewline, Composed,
17        CopySelection, Custom, DeleteChar, DeleteLine, DeleteSelection, Execute, FindNext, FindPrevious, InsertChar,
18        InsertNewline, LineBreak, MoveBackward, MoveDown, MoveForward, MoveToEnd, MoveToFirst, MoveToStart, MoveUp,
19        MoveWordBackward, MoveWordForwardStart, Paste, Redo, RemoveChar, RemoveCharFromSearch, SelectBetween,
20        StopSearch, SwitchMode, TriggerSearch, Undo,
21    },
22    debug::log_to_file,
23    state::command::CommandState,
24    EditorMode, EditorState,
25};
26
27#[derive(Clone, Debug)]
28pub struct Input<I>
29where
30    I: Clone + Execute + Serialize + for<'de> Deserialize<'de> + Default,
31{
32    pub register: Register<I>,
33    pub command: CommandState<I>,
34}
35
36impl<I> Default for Input<I>
37where
38    I: Clone + Execute + Serialize + for<'de> Deserialize<'de> + Default,
39{
40    #[allow(clippy::too_many_lines)]
41    fn default() -> Self {
42        let mut r = Register::<I>::new();
43
44        // Go into normal mode
45        r.insert(RegisterKey::i(vec![Key::Esc]), Action::SwitchMode(SwitchMode(EditorMode::Normal)));
46        r.insert(RegisterKey::v(vec![Key::Esc]), SwitchMode(EditorMode::Normal));
47
48        // Go into insert mode
49        r.insert(RegisterKey::n(vec![Key::Char('i')]), SwitchMode(EditorMode::Insert));
50
51        // Go into visual mode
52        r.insert(RegisterKey::n(vec![Key::Char('v')]), SwitchMode(EditorMode::Visual));
53
54        // Goes into search mode and starts of a new search.
55        r.insert(RegisterKey::n(vec![Key::Char('/')]), StartSearch);
56        // Trigger initial search
57        r.insert(RegisterKey::s(vec![Key::Enter]), TriggerSearch);
58        // Find next
59        r.insert(RegisterKey::n(vec![Key::Char('n')]), FindNext);
60        // Find previous
61        r.insert(RegisterKey::n(vec![Key::Char('N')]), FindPrevious);
62        // Clear search
63        r.insert(RegisterKey::s(vec![Key::Esc]), StopSearch);
64        // Delete last character from search
65        r.insert(RegisterKey::s(vec![Key::Backspace]), RemoveCharFromSearch);
66
67        // Go into insert mode and move one char forward
68        r.insert(RegisterKey::n(vec![Key::Char('a')]), Append);
69
70        // Move cursor right
71        r.insert(RegisterKey::n(vec![Key::Char('l')]), MoveForward(1));
72        r.insert(RegisterKey::n(vec![Key::Right]), MoveForward(1));
73        r.insert(RegisterKey::v(vec![Key::Char('l')]), MoveForward(1));
74        r.insert(RegisterKey::v(vec![Key::Right]), MoveForward(1));
75        r.insert(RegisterKey::i(vec![Key::Right]), MoveForward(1));
76
77        // Move cursor left
78        r.insert(RegisterKey::n(vec![Key::Char('h')]), MoveBackward(1));
79        r.insert(RegisterKey::n(vec![Key::Left]), MoveBackward(1));
80        r.insert(RegisterKey::v(vec![Key::Char('h')]), MoveBackward(1));
81        r.insert(RegisterKey::v(vec![Key::Left]), MoveBackward(1));
82        r.insert(RegisterKey::i(vec![Key::Left]), MoveBackward(1));
83
84        // Move cursor up
85        r.insert(RegisterKey::n(vec![Key::Char('k')]), MoveUp(1));
86        r.insert(RegisterKey::n(vec![Key::Up]), MoveUp(1));
87        r.insert(RegisterKey::v(vec![Key::Char('k')]), MoveUp(1));
88        r.insert(RegisterKey::v(vec![Key::Up]), MoveUp(1));
89        r.insert(RegisterKey::i(vec![Key::Up]), MoveUp(1));
90
91        // Move cursor down
92        r.insert(RegisterKey::n(vec![Key::Char('j')]), MoveDown(1));
93        r.insert(RegisterKey::n(vec![Key::Down]), MoveDown(1));
94        r.insert(RegisterKey::v(vec![Key::Char('j')]), MoveDown(1));
95        r.insert(RegisterKey::v(vec![Key::Down]), MoveDown(1));
96        r.insert(RegisterKey::i(vec![Key::Down]), MoveDown(1));
97
98        // Move one word forward/backward
99        r.insert(RegisterKey::n(vec![Key::Char('w')]), MoveWordForwardStart(1));
100        r.insert(RegisterKey::n(vec![Key::Char('e')]), MoveWordForwardEnd(1));
101        r.insert(RegisterKey::n(vec![Key::Char('b')]), MoveWordBackward(1));
102        r.insert(RegisterKey::v(vec![Key::Char('w')]), MoveWordForwardStart(1));
103        r.insert(RegisterKey::v(vec![Key::Char('e')]), MoveWordForwardEnd(1));
104        r.insert(RegisterKey::v(vec![Key::Char('b')]), MoveWordBackward(1));
105
106        // Move cursor to start/first/last position
107        r.insert(RegisterKey::n(vec![Key::Char('0')]), MoveToStart());
108        r.insert(RegisterKey::n(vec![Key::Char('_')]), MoveToFirst());
109        r.insert(RegisterKey::n(vec![Key::Char('$')]), MoveToEnd());
110        r.insert(RegisterKey::v(vec![Key::Char('0')]), MoveToStart());
111        r.insert(RegisterKey::v(vec![Key::Char('_')]), MoveToFirst());
112        r.insert(RegisterKey::v(vec![Key::Char('$')]), MoveToEnd());
113
114        // Move cursor to start/first/last position and enter insert mode
115        r.insert(
116            RegisterKey::n(vec![Key::Char('I')]),
117            Composed::new(MoveToFirst()).chain(SwitchMode(EditorMode::Insert)),
118        );
119        r.insert(RegisterKey::n(vec![Key::Char('A')]), Composed::new(MoveToEnd()).chain(Append));
120
121        // Append/insert new line and switch into insert mode
122        r.insert(
123            RegisterKey::n(vec![Key::Char('o')]),
124            Composed::new(AppendNewline(1)).chain(SwitchMode(EditorMode::Insert)),
125        );
126        r.insert(
127            RegisterKey::n(vec![Key::Char('O')]),
128            Composed::new(InsertNewline(1)).chain(SwitchMode(EditorMode::Insert)),
129        );
130
131        // Insert a line break
132        r.insert(RegisterKey::i(vec![Key::Enter]), LineBreak(1));
133
134        // Remove the current character
135        r.insert(RegisterKey::n(vec![Key::Char('x')]), RemoveChar(1));
136
137        // Delete the previous character
138        r.insert(RegisterKey::i(vec![Key::Backspace]), DeleteChar(1));
139
140        // Delete the current line
141        r.insert(RegisterKey::n(vec![Key::Char('d'), Key::Char('d')]), DeleteLine(1));
142
143        // Delete the current selection
144        r.insert(RegisterKey::v(vec![Key::Char('d')]), DeleteSelection);
145
146        // Select inner word between delimiters
147        r.insert(
148            RegisterKey::n(vec![Key::Char('c'), Key::Char('i'), Key::Char('w')]),
149            SelectBetween(vec![('(', ')'), ('[', ']'), ('{', '}'), ('<', '>'), ('"', '"'), ('\'', '\'')]),
150        );
151
152        // Undo
153        r.insert(RegisterKey::n(vec![Key::Char('u')]), Undo);
154
155        // Redo
156        r.insert(RegisterKey::n(vec![Key::Char('r')]), Redo);
157
158        // Copy
159        r.insert(RegisterKey::v(vec![Key::Char('y')]), CopySelection);
160
161        // Paste
162        r.insert(RegisterKey::n(vec![Key::Char('p')]), Paste);
163        r.insert(RegisterKey::v(vec![Key::Char('p')]), Paste);
164
165        Self { register: r, command: CommandState::default() }
166    }
167}
168
169impl<I> Input<I>
170where
171    I: Clone + Execute + Serialize + for<'de> Deserialize<'de> + Default + Debug,
172{
173    pub fn on_key<T>(&mut self, key: T, state: &mut EditorState) -> Option<Custom<I>>
174    where
175        T: Into<KeyEvent> + Copy,
176    {
177        let mode = state.mode;
178        /*
179
180        r.insert(RegisterKey::n(vec![Key::Char(':')]), StartCommand);
181        r.insert(RegisterKey::c(vec![Key::Esc]), StopCommand);
182        r.insert(RegisterKey::c(vec![Key::Enter]), TriggerCommand);
183        r.insert(RegisterKey::c(vec![Key::Backspace]), RemoveCharFromCommand);
184         */
185        match key.into().code {
186            // Always insert characters in insert mode
187            KeyCode::Char(c) if mode == EditorMode::Insert => InsertChar(c).execute(state),
188            // Always add characters to search in search mode
189            KeyCode::Char(c) if mode == EditorMode::Search => AppendCharToSearch(c).execute(state),
190
191            KeyCode::Char(':') if mode == EditorMode::Normal => {
192                self.command.clear();
193                state.command = self.command.input.clone();
194                state.mode = EditorMode::Command;
195            },
196            KeyCode::Char(c) if mode == EditorMode::Command => {
197                self.command.push_char(c);
198                log_to_file(format!("Command: {}", self.command.input));
199                state.command = self.command.input.clone();
200            },
201            KeyCode::Backspace if mode == EditorMode::Command => {
202                self.command.remove_char();
203                state.command = self.command.input.clone();
204            },
205            KeyCode::Esc if mode == EditorMode::Command => {
206                self.command.clear();
207                state.command = self.command.input.clone();
208                state.mode = EditorMode::Normal;
209            },
210            KeyCode::Enter if mode == EditorMode::Command => {
211                let commands = self.command.available_commands.clone();
212                log_to_file(format!("Commands: {:?}", commands));
213                let command =
214                    commands.iter().find(|c| c.name == self.command.input || c.aliases.contains(&self.command.input));
215                log_to_file(format!("Command: {:?}", command.clone()));
216                state.mode = EditorMode::Normal;
217                self.command.clear();
218                state.command = self.command.input.clone();
219                if let Some(command) = command {
220                    return Some(Custom((command.action)(self.command.input.clone())));
221                }
222            },
223
224            // Else lookup an action from the register
225            _ => {
226                if let Some(mut action) = self.register.get(key.into(), mode) {
227                    match action {
228                        Action::Custom(action) => return Some(action),
229                        _ => action.execute(state),
230                    }
231                }
232            },
233        }
234        None
235    }
236}