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 pub select: SeekSelect,
39 pub include: SeekInclude,
41 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![], "<ret>" => vec![Seek(b'\n')], "<tab>" => vec![Seek(b'\t')], 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 } }
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}