Skip to main content

reovim_kernel/api/
debug.rs

1//! Debug snapshot types and functions.
2//!
3//! This module provides types and functions for extracting debug-oriented
4//! snapshots of kernel state. These are "mechanism" functions that extract
5//! data - the policy of what to do with them is handled by runner handlers.
6//!
7//! # Design Philosophy
8//!
9//! - **No serde**: Snapshot types are plain Rust structs. Serialization is
10//!   handled by the protocol crate.
11//! - **Pure extraction**: These functions only read state, never modify it.
12//! - **Clean boundary**: Snapshot types don't expose internal implementation
13//!   details like `HashMap` structures.
14
15use crate::{
16    core::{MarkBank, ModeStack, RegisterBank, SpecialMark, YankType},
17    mm::{BufferId, Position},
18};
19
20use super::context::KernelContext;
21
22// ============================================================================
23// Snapshot Types
24// ============================================================================
25
26/// Snapshot of kernel state for debugging.
27#[derive(Debug, Clone)]
28pub struct KernelStateSnapshot {
29    /// Number of buffers currently loaded.
30    pub buffer_count: usize,
31    /// List of all buffer IDs.
32    pub buffer_ids: Vec<BufferId>,
33    /// Number of event handlers registered.
34    pub event_handlers: usize,
35    /// Number of events in the queue.
36    pub event_queue_len: usize,
37}
38
39/// Yank type for snapshot (mirrors `YankType` but decoupled).
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum YankTypeSnapshot {
42    /// Characterwise yank.
43    Characterwise,
44    /// Linewise yank.
45    Linewise,
46}
47
48impl From<YankType> for YankTypeSnapshot {
49    fn from(yt: YankType) -> Self {
50        match yt {
51            YankType::Characterwise => Self::Characterwise,
52            YankType::Linewise => Self::Linewise,
53        }
54    }
55}
56
57/// Snapshot of a single register.
58#[derive(Debug, Clone)]
59pub struct RegisterSnapshot {
60    /// Register name ('"' for unnamed, 'a'-'z' for named).
61    pub name: char,
62    /// Register text content.
63    pub text: String,
64    /// Yank type.
65    pub yank_type: YankTypeSnapshot,
66}
67
68/// Snapshot of all registers.
69#[derive(Debug, Clone)]
70pub struct RegistersSnapshot {
71    /// Unnamed register (").
72    pub unnamed: RegisterSnapshot,
73    /// Named registers (a-z), only non-empty ones.
74    pub named: Vec<RegisterSnapshot>,
75}
76
77/// Snapshot of a single mark.
78#[derive(Debug, Clone)]
79pub struct MarkSnapshot {
80    /// Mark name ('a'-'z' for local, 'A'-'Z' for global, special chars for special marks).
81    pub name: String,
82    /// Mark position.
83    pub position: Position,
84    /// Buffer ID for global marks.
85    pub buffer_id: Option<BufferId>,
86}
87
88/// Snapshot of all marks.
89#[derive(Debug, Clone)]
90pub struct MarksSnapshot {
91    /// Local marks (a-z) for current buffer.
92    pub local: Vec<MarkSnapshot>,
93    /// Global marks (A-Z) across all buffers.
94    pub global: Vec<MarkSnapshot>,
95    /// Special marks ('.', '^', etc.).
96    pub special: Vec<MarkSnapshot>,
97}
98
99/// Snapshot of mode stack.
100#[derive(Debug, Clone)]
101pub struct ModeStackSnapshot {
102    /// Current active mode display string.
103    pub current: String,
104    /// Full mode stack (bottom to top).
105    pub stack: Vec<String>,
106    /// Stack depth.
107    pub depth: usize,
108}
109
110// ============================================================================
111// Snapshot Functions
112// ============================================================================
113
114/// Create a kernel state snapshot from `KernelContext`.
115#[must_use]
116pub fn snapshot_kernel_state(ctx: &KernelContext) -> KernelStateSnapshot {
117    let buffer_ids = ctx.buffers.list();
118    let buffer_count = buffer_ids.len();
119
120    KernelStateSnapshot {
121        buffer_count,
122        buffer_ids,
123        event_handlers: ctx.event_bus.total_handler_count(),
124        event_queue_len: ctx.event_bus.queue_len(),
125    }
126}
127
128/// Create a registers snapshot from `RegisterBank`.
129#[must_use]
130#[cfg_attr(coverage_nightly, coverage(off))]
131pub fn snapshot_registers(bank: &RegisterBank) -> RegistersSnapshot {
132    // Get unnamed register
133    let unnamed_content = bank.get();
134    let unnamed = RegisterSnapshot {
135        name: '"',
136        text: unnamed_content.text.clone(),
137        yank_type: unnamed_content.yank_type.into(),
138    };
139
140    // Get non-empty named registers
141    let mut named = Vec::new();
142    for c in 'a'..='z' {
143        if let Some(content) = bank.get_named(c)
144            && !content.text.is_empty()
145        {
146            named.push(RegisterSnapshot {
147                name: c,
148                text: content.text.clone(),
149                yank_type: content.yank_type.into(),
150            });
151        }
152    }
153
154    RegistersSnapshot { unnamed, named }
155}
156
157/// Create a marks snapshot from `MarkBank`.
158#[must_use]
159pub fn snapshot_marks(bank: &MarkBank) -> MarksSnapshot {
160    // Get local marks
161    let local: Vec<MarkSnapshot> = bank
162        .list_local()
163        .into_iter()
164        .map(|(name, pos)| MarkSnapshot {
165            name: name.to_string(),
166            position: pos,
167            buffer_id: None,
168        })
169        .collect();
170
171    // Get global marks
172    let global: Vec<MarkSnapshot> = bank
173        .list_global()
174        .into_iter()
175        .map(|(name, mark)| MarkSnapshot {
176            name: name.to_string(),
177            position: mark.position,
178            buffer_id: Some(mark.buffer_id),
179        })
180        .collect();
181
182    // Get special marks
183    let special_marks = [
184        (SpecialMark::LastJump, "'"),
185        (SpecialMark::LastEdit, "."),
186        (SpecialMark::LastInsert, "^"),
187        (SpecialMark::VisualStart, "<"),
188        (SpecialMark::VisualEnd, ">"),
189        (SpecialMark::LastExitInsert, "]"),
190    ];
191
192    let special: Vec<MarkSnapshot> = special_marks
193        .iter()
194        .filter_map(|(mark_type, name)| {
195            bank.get_special(*mark_type).map(|mark| MarkSnapshot {
196                name: (*name).to_string(),
197                position: mark.position,
198                buffer_id: Some(mark.buffer_id),
199            })
200        })
201        .collect();
202
203    MarksSnapshot {
204        local,
205        global,
206        special,
207    }
208}
209
210/// Create a mode stack snapshot from `ModeStack`.
211#[must_use]
212pub fn snapshot_mode_stack(stack: &ModeStack) -> ModeStackSnapshot {
213    let current = stack.current().to_string();
214    let depth = stack.depth();
215
216    // Get all modes in stack
217    let stack_vec: Vec<String> = stack.as_slice().iter().map(ToString::to_string).collect();
218
219    ModeStackSnapshot {
220        current,
221        stack: stack_vec,
222        depth,
223    }
224}