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}