ass_editor/core/incremental/
apply.rs1use super::{DocumentChange, IncrementalParser};
8use crate::core::errors::EditorError;
9use crate::core::{Range, Result};
10use ass_core::parser::{script::ScriptDeltaOwned, Script};
11
12#[cfg(feature = "std")]
13use std::borrow::Cow;
14
15#[cfg(not(feature = "std"))]
16use alloc::{borrow::Cow, string::ToString};
17
18impl IncrementalParser {
19 pub fn apply_change(
21 &mut self,
22 document_text: &str,
23 range: Range,
24 new_text: &str,
25 ) -> Result<ScriptDeltaOwned> {
26 if self.cached_script.is_none() || self.bytes_changed >= self.reparse_threshold {
28 return self.full_reparse(document_text);
29 }
30
31 if range.end.offset > document_text.len() || range.start.offset > range.end.offset {
33 return Err(EditorError::InvalidRange {
34 start: range.start.offset,
35 end: range.end.offset,
36 length: document_text.len(),
37 });
38 }
39
40 let start_is_valid = range.start.offset == 0
42 || range.start.offset == document_text.len()
43 || document_text.is_char_boundary(range.start.offset);
44 let end_is_valid = range.end.offset == 0
45 || range.end.offset == document_text.len()
46 || document_text.is_char_boundary(range.end.offset);
47
48 if !start_is_valid || !end_is_valid {
49 return Err(EditorError::command_failed(
52 "Edit range is not on valid UTF-8 character boundaries",
53 ));
54 }
55
56 let old_text = &document_text[range.start.offset..range.end.offset];
58 let (start_byte, end_byte) = (range.start.offset, range.end.offset);
59
60 let change = DocumentChange {
62 range,
63 new_text: Cow::Owned(new_text.to_string()),
64 old_text: Cow::Owned(old_text.to_string()),
65 #[cfg(feature = "std")]
66 timestamp: std::time::Instant::now(),
67 change_id: self.next_change_id,
68 };
69 self.next_change_id += 1;
70
71 let change_size = new_text.len().abs_diff(old_text.len());
73 self.bytes_changed += change_size;
74
75 self.pending_changes.push(change);
77
78 let byte_range = start_byte..end_byte;
81
82 let cached = self.cached_script.as_ref().ok_or_else(|| {
84 EditorError::command_failed("Cached script unavailable for incremental parsing")
85 })?;
86 let script = Script::parse(cached).map_err(EditorError::from)?;
87
88 match script.parse_partial(byte_range, new_text) {
90 Ok(delta) => {
91 self.update_cached_script(range, new_text)?;
93 Ok(delta)
94 }
95 Err(_e) => {
96 self.pending_changes.pop(); self.bytes_changed -= change_size;
99
100 #[cfg(feature = "std")]
102 eprintln!("Incremental parse failed, falling back to full parse: {_e}");
103
104 self.full_reparse(document_text)
105 }
106 }
107 }
108}