1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Index-based structured event editing
//!
//! Implements `edit_event_by_index`, which locates an event in the raw text,
//! applies field-level modifications, and rewrites the dialogue/comment line
//! via the `build_modified_event_line_from_data` helper.
use super::EditorDocument;
use crate::core::errors::{EditorError, Result};
use crate::core::position::{Position, Range};
use ass_core::parser::ast::Section;
#[cfg(not(feature = "std"))]
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
impl EditorDocument {
/// Edit event by index with full field support
///
/// Allows structured editing of specific event fields by index.
/// Returns the modified event line for undo support.
///
/// # Arguments
///
/// * `index` - Zero-based index of the event to edit
/// * `update_fn` - Function that receives the current event and returns modifications
///
/// # Example
///
/// ```rust
/// # use ass_editor::core::EditorDocument;
/// # let content = r#"[Script Info]
/// # Title: Test
/// #
/// # [Events]
/// # Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
/// # Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Original text"#;
/// # let mut doc = EditorDocument::from_content(content).unwrap();
/// doc.edit_event_by_index(0, |event| {
/// vec![
/// ("text", "New dialogue text".to_string()),
/// ("style", "NewStyle".to_string()),
/// ]
/// })?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn edit_event_by_index<F>(&mut self, index: usize, update_fn: F) -> Result<String>
where
F: for<'a> FnOnce(&ass_core::parser::ast::Event<'a>) -> Vec<(&'static str, String)>,
{
let content = self.text();
let mut event_info = None;
let mut event_count = 0;
// Find the event and its location in the document
self.parse_script_with(|script| -> Result<()> {
for section in script.sections() {
if let Section::Events(events) = section {
for event in events {
if event_count == index {
// Get the modifications from the update function
let modifications = update_fn(event);
// Build a pattern to search for this specific event
let event_type_str = event.event_type.as_str();
let pattern = format!(
"{}: {},{},{}",
event_type_str, event.layer, event.start, event.end
);
let event_line = if let Some(pos) = content.find(&pattern) {
let line_end = content[pos..]
.find('\n')
.map(|n| pos + n)
.unwrap_or(content.len());
let line = content[pos..line_end].to_string();
(pos, line_end, line)
} else {
return Err(EditorError::ValidationError {
message: "Could not find event line in document".to_string(),
});
};
// Store the event data we need instead of cloning
let event_data = (
event.event_type,
event.layer.to_string(),
event.start.to_string(),
event.end.to_string(),
event.style.to_string(),
event.name.to_string(),
event.margin_l.to_string(),
event.margin_r.to_string(),
event.margin_v.to_string(),
event.effect.to_string(),
event.text.to_string(),
);
event_info = Some((event_data, event_line, modifications));
return Ok(());
}
event_count += 1;
}
}
}
Ok(())
})??;
if let Some((event_data, (line_start, line_end, original_line), modifications)) = event_info
{
// Build the new event line with modifications
let new_line = self.build_modified_event_line_from_data(
event_data,
&original_line,
modifications,
)?;
// Replace the line in the document
let range = Range::new(Position::new(line_start), Position::new(line_end));
self.replace(range, &new_line)?;
Ok(new_line)
} else {
Err(EditorError::InvalidRange {
start: index,
end: index + 1,
length: event_count,
})
}
}
}