ass_editor/commands/event_commands/
effect_exec.rs1use super::effect::{EffectOperation, EventEffectCommand};
4use super::helpers::{collect_event_lines, parse_event_line};
5use crate::commands::{CommandResult, EditorCommand};
6use crate::core::{EditorDocument, Position, Range, Result};
7use ass_core::parser::ast::EventType;
8
9#[cfg(not(feature = "std"))]
10use alloc::{
11 format,
12 string::{String, ToString},
13 vec::Vec,
14};
15
16impl EditorCommand for EventEffectCommand {
17 fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
18 let content = document.text();
19 let event_lines = collect_event_lines(&content)?;
20 let mut replacements = Vec::new();
21 let mut total_range: Option<Range> = None;
22
23 for event_line in event_lines {
24 let should_modify =
25 self.event_indices.is_empty() || self.event_indices.contains(&event_line.index);
26
27 if !should_modify {
28 continue;
29 }
30
31 if let Ok(event) = parse_event_line(event_line.line) {
32 let new_effect = match self.operation {
33 EffectOperation::Set => self.effect.clone(),
34 EffectOperation::Clear => String::new(),
35 EffectOperation::Append => {
36 if event.effect.is_empty() {
37 self.effect.clone()
38 } else {
39 format!("{} {}", event.effect, self.effect)
40 }
41 }
42 EffectOperation::Prepend => {
43 if event.effect.is_empty() {
44 self.effect.clone()
45 } else {
46 format!("{} {}", self.effect, event.effect)
47 }
48 }
49 };
50
51 let event_type_str = match event.event_type {
52 EventType::Dialogue => "Dialogue",
53 EventType::Comment => "Comment",
54 _ => "Dialogue",
55 };
56 let new_line = format!(
57 "{}: {},{},{},{},{},{},{},{},{},{}",
58 event_type_str,
59 event.layer,
60 event.start,
61 event.end,
62 event.style,
63 event.name,
64 event.margin_l,
65 event.margin_r,
66 event.margin_v,
67 new_effect,
68 event.text
69 );
70
71 let change_range = Range::new(
72 Position::new(event_line.start),
73 Position::new(event_line.start + new_line.len()),
74 );
75 total_range = Some(match total_range {
76 Some(existing) => existing.union(&change_range),
77 None => change_range,
78 });
79 replacements.push((event_line.start, event_line.end, new_line));
80 }
81 }
82
83 for (start, end, new_line) in replacements.iter().rev() {
84 let range = Range::new(Position::new(*start), Position::new(*end));
85 document.replace(range, new_line)?;
86 }
87
88 let changes_made = replacements.len();
89
90 if changes_made > 0 {
91 let operation_name = match self.operation {
92 EffectOperation::Set => "set",
93 EffectOperation::Clear => "cleared",
94 EffectOperation::Append => "appended",
95 EffectOperation::Prepend => "prepended",
96 };
97
98 Ok(CommandResult::success_with_change(
99 total_range.unwrap_or(Range::new(Position::new(0), Position::new(0))),
100 Position::new(document.len_bytes()),
101 )
102 .with_message(format!("Effect {operation_name} for {changes_made} events")))
103 } else {
104 Ok(CommandResult::success().with_message("No events were modified".to_string()))
105 }
106 }
107
108 fn description(&self) -> &str {
109 self.description.as_deref().unwrap_or("Modify event effect")
110 }
111
112 fn memory_usage(&self) -> usize {
113 core::mem::size_of::<Self>()
114 + self.event_indices.len() * core::mem::size_of::<usize>()
115 + self.effect.len()
116 + self.description.as_ref().map_or(0, |d| d.len())
117 }
118}