Skip to main content

strop_core/
diagnostics.rs

1//! Mutation diagnostics at the mechanics boundary, including open undo groups.
2use crate::Buffer;
3use serde::Serialize;
4use std::sync::atomic::{AtomicU64, Ordering};
5use strop_trace::{capture_content, enabled, record, EventKind};
6
7static NEXT_BUFFER: AtomicU64 = AtomicU64::new(1);
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
10#[serde(transparent)]
11pub struct BufferTraceId(u64);
12impl BufferTraceId {
13    pub(crate) fn next() -> Self {
14        Self(NEXT_BUFFER.fetch_add(1, Ordering::Relaxed))
15    }
16}
17
18#[derive(Serialize)]
19struct Mutation<'a> {
20    buffer: BufferTraceId,
21    source: crate::ChangeOrigin,
22    revision: u64,
23    start_byte: usize,
24    removed_bytes: usize,
25    inserted_bytes: usize,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    inserted_text: Option<&'a str>,
28}
29
30impl Buffer {
31    pub fn trace_id(&self) -> BufferTraceId {
32        self.trace_identity
33    }
34
35    pub(crate) fn trace_edit(
36        &self,
37        source: crate::ChangeOrigin,
38        start_byte: usize,
39        removed_bytes: usize,
40        inserted: &str,
41    ) {
42        if !enabled() {
43            return;
44        }
45        record(
46            EventKind::Mutation,
47            &Mutation {
48                buffer: self.trace_id(),
49                source,
50                revision: self.revision().get(),
51                start_byte,
52                removed_bytes,
53                inserted_bytes: inserted.len(),
54                inserted_text: capture_content().then_some(inserted),
55            },
56        );
57    }
58
59    pub(crate) fn trace_snapshot(&self, removed_bytes: usize) {
60        if !enabled() {
61            return;
62        }
63        let text = capture_content().then(|| self.text().to_string());
64        record(
65            EventKind::Mutation,
66            &Mutation {
67                buffer: self.trace_id(),
68                source: crate::ChangeOrigin::System,
69                revision: self.revision().get(),
70                start_byte: 0,
71                removed_bytes,
72                inserted_bytes: self.len_bytes(),
73                inserted_text: text.as_deref(),
74            },
75        );
76    }
77
78    pub(crate) fn trace_history(&self, edits: &[crate::history::Edit]) {
79        if !enabled() {
80            return;
81        }
82        #[derive(Serialize)]
83        struct HistoryEdit<'a> {
84            start_byte: usize,
85            bytes: usize,
86            kind: crate::history::EditKind,
87            #[serde(skip_serializing_if = "Option::is_none")]
88            text: Option<&'a str>,
89        }
90        #[derive(Serialize)]
91        struct AppliedHistory<'a> {
92            buffer: BufferTraceId,
93            revision: u64,
94            edits: Vec<HistoryEdit<'a>>,
95        }
96        record(
97            EventKind::History,
98            &AppliedHistory {
99                buffer: self.trace_id(),
100                revision: self.revision().get(),
101                edits: edits
102                    .iter()
103                    .map(|edit| HistoryEdit {
104                        start_byte: edit.at,
105                        bytes: edit.text.len(),
106                        kind: edit.kind,
107                        text: capture_content().then_some(edit.text.as_str()),
108                    })
109                    .collect(),
110            },
111        );
112    }
113}