ass_editor/core/document/validation.rs
1//! Script parsing entry points, command execution, and validation
2//!
3//! Hosts the `parse_script_with` callback bridge, the history-recording
4//! `execute_command`, and the lazy-validator integration methods.
5
6use super::EditorDocument;
7use crate::core::errors::{EditorError, Result};
8use crate::core::position::{Position, Range};
9use ass_core::parser::Script;
10
11#[cfg(not(feature = "std"))]
12use alloc::string::{String, ToString};
13
14impl EditorDocument {
15 /// Parse the current document content into a Script
16 ///
17 /// Returns a boxed closure that provides the parsed Script.
18 /// This avoids lifetime issues by ensuring the content outlives the Script.
19 pub fn parse_script_with<F, R>(&self, f: F) -> Result<R>
20 where
21 F: FnOnce(&Script) -> R,
22 {
23 let content = self.text();
24 match Script::parse(&content) {
25 Ok(script) => Ok(f(&script)),
26 Err(e) => Err(EditorError::from(e)),
27 }
28 }
29
30 /// Validate the document content can be parsed as valid ASS
31 /// This is the basic validation that just checks parsing
32 pub fn validate(&self) -> Result<()> {
33 let content = self.text();
34 Script::parse(&content).map_err(EditorError::from)?;
35 Ok(())
36 }
37
38 /// Execute a command with proper history recording
39 ///
40 /// This method ensures that commands are properly recorded in the undo history.
41 /// Use this instead of calling command.execute() directly if you want undo support.
42 ///
43 /// For BatchCommand, this creates a synthetic undo operation that captures
44 /// the aggregate effect of all sub-commands.
45 pub fn execute_command(
46 &mut self,
47 command: &dyn crate::commands::EditorCommand,
48 ) -> Result<crate::commands::CommandResult> {
49 use crate::core::history::Operation;
50
51 // Get the cursor position before
52 let _cursor_before = self.cursor_position();
53
54 // Execute the command
55 let result = command.execute(self)?;
56
57 // Only record if the command changed content
58 if result.content_changed {
59 // Determine the operation based on the result
60 let operation = if let Some(range) = result.modified_range {
61 if range.is_empty() {
62 // This was an insertion
63 let inserted_text = self.text_range(Range::new(
64 range.start,
65 Position::new(
66 range.start.offset
67 + result
68 .new_cursor
69 .map_or(0, |c| c.offset - range.start.offset),
70 ),
71 ))?;
72 Operation::Insert {
73 position: range.start,
74 text: inserted_text,
75 }
76 } else {
77 // For batch commands and complex operations, we need to be more careful
78 // Store enough information to properly undo
79 // This is a simplified approach - ideally each command would handle its own undo
80 let cmd_desc = command.description();
81 if cmd_desc.contains("batch") || cmd_desc.contains("Multiple") {
82 // For batch operations, we don't have enough info
83 // Just use a simple insert operation
84 Operation::Insert {
85 position: range.start,
86 text: String::new(),
87 }
88 } else {
89 // For simple operations, use the range info
90 Operation::Replace {
91 range,
92 old_text: String::new(), // We don't have the old text
93 new_text: self.text_range(range).unwrap_or_default(),
94 }
95 }
96 }
97 } else {
98 // No range info - create a generic operation
99 Operation::Insert {
100 position: Position::new(0),
101 text: String::new(),
102 }
103 };
104
105 // Update cursor if needed
106 if let Some(new_cursor) = result.new_cursor {
107 self.set_cursor_position(Some(new_cursor));
108 }
109
110 // Record in history
111 self.history
112 .record_operation(operation, command.description().to_string(), &result);
113
114 // Clear validation cache since content changed
115 self.validator.clear_cache();
116 }
117
118 Ok(result)
119 }
120
121 /// Perform comprehensive validation using the LazyValidator
122 /// Returns detailed validation results including warnings and suggestions
123 ///
124 /// Note: Returns a cloned result to avoid borrow checker issues
125 pub fn validate_comprehensive(&mut self) -> Result<crate::utils::validator::ValidationResult> {
126 // Create a temporary document reference for validation
127 // This is needed because we can't pass &self while mutably borrowing validator
128 let temp_doc = EditorDocument::from_content(&self.text())?;
129 let result = self.validator.validate(&temp_doc)?;
130 Ok(result.clone())
131 }
132
133 /// Force revalidation even if cached results exist
134 pub fn force_validate(&mut self) -> Result<crate::utils::validator::ValidationResult> {
135 // Create a temporary document reference for validation
136 let temp_doc = EditorDocument::from_content(&self.text())?;
137 let result = self.validator.force_validate(&temp_doc)?;
138 Ok(result.clone())
139 }
140
141 /// Check if document is valid (quick check using cache if available)
142 pub fn is_valid_cached(&mut self) -> Result<bool> {
143 // Create a temporary document reference for validation
144 let temp_doc = EditorDocument::from_content(&self.text())?;
145 self.validator.is_valid(&temp_doc)
146 }
147
148 /// Get cached validation result without revalidating
149 pub fn validation_result(&self) -> Option<&crate::utils::validator::ValidationResult> {
150 self.validator.cached_result()
151 }
152
153 /// Configure the validator
154 pub fn set_validator_config(&mut self, config: crate::utils::validator::ValidatorConfig) {
155 self.validator.set_config(config);
156 }
157
158 /// Get mutable access to the validator
159 pub fn validator_mut(&mut self) -> &mut crate::utils::validator::LazyValidator {
160 &mut self.validator
161 }
162}