Skip to main content

ass_editor/core/incremental/
reparse.rs

1//! Full reparse and cache-update routines for [`IncrementalParser`].
2//!
3//! Implements [`IncrementalParser::full_reparse`], which rebuilds the cached
4//! script and computes a delta against the previous parse, plus the private
5//! cache update helper that keeps cached content consistent with applied edits.
6
7use super::IncrementalParser;
8use crate::core::errors::EditorError;
9use crate::core::{Range, Result};
10use ass_core::parser::{script::ScriptDeltaOwned, Script};
11
12#[cfg(not(feature = "std"))]
13use alloc::{format, string::String, string::ToString, vec::Vec};
14
15impl IncrementalParser {
16    /// Force a full reparse of the document
17    pub fn full_reparse(&mut self, content: &str) -> Result<ScriptDeltaOwned> {
18        // Parse the entire document
19        let new_script = Script::parse(content).map_err(EditorError::from)?;
20
21        // If we had a previous script, calculate delta
22        let delta = if let Some(cached_content) = &self.cached_script {
23            let old_script = Script::parse(cached_content).map_err(EditorError::from)?;
24
25            // Calculate sections that changed
26            let delta = ass_core::parser::calculate_delta(&old_script, &new_script);
27
28            // Convert to owned format
29            let mut owned_delta = ScriptDeltaOwned {
30                added: Vec::new(),
31                modified: Vec::new(),
32                removed: Vec::new(),
33                new_issues: new_script.issues().to_vec(),
34            };
35
36            // Convert added sections
37            for section in delta.added {
38                owned_delta.added.push(format!("{section:?}"));
39            }
40
41            // Convert modified sections
42            for (idx, section) in delta.modified {
43                owned_delta.modified.push((idx, format!("{section:?}")));
44            }
45
46            // Copy removed indices
47            owned_delta.removed = delta.removed;
48
49            owned_delta
50        } else {
51            // First parse - everything is "added"
52            ScriptDeltaOwned {
53                added: new_script
54                    .sections()
55                    .iter()
56                    .map(|s| format!("{s:?}"))
57                    .collect(),
58                modified: Vec::new(),
59                removed: Vec::new(),
60                new_issues: new_script.issues().to_vec(),
61            }
62        };
63
64        // Update cache
65        self.cached_script = Some(content.to_string());
66        self.pending_changes.clear();
67        self.bytes_changed = 0;
68
69        Ok(delta)
70    }
71
72    /// Update the cached script content with a change
73    pub(super) fn update_cached_script(&mut self, range: Range, new_text: &str) -> Result<()> {
74        if let Some(cached) = &mut self.cached_script {
75            // Validate boundaries
76            if range.start.offset > cached.len() || range.end.offset > cached.len() {
77                return Err(EditorError::InvalidRange {
78                    start: range.start.offset,
79                    end: range.end.offset,
80                    length: cached.len(),
81                });
82            }
83
84            // Ensure we're on valid UTF-8 boundaries
85            if !cached.is_char_boundary(range.start.offset)
86                || !cached.is_char_boundary(range.end.offset)
87            {
88                return Err(EditorError::command_failed(
89                    "Cache update range is not on valid UTF-8 character boundaries",
90                ));
91            }
92
93            // Build the new content
94            let mut result = String::with_capacity(
95                cached.len() - (range.end.offset - range.start.offset) + new_text.len(),
96            );
97
98            // Copy content before the change
99            result.push_str(&cached[..range.start.offset]);
100
101            // Insert new text
102            result.push_str(new_text);
103
104            // Copy content after the change
105            if range.end.offset < cached.len() {
106                result.push_str(&cached[range.end.offset..]);
107            }
108
109            *cached = result;
110        }
111
112        Ok(())
113    }
114}