Skip to main content

ass_editor/core/document/
incremental_parse.rs

1//! Safe edit fallback and delta-tracking parse helpers
2//!
3//! `edit_safe` always succeeds by falling back to a plain replace, while the
4//! stream-gated helpers expose incremental event editing and delta-aware
5//! parsing for command-system integration.
6
7use super::EditorDocument;
8use crate::core::errors::Result;
9use crate::core::position::Range;
10
11#[cfg(feature = "stream")]
12use crate::core::errors::EditorError;
13#[cfg(feature = "stream")]
14use crate::core::position::Position;
15#[cfg(feature = "stream")]
16use ass_core::parser::script::ScriptDeltaOwned;
17#[cfg(feature = "stream")]
18use ass_core::parser::Script;
19#[cfg(feature = "stream")]
20use core::ops::Range as StdRange;
21
22#[cfg(all(feature = "stream", not(feature = "std")))]
23use alloc::format;
24
25impl EditorDocument {
26    /// Safe edit with automatic fallback to regular replace on error
27    ///
28    /// This method tries incremental parsing first for performance,
29    /// but falls back to regular replace if incremental parsing is unavailable
30    /// or fails. This ensures edits always succeed.
31    pub fn edit_safe(&mut self, range: Range, new_text: &str) -> Result<()> {
32        #[cfg(feature = "stream")]
33        {
34            // Try incremental parsing first
35            match self.edit_incremental(range, new_text) {
36                Ok(_) => return Ok(()),
37                Err(_e) => {
38                    #[cfg(feature = "std")]
39                    eprintln!("Incremental edit failed, falling back to regular replace: {_e}");
40                }
41            }
42        }
43
44        // Fallback to regular replace
45        self.replace(range, new_text)
46    }
47
48    /// Edit event using incremental parsing for performance
49    #[cfg(feature = "stream")]
50    pub fn edit_event_incremental(
51        &mut self,
52        event_text: &str,
53        new_text: &str,
54    ) -> Result<ScriptDeltaOwned> {
55        let content = self.text();
56        if let Some(pos) = content.find(event_text) {
57            let range = Range::new(Position::new(pos), Position::new(pos + event_text.len()));
58            self.edit_incremental(range, new_text)
59        } else {
60            Err(EditorError::ValidationError {
61                message: format!("Event text not found: {event_text}"),
62            })
63        }
64    }
65
66    /// Parse with delta tracking for command system integration
67    #[cfg(feature = "stream")]
68    pub fn parse_with_delta_tracking<F, R>(
69        &self,
70        range: Option<StdRange<usize>>,
71        new_text: Option<&str>,
72        f: F,
73    ) -> Result<R>
74    where
75        F: FnOnce(&Script, Option<&ScriptDeltaOwned>) -> R,
76    {
77        let content = self.text();
78        let script = Script::parse(&content).map_err(EditorError::from)?;
79
80        if let (Some(range), Some(text)) = (range, new_text) {
81            // Get delta for the change
82            match script.parse_partial(range, text) {
83                Ok(delta) => Ok(f(&script, Some(&delta))),
84                Err(_) => {
85                    // Fallback to full re-parse if incremental fails
86                    Ok(f(&script, None))
87                }
88            }
89        } else {
90            Ok(f(&script, None))
91        }
92    }
93}