Skip to main content

typ_core/
action.rs

1//! The named vocabulary of editing operations.
2//!
3//! Every editing primitive in TYPE is an `Action` with explicit arguments.
4//! Three consumers depend on that: the keymap, the command palette, and the
5//! opt-in vim layer. A primitive reachable only from a `handle_key` arm is
6//! invisible to all three, so no key handler may mutate a buffer directly.
7
8/// Which way an operation runs.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum Direction {
11    Backward,
12    Forward,
13}
14
15/// Where a motion lands.
16///
17/// Motions carry no "extend" flag themselves — that is an argument of
18/// `Action::Move`, so every motion is automatically available in both forms
19/// rather than being listed twice and drifting apart.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum Motion {
22    Left,
23    Right,
24    Up,
25    Down,
26    WordLeft,
27    WordRight,
28    LineStart,
29    LineEnd,
30    PageUp,
31    PageDown,
32    DocumentStart,
33    DocumentEnd,
34}
35
36impl Motion {
37    pub const ALL: &'static [Motion] = &[
38        Motion::Left,
39        Motion::Right,
40        Motion::Up,
41        Motion::Down,
42        Motion::WordLeft,
43        Motion::WordRight,
44        Motion::LineStart,
45        Motion::LineEnd,
46        Motion::PageUp,
47        Motion::PageDown,
48        Motion::DocumentStart,
49        Motion::DocumentEnd,
50    ];
51}
52
53/// A named editing operation.
54///
55/// `InsertChar` is deliberately absent from `ALL` and unnameable: typed text
56/// arrives as a key event, not as a binding, and a bindable "insert this
57/// character" would be a text-substitution feature wearing a keybinding's
58/// clothes.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub enum Action {
61    Move {
62        motion: Motion,
63        extend: bool,
64    },
65    Delete {
66        direction: Direction,
67        by_word: bool,
68    },
69    InsertNewline,
70    InsertChar(char),
71    Undo,
72    Redo,
73    SelectAll,
74    SelectLine,
75    /// Select the word under the cursor, then each next occurrence of it.
76    SelectNextOccurrence,
77    /// Select every occurrence at once.
78    SelectAllOccurrences,
79    CollapseSelections,
80    AddCursor(Direction),
81    Save,
82    Quit,
83    FocusNext,
84    GotoLine,
85    SearchOpen,
86    SearchNext,
87    SearchPrevious,
88    ReplaceOpen,
89    Copy,
90    Cut,
91    Paste,
92    Indent,
93    Outdent,
94}
95
96impl Action {
97    /// Every action a config file may name, in a stable order.
98    pub const ALL: &'static [Action] = &[
99        Action::Move {
100            motion: Motion::Left,
101            extend: false,
102        },
103        Action::Move {
104            motion: Motion::Left,
105            extend: true,
106        },
107        Action::Move {
108            motion: Motion::Right,
109            extend: false,
110        },
111        Action::Move {
112            motion: Motion::Right,
113            extend: true,
114        },
115        Action::Move {
116            motion: Motion::Up,
117            extend: false,
118        },
119        Action::Move {
120            motion: Motion::Up,
121            extend: true,
122        },
123        Action::Move {
124            motion: Motion::Down,
125            extend: false,
126        },
127        Action::Move {
128            motion: Motion::Down,
129            extend: true,
130        },
131        Action::Move {
132            motion: Motion::WordLeft,
133            extend: false,
134        },
135        Action::Move {
136            motion: Motion::WordLeft,
137            extend: true,
138        },
139        Action::Move {
140            motion: Motion::WordRight,
141            extend: false,
142        },
143        Action::Move {
144            motion: Motion::WordRight,
145            extend: true,
146        },
147        Action::Move {
148            motion: Motion::LineStart,
149            extend: false,
150        },
151        Action::Move {
152            motion: Motion::LineStart,
153            extend: true,
154        },
155        Action::Move {
156            motion: Motion::LineEnd,
157            extend: false,
158        },
159        Action::Move {
160            motion: Motion::LineEnd,
161            extend: true,
162        },
163        Action::Move {
164            motion: Motion::PageUp,
165            extend: false,
166        },
167        Action::Move {
168            motion: Motion::PageUp,
169            extend: true,
170        },
171        Action::Move {
172            motion: Motion::PageDown,
173            extend: false,
174        },
175        Action::Move {
176            motion: Motion::PageDown,
177            extend: true,
178        },
179        Action::Move {
180            motion: Motion::DocumentStart,
181            extend: false,
182        },
183        Action::Move {
184            motion: Motion::DocumentStart,
185            extend: true,
186        },
187        Action::Move {
188            motion: Motion::DocumentEnd,
189            extend: false,
190        },
191        Action::Move {
192            motion: Motion::DocumentEnd,
193            extend: true,
194        },
195        Action::Delete {
196            direction: Direction::Backward,
197            by_word: false,
198        },
199        Action::Delete {
200            direction: Direction::Backward,
201            by_word: true,
202        },
203        Action::Delete {
204            direction: Direction::Forward,
205            by_word: false,
206        },
207        Action::Delete {
208            direction: Direction::Forward,
209            by_word: true,
210        },
211        Action::InsertNewline,
212        Action::Undo,
213        Action::Redo,
214        Action::SelectAll,
215        Action::SelectLine,
216        Action::SelectNextOccurrence,
217        Action::SelectAllOccurrences,
218        Action::CollapseSelections,
219        Action::AddCursor(Direction::Backward),
220        Action::AddCursor(Direction::Forward),
221        Action::Save,
222        Action::Quit,
223        Action::FocusNext,
224        Action::GotoLine,
225        Action::SearchOpen,
226        Action::SearchNext,
227        Action::SearchPrevious,
228        Action::ReplaceOpen,
229        Action::Copy,
230        Action::Cut,
231        Action::Paste,
232        Action::Indent,
233        Action::Outdent,
234    ];
235
236    pub fn name(&self) -> &'static str {
237        match self {
238            // The name is a compile-time pairing rather than a runtime
239            // `format!`, so it returns `&'static str` and compares without
240            // allocating on every keymap lookup.
241            Action::Move { motion, extend } => match (motion, extend) {
242                (Motion::Left, false) => "move_left",
243                (Motion::Left, true) => "extend_left",
244                (Motion::Right, false) => "move_right",
245                (Motion::Right, true) => "extend_right",
246                (Motion::Up, false) => "move_up",
247                (Motion::Up, true) => "extend_up",
248                (Motion::Down, false) => "move_down",
249                (Motion::Down, true) => "extend_down",
250                (Motion::WordLeft, false) => "move_word_left",
251                (Motion::WordLeft, true) => "extend_word_left",
252                (Motion::WordRight, false) => "move_word_right",
253                (Motion::WordRight, true) => "extend_word_right",
254                (Motion::LineStart, false) => "move_line_start",
255                (Motion::LineStart, true) => "extend_line_start",
256                (Motion::LineEnd, false) => "move_line_end",
257                (Motion::LineEnd, true) => "extend_line_end",
258                (Motion::PageUp, false) => "move_page_up",
259                (Motion::PageUp, true) => "extend_page_up",
260                (Motion::PageDown, false) => "move_page_down",
261                (Motion::PageDown, true) => "extend_page_down",
262                (Motion::DocumentStart, false) => "move_document_start",
263                (Motion::DocumentStart, true) => "extend_document_start",
264                (Motion::DocumentEnd, false) => "move_document_end",
265                (Motion::DocumentEnd, true) => "extend_document_end",
266            },
267            Action::Delete {
268                direction: Direction::Backward,
269                by_word: false,
270            } => "delete_backward",
271            Action::Delete {
272                direction: Direction::Backward,
273                by_word: true,
274            } => "delete_word_backward",
275            Action::Delete {
276                direction: Direction::Forward,
277                by_word: false,
278            } => "delete_forward",
279            Action::Delete {
280                direction: Direction::Forward,
281                by_word: true,
282            } => "delete_word_forward",
283            Action::InsertNewline => "insert_newline",
284            // Never returned by `from_name`; see the type docs.
285            Action::InsertChar(_) => "insert_char_literal",
286            Action::Undo => "undo",
287            Action::Redo => "redo",
288            Action::SelectAll => "select_all",
289            Action::SelectLine => "select_line",
290            Action::SelectNextOccurrence => "select_next_occurrence",
291            Action::SelectAllOccurrences => "select_all_occurrences",
292            Action::CollapseSelections => "collapse_selections",
293            Action::AddCursor(Direction::Backward) => "add_cursor_above",
294            Action::AddCursor(Direction::Forward) => "add_cursor_below",
295            Action::Save => "save",
296            Action::Quit => "quit",
297            Action::FocusNext => "focus_next",
298            Action::GotoLine => "goto_line",
299            Action::SearchOpen => "search_open",
300            Action::SearchNext => "search_next",
301            Action::SearchPrevious => "search_previous",
302            Action::ReplaceOpen => "replace_open",
303            Action::Copy => "copy",
304            Action::Cut => "cut",
305            Action::Paste => "paste",
306            Action::Indent => "indent",
307            Action::Outdent => "outdent",
308        }
309    }
310
311    /// Look an action up by the name a config file uses.
312    ///
313    /// Linear over ~40 entries and called once per keymap load, never per
314    /// keypress — a map would be more code for no measurable gain.
315    pub fn from_name(name: &str) -> Option<Action> {
316        Action::ALL.iter().copied().find(|a| a.name() == name)
317    }
318}