Skip to main content

strop_engine/editor/
macros.rs

1//! Macros (0016 §macros): `q{reg}` records raw key events, `@{reg}`
2//! replays them — replay feeds keys back through the ONE input
3//! machine, so a macro is exactly as capable as hands on the keyboard.
4//! Counts work (`3@a`), `@@` repeats the last replay.
5
6use super::Editor;
7
8impl Editor {
9    /// `q{reg}`: start recording; `q` while recording stops (handled in
10    /// `feed` before the machine sees it — the toggle key never
11    /// records itself).
12    pub(crate) fn macro_toggle(&mut self, reg: char) {
13        self.recording = Some(reg);
14        self.macros.insert(reg, Vec::new());
15        self.message = format!("recording @{reg}");
16    }
17
18    /// `@{reg}`: replay count times. A guard bounds self-replay (`@a`
19    /// inside @a) — vim errors at recursion, we stop at 64 deep.
20    pub(crate) fn macro_play(&mut self, reg: char, count: usize) {
21        let Some(keys) = self.macros.get(&reg).cloned() else {
22            self.message = format!("register @{reg} is empty");
23            return;
24        };
25        if keys.is_empty() {
26            self.message = format!("register @{reg} is empty");
27            return;
28        }
29        self.last_macro = Some(reg);
30        let depth = self.macro_depth;
31        if depth >= 64 {
32            self.message = "macro recursion too deep".into();
33            return;
34        }
35        if self.resolution.enabled {
36            self.queue_macro(keys, count, depth + 1);
37            return;
38        }
39        for _ in 0..count {
40            for key in &keys {
41                if self.should_quit {
42                    return;
43                }
44                self.macro_depth = depth + 1;
45                self.feed(*key);
46                self.macro_depth = depth;
47            }
48        }
49    }
50
51    /// `@@`: the last replayed register (vim).
52    pub(crate) fn macro_again(&mut self, count: usize) {
53        match self.last_macro {
54            Some(reg) => self.macro_play(reg, count),
55            None => self.message = "no macro replayed yet".into(),
56        }
57    }
58}