teehee/modes/
normal.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3
4use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
5use lazy_static::lazy_static;
6
7use crate::keymap::KeyMap;
8use crate::operations as ops;
9use crate::selection::Direction;
10use crate::{
11    cmd_count, modes,
12    modes::mode::{DirtyBytes, Mode, ModeTransition},
13    Buffers,
14};
15
16use super::insert::InsertionMode;
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19pub struct Normal {
20    count_state: cmd_count::State,
21}
22
23#[derive(Debug, PartialEq, Clone, Copy)]
24enum Action {
25    Move(Direction),
26    Extend(Direction),
27    SplitMode,
28    JumpToMode,
29    ExtendToMode,
30    CollapseMode { hex: bool },
31    CommandMode,
32    SwapCaret,
33    CollapseSelection,
34    Delete { register: char },
35    Yank { register: char },
36    Paste { after: bool, register: char },
37    Change { hex: bool, register: char },
38    Insert { hex: bool },
39    Append { hex: bool },
40    Overwrite { hex: bool },
41    RemoveMain,
42    RetainMain,
43    SelectPrev,
44    SelectNext,
45    SelectAll,
46    ReplaceMode { hex: bool },
47    Measure,
48    Undo,
49    Redo,
50}
51
52fn default_maps() -> KeyMap<Action> {
53    KeyMap {
54        maps: keys!(
55            ('h' => Action::Move(Direction::Left)),
56            (key KeyCode::Left => Action::Move(Direction::Left)),
57            ('j' => Action::Move(Direction::Down)),
58            (key KeyCode::Down => Action::Move(Direction::Down)),
59            ('k' => Action::Move(Direction::Up)),
60            (key KeyCode::Up => Action::Move(Direction::Up)),
61            ('l' => Action::Move(Direction::Right)),
62            (key KeyCode::Right => Action::Move(Direction::Right)),
63            ('H' => Action::Extend(Direction::Left)),
64            ('J' => Action::Extend(Direction::Down)),
65            ('K' => Action::Extend(Direction::Up)),
66            ('L' => Action::Extend(Direction::Right)),
67            ('g' => Action::JumpToMode),
68            ('G' => Action::ExtendToMode),
69            (alt 's' => Action::SplitMode),
70            (':' => Action::CommandMode),
71            (';' => Action::CollapseSelection),
72            (alt ';' => Action::SwapCaret),
73            ('%' => Action::SelectAll),
74            (' ' => Action::RetainMain),
75            (alt ' ' => Action::RemoveMain),
76            ('(' => Action::SelectPrev),
77            (')' => Action::SelectNext),
78            ('M' => Action::Measure),
79            ('u' => Action::Undo),
80            ('U' => Action::Redo),
81
82            ('p' => Action::Paste{after: true, register: '"'}),
83            ('P' => Action::Paste{after: false, register: '"'}),
84            ('d' => Action::Delete{register: '"'}),
85            ('y' => Action::Yank{register: '"'}),
86            ('c' => Action::Change{hex: false, register: '"'}),
87            ('C' => Action::Change{hex: true, register: '"'}),
88
89            ('i' => Action::Insert{hex: false}),
90            ('I' => Action::Insert{hex: true}),
91            ('a' => Action::Append{hex: false}),
92            ('A' => Action::Append{hex: true}),
93            ('r' => Action::ReplaceMode{hex: false}),
94            ('R' => Action::ReplaceMode{hex: true}),
95            ('o' => Action::Overwrite{hex: false}),
96            ('O' => Action::Overwrite{hex: true}),
97
98            ('s' => Action::CollapseMode{hex: false}),
99            ('S' => Action::CollapseMode{hex: true})
100        ),
101    }
102}
103
104lazy_static! {
105    static ref DEFAULT_MAPS: KeyMap<Action> = default_maps();
106}
107
108impl Mode for Normal {
109    fn name(&self) -> Cow<'static, str> {
110        format!("NORMAL{}", self.count_state).into()
111    }
112
113    fn transition(
114        &self,
115        event: &Event,
116        buffers: &mut Buffers,
117        bytes_per_line: usize,
118    ) -> Option<ModeTransition> {
119        let buffer = buffers.current_mut();
120        if let cmd_count::Transition::Update(new_state) = self.count_state.transition(event) {
121            Some(ModeTransition::new_mode(Normal {
122                count_state: new_state,
123            }))
124        } else if let Some(action) = DEFAULT_MAPS.event_to_action(event) {
125            Some(match action {
126                Action::JumpToMode => match self.count_state {
127                    cmd_count::State::None => {
128                        ModeTransition::new_mode(modes::jumpto::JumpTo { extend: false })
129                    }
130                    cmd_count::State::Some { count: offset, .. } => {
131                        ModeTransition::new_mode_and_dirty(
132                            Normal::new(),
133                            buffer.map_selections(|region| vec![region.jump_to(offset)]),
134                        )
135                    }
136                },
137                Action::ExtendToMode => match self.count_state {
138                    cmd_count::State::None => {
139                        ModeTransition::new_mode(modes::jumpto::JumpTo { extend: true })
140                    }
141                    cmd_count::State::Some { count: offset, .. } => {
142                        ModeTransition::new_mode_and_dirty(
143                            Normal::new(),
144                            buffer.map_selections(|region| vec![region.extend_to(offset)]),
145                        )
146                    }
147                },
148                Action::SplitMode => ModeTransition::new_mode(modes::split::Split::new()),
149                Action::Insert { hex } => ModeTransition::new_mode_and_dirty(
150                    modes::insert::Insert {
151                        hex,
152                        mode: InsertionMode::Insert,
153                        hex_half: None,
154                    },
155                    buffer.map_selections(|region| vec![region.to_backward()]),
156                ),
157                Action::Append { hex } => ModeTransition::new_mode_and_dirty(
158                    modes::insert::Insert {
159                        hex,
160                        mode: InsertionMode::Append,
161                        hex_half: None,
162                    },
163                    {
164                        let max_size = buffer.data.len();
165                        buffer.map_selections(|region| {
166                            vec![region.to_forward().simple_extend(
167                                Direction::Right,
168                                bytes_per_line,
169                                max_size,
170                                1,
171                            )]
172                        })
173                    },
174                ),
175                Action::ReplaceMode { hex } => ModeTransition::new_mode(modes::replace::Replace {
176                    hex,
177                    hex_half: None,
178                }),
179                Action::Overwrite { hex } => ModeTransition::new_mode(modes::insert::Insert {
180                    hex,
181                    mode: InsertionMode::Overwrite,
182                    hex_half: None,
183                }),
184                Action::Move(direction) => {
185                    let max_bytes = buffer.data.len();
186                    ModeTransition::new_mode_and_dirty(
187                        Normal::new(),
188                        buffer.map_selections(|region| {
189                            vec![region.simple_move(
190                                direction,
191                                bytes_per_line,
192                                max_bytes,
193                                self.count_state.to_count(),
194                            )]
195                        }),
196                    )
197                }
198                Action::Extend(direction) => {
199                    let max_bytes = buffer.data.len();
200                    ModeTransition::new_mode_and_dirty(
201                        Normal::new(),
202                        buffer.map_selections(|region| {
203                            vec![region.simple_extend(
204                                direction,
205                                bytes_per_line,
206                                max_bytes,
207                                self.count_state.to_count(),
208                            )]
209                        }),
210                    )
211                }
212                Action::SwapCaret => ModeTransition::DirtyBytes(
213                    buffer.map_selections(|region| vec![region.swap_caret()]),
214                ),
215                Action::CollapseSelection => ModeTransition::DirtyBytes(
216                    buffer.map_selections(|region| vec![region.collapse()]),
217                ),
218                Action::Delete { register } => {
219                    buffer.yank_selections(register);
220                    if !buffer.data.is_empty() {
221                        let delta = ops::deletion(&buffer.data, &buffer.selection);
222                        ModeTransition::DirtyBytes(buffer.apply_delta(delta))
223                    } else {
224                        ModeTransition::None
225                    }
226                }
227                Action::Change { hex, register } => {
228                    buffer.yank_selections(register);
229                    if !buffer.data.is_empty() {
230                        let delta = ops::deletion(&buffer.data, &buffer.selection);
231                        ModeTransition::new_mode_and_dirty(
232                            modes::insert::Insert {
233                                hex,
234                                mode: InsertionMode::Insert,
235                                hex_half: None,
236                            },
237                            buffer.apply_delta(delta),
238                        )
239                    } else {
240                        ModeTransition::new_mode(modes::insert::Insert {
241                            hex,
242                            mode: InsertionMode::Insert,
243                            hex_half: None,
244                        })
245                    }
246                }
247                Action::Yank { register } => {
248                    buffer.yank_selections(register);
249                    ModeTransition::None
250                }
251                Action::Paste { register, after } => {
252                    let delta = ops::paste(
253                        &buffer.data,
254                        &buffer.selection,
255                        buffer.registers.get(&register).unwrap_or(&vec![vec![]]),
256                        after,
257                        self.count_state.to_count(),
258                    );
259                    ModeTransition::DirtyBytes(buffer.apply_delta(delta))
260                }
261                // selection indexing in the UI starts at 1
262                // hence we check for count > 0 and offset by -1
263                Action::RemoveMain => match self.count_state {
264                    cmd_count::State::Some { count, .. } if count > 0 => {
265                        ModeTransition::new_mode_and_dirty(
266                            Normal::new(),
267                            buffer.remove_selection(count - 1),
268                        )
269                    }
270                    _ => ModeTransition::DirtyBytes(
271                        buffer.remove_selection(buffer.selection.main_selection),
272                    ),
273                },
274                Action::RetainMain => match self.count_state {
275                    cmd_count::State::Some { count, .. } if count > 0 => {
276                        ModeTransition::new_mode_and_dirty(
277                            Normal::new(),
278                            buffer.retain_selection(count - 1),
279                        )
280                    }
281                    _ => ModeTransition::DirtyBytes(
282                        buffer.retain_selection(buffer.selection.main_selection),
283                    ),
284                },
285
286                // new_mode to clear count
287                Action::SelectNext => ModeTransition::new_mode_and_dirty(
288                    Normal::new(),
289                    buffer.select_next(self.count_state.to_count()),
290                ),
291                Action::SelectPrev => ModeTransition::new_mode_and_dirty(
292                    Normal::new(),
293                    buffer.select_prev(self.count_state.to_count()),
294                ),
295                Action::SelectAll => {
296                    buffer.selection.select_all(buffer.data.len());
297                    ModeTransition::DirtyBytes(DirtyBytes::ChangeInPlace(vec![(0..buffer
298                        .data
299                        .len())
300                        .into()]))
301                }
302                Action::CollapseMode { hex } => ModeTransition::new_mode(
303                    modes::search::Search::new(modes::collapse::Collapse(), hex),
304                ),
305                Action::Measure => ModeTransition::new_mode_and_info(
306                    Normal::new(),
307                    format!(
308                        "{} = 0x{:x} bytes",
309                        buffer.selection.main().len(),
310                        buffer.selection.main().len()
311                    ),
312                ),
313                Action::CommandMode => ModeTransition::new_mode(modes::command::Command::new()),
314                Action::Undo => buffer.perform_undo().map_or_else(
315                    || {
316                        ModeTransition::new_mode_and_info(
317                            Normal::new(),
318                            "nothing left to undo".to_owned(),
319                        )
320                    },
321                    |dirty| ModeTransition::new_mode_and_dirty(Normal::new(), dirty),
322                ),
323                Action::Redo => buffer.perform_redo().map_or_else(
324                    || {
325                        ModeTransition::new_mode_and_info(
326                            Normal::new(),
327                            "nothing left to redo".to_owned(),
328                        )
329                    },
330                    |dirty| ModeTransition::new_mode_and_dirty(Normal::new(), dirty),
331                ),
332            })
333        } else {
334            None
335        }
336    }
337    fn as_any(&self) -> &dyn std::any::Any {
338        self
339    }
340}
341
342impl Normal {
343    pub fn new() -> Normal {
344        Normal {
345            count_state: cmd_count::State::None,
346        }
347    }
348}