Skip to main content

indigo_core/mode/
seek.rs

1#![allow(clippy::enum_glob_use)]
2
3use crate::{
4    editor::Editor,
5    key::{KeyCode, KeyModifiers},
6    keymap::{Keymap, KeymapResult, keymap},
7    mode::{Mode, normal},
8};
9use std::{num::NonZeroUsize, sync::LazyLock};
10
11#[cfg(feature = "arbitrary")]
12use arbitrary::Arbitrary;
13
14#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
15#[derive(Clone, Copy, Debug)]
16pub enum SeekSelect {
17    Move,
18    Extend,
19}
20
21#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
22#[derive(Clone, Copy, Debug)]
23pub enum SeekInclude {
24    Until,
25    Onto,
26}
27
28#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
29#[derive(Clone, Copy, Debug)]
30pub enum SeekDirection {
31    Prev,
32    Next,
33}
34
35#[derive(Clone)]
36pub struct State {
37    /// Move and create a new selection, or extend the current one?
38    pub select: SeekSelect,
39    /// Include seek target in selection, or stop just short?
40    pub include: SeekInclude,
41    /// Seek forward or backward?
42    pub direction: SeekDirection,
43}
44
45#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
46#[derive(Debug)]
47pub enum Action {
48    Seek(u8),
49}
50
51pub static KEYMAP: LazyLock<Keymap<Vec<Action>>> = LazyLock::new(|| {
52    use Action::*;
53    keymap! { Vec<Action>;
54        "<esc>" => vec![], /// Cancel
55        "<ret>" => vec![Seek(b'\n')], /// Seek to newline
56        "<tab>" => vec![Seek(b'\t')], /// Seek to tab
57        keys => {
58            if let [key] = keys
59                && let KeyCode::Char(c) = key.code
60                && key.modifiers.difference(KeyModifiers::SHIFT).is_empty()
61            {
62                KeymapResult::Fallback(vec![Seek(c)])
63            } else {
64                KeymapResult::Unmapped
65            }
66        } /// Seek to character
67    }
68});
69
70pub fn handle_actions(editor: &mut Editor, actions: &[Action]) {
71    for action in actions {
72        handle_action(editor, action);
73    }
74}
75
76pub fn handle_action(editor: &mut Editor, action: &Action) {
77    match action {
78        Action::Seek(byte) => seek(editor, *byte),
79    }
80}
81
82pub fn handle_keys(editor: &mut Editor) -> bool {
83    match KEYMAP.get_keys(&editor.pending_keys) {
84        KeymapResult::Mapped(actions) => {
85            editor.pending_keys.clear();
86            handle_actions(editor, actions);
87            normal::enter(editor);
88        }
89        KeymapResult::Fallback(actions) => {
90            editor.pending_keys.clear();
91            handle_actions(editor, &actions);
92            normal::enter(editor);
93        }
94        KeymapResult::Unmapped => {
95            editor.pending_keys.clear();
96            normal::enter(editor);
97        }
98        KeymapResult::Pending => {}
99    }
100
101    true
102}
103
104pub fn enter(
105    editor: &mut Editor,
106    select: SeekSelect,
107    include: SeekInclude,
108    direction: SeekDirection,
109) {
110    editor.mode = Mode::Seek(State {
111        select,
112        include,
113        direction,
114    });
115}
116
117fn seek(editor: &mut Editor, byte: u8) {
118    use SeekDirection::{Next, Prev};
119    use SeekInclude::{Onto, Until};
120    use SeekSelect::{Extend, Move};
121
122    let (select, include, direction, count) = {
123        let Mode::Seek(seek_mode) = &editor.mode else {
124            panic!("Not in seek mode")
125        };
126        let count = editor.count.take().unwrap_or(NonZeroUsize::MIN).get();
127        (
128            seek_mode.select,
129            seek_mode.include,
130            seek_mode.direction,
131            count,
132        )
133    };
134
135    let mut window = editor.focused_window_mut();
136
137    window
138        .selection_move()
139        .for_each_move(|mut range| match (select, include, direction) {
140            (Move, Until, Prev) => range.move_until_prev_byte(byte, count),
141            (Extend, Until, Prev) => range.extend_until_prev_byte(byte, count),
142            (Move, Until, Next) => range.move_until_next_byte(byte, count),
143            (Extend, Until, Next) => range.extend_until_next_byte(byte, count),
144            (Move, Onto, Prev) => range.move_onto_prev_byte(byte, count),
145            (Extend, Onto, Prev) => range.extend_onto_prev_byte(byte, count),
146            (Move, Onto, Next) => range.move_onto_next_byte(byte, count),
147            (Extend, Onto, Next) => range.extend_onto_next_byte(byte, count),
148        });
149    window.selection_move().merge_overlapping();
150
151    window.scroll_to_selection();
152}