Skip to main content

text_document/
events.rs

1//! Document event types and subscription handle.
2
3use std::sync::Arc;
4use std::sync::atomic::AtomicBool;
5
6use crate::inner::{CallbackEntry, TextDocumentInner};
7
8/// Events emitted by a [`TextDocument`](crate::TextDocument).
9///
10/// Subscribe via [`TextDocument::on_change`](crate::TextDocument::on_change) (callback-based)
11/// or poll via [`TextDocument::poll_events`](crate::TextDocument::poll_events) (frame-loop).
12///
13/// These events carry enough information for a UI to do incremental updates —
14/// repaint only the affected region, not the entire document.
15#[derive(Debug, Clone)]
16pub enum DocumentEvent {
17    /// Text content changed at a specific region.
18    ///
19    /// Emitted by: `insert_text`, `delete_char`, `delete_previous_char`,
20    /// `remove_selected_text`, `insert_formatted_text`, `insert_block`,
21    /// `insert_html`, `insert_markdown`, `insert_fragment`, `insert_image`.
22    ContentsChanged {
23        position: usize,
24        chars_removed: usize,
25        chars_added: usize,
26        blocks_affected: usize,
27    },
28
29    /// Formatting changed without text content change.
30    FormatChanged { position: usize, length: usize },
31
32    /// Block count changed. Carries the new count.
33    BlockCountChanged(usize),
34
35    /// The document was completely replaced (import, clear).
36    DocumentReset,
37
38    /// Undo/redo was performed or availability changed.
39    UndoRedoChanged { can_undo: bool, can_redo: bool },
40
41    /// The modified flag changed.
42    ModificationChanged(bool),
43
44    /// A long operation progressed.
45    LongOperationProgress {
46        operation_id: String,
47        percent: f64,
48        message: String,
49    },
50
51    /// A long operation completed or failed.
52    LongOperationFinished {
53        operation_id: String,
54        success: bool,
55        error: Option<String>,
56    },
57}
58
59/// Handle to a document event subscription.
60///
61/// Events are delivered as long as this handle is alive.
62/// Drop it to unsubscribe. No explicit unsubscribe method needed.
63pub struct Subscription {
64    alive: Arc<AtomicBool>,
65}
66
67impl Drop for Subscription {
68    fn drop(&mut self) {
69        self.alive
70            .store(false, std::sync::atomic::Ordering::Relaxed);
71    }
72}
73
74/// Register a callback with the document inner, returning a Subscription handle.
75pub(crate) fn subscribe_inner<F>(inner: &mut TextDocumentInner, callback: F) -> Subscription
76where
77    F: Fn(DocumentEvent) + Send + Sync + 'static,
78{
79    let alive = Arc::new(AtomicBool::new(true));
80    inner.callbacks.push(CallbackEntry {
81        alive: Arc::downgrade(&alive),
82        callback: Arc::new(callback),
83    });
84    Subscription { alive }
85}