Skip to main content

strop_engine/editor/trace/
mod.rs

1//! Editor-specific producers and the forensic replay driver. Storage and
2//! ordering belong to strop-trace.
3pub mod drive;
4#[cfg(test)]
5pub(crate) mod model;
6pub mod seed;
7pub mod services;
8mod snapshot;
9
10#[cfg(test)]
11mod generated;
12#[cfg(test)]
13mod tests;
14
15use super::{Editor, Key};
16use serde::Serialize;
17use std::cell::Cell;
18use strop_trace::{enabled, record, EventKind};
19thread_local! { static INPUT_DEPTH: Cell<usize> = const { Cell::new(0) }; }
20
21pub struct InputScope;
22impl InputScope {
23    pub fn enter(editor: &Editor, key: Key) -> Option<Self> {
24        if !enabled() {
25            return None;
26        }
27        let depth = INPUT_DEPTH.with(|value| {
28            let depth = value.get();
29            value.set(depth + 1);
30            depth
31        });
32        #[derive(Serialize)]
33        struct Input {
34            action: &'static str,
35            source: InputSource,
36            key: Key,
37            replay: String,
38        }
39        let source = if editor.macro_depth > 0 {
40            InputSource::Macro
41        } else if depth > 0 {
42            InputSource::Synthetic
43        } else {
44            InputSource::External
45        };
46        record(
47            EventKind::Input,
48            &Input {
49                action: "key",
50                source,
51                key,
52                replay: replay_token(key),
53            },
54        );
55        Some(Self)
56    }
57}
58impl Drop for InputScope {
59    fn drop(&mut self) {
60        INPUT_DEPTH.with(|value| value.set(value.get().saturating_sub(1)));
61    }
62}
63#[derive(Serialize)]
64#[serde(rename_all = "snake_case")]
65enum InputSource {
66    External,
67    Synthetic,
68    Macro,
69}
70
71pub fn replay_token(key: Key) -> String {
72    match key {
73        Key::Char('<') => "<lt>".into(),
74        Key::Char('>') => "<gt>".into(),
75        Key::Char(c) => c.to_string(),
76        Key::Esc => "<esc>".into(),
77        Key::Enter => "<cr>".into(),
78        Key::Backspace => "<bs>".into(),
79        Key::Up => "<up>".into(),
80        Key::Down => "<down>".into(),
81        Key::Left => "<left>".into(),
82        Key::Right => "<right>".into(),
83        Key::Tab => "<tab>".into(),
84        Key::Backtab => "<s-tab>".into(),
85        Key::CtrlD => "<c-d>".into(),
86        Key::CtrlR => "<c-r>".into(),
87        Key::CtrlO => "<c-o>".into(),
88        Key::CtrlW => "<c-w>".into(),
89        Key::CtrlX => "<c-x>".into(),
90        Key::CtrlU => "<c-u>".into(),
91        Key::CtrlF => "<c-f>".into(),
92        Key::CtrlB => "<c-b>".into(),
93        Key::CtrlCaret => "<c-^>".into(),
94        Key::CtrlV => "<c-v>".into(),
95        Key::CtrlL => "<c-l>".into(),
96    }
97}