ass_editor/commands/event_commands/
merge.rs1use super::helpers::{collect_event_lines, parse_event_line};
4use crate::commands::{CommandResult, EditorCommand};
5use crate::core::{EditorDocument, EditorError, Position, Range, Result};
6use ass_core::parser::ast::EventType;
7
8#[cfg(not(feature = "std"))]
9use alloc::{
10 format,
11 string::{String, ToString},
12 vec::Vec,
13};
14
15#[derive(Debug, Clone)]
17pub struct MergeEventsCommand {
18 pub first_event_index: usize,
19 pub second_event_index: usize,
20 pub merge_text_separator: String, pub description: Option<String>,
22}
23
24impl MergeEventsCommand {
25 pub fn new(first_event_index: usize, second_event_index: usize) -> Self {
27 Self {
28 first_event_index,
29 second_event_index,
30 merge_text_separator: " ".to_string(), description: None,
32 }
33 }
34
35 pub fn with_separator(mut self, separator: String) -> Self {
37 self.merge_text_separator = separator;
38 self
39 }
40
41 #[must_use]
43 pub fn with_description(mut self, description: String) -> Self {
44 self.description = Some(description);
45 self
46 }
47}
48
49impl EditorCommand for MergeEventsCommand {
50 fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
51 if self.first_event_index >= self.second_event_index {
52 return Err(EditorError::command_failed(
53 "First event index must be less than second event index",
54 ));
55 }
56
57 let content = document.text();
58 let event_lines = collect_event_lines(&content)?;
59 let events: Vec<_> = event_lines
60 .into_iter()
61 .filter(|event_line| {
62 event_line.index == self.first_event_index
63 || event_line.index == self.second_event_index
64 })
65 .collect();
66
67 if events.len() != 2 {
68 return Err(EditorError::command_failed(
69 "Could not find both events to merge",
70 ));
71 }
72
73 let first_event_line = events[0].line;
75 let second_event_line = events[1].line;
76
77 let first_event = parse_event_line(first_event_line)?;
78 let second_event = parse_event_line(second_event_line)?;
79
80 let merged_text = format!(
82 "{}{}{}",
83 first_event.text, self.merge_text_separator, second_event.text
84 );
85
86 let event_type_str = match first_event.event_type {
88 EventType::Dialogue => "Dialogue",
89 EventType::Comment => "Comment",
90 _ => "Dialogue", };
92 let merged_event = format!(
93 "{}: {},{},{},{},{},{},{},{},{},{}",
94 event_type_str,
95 first_event.layer,
96 first_event.start,
97 second_event.end,
98 first_event.style,
99 first_event.name,
100 first_event.margin_l,
101 first_event.margin_r,
102 first_event.margin_v,
103 first_event.effect,
104 merged_text
105 );
106
107 let first_start = events[0].start;
109 let second_end = events[1].end_with_newline;
110 let range = Range::new(Position::new(first_start), Position::new(second_end));
111 let replacement = format!("{merged_event}\n");
112
113 document.replace(range, &replacement)?;
114
115 let end_pos = Position::new(first_start + replacement.len());
116 Ok(CommandResult::success_with_change(
117 Range::new(Position::new(first_start), end_pos),
118 end_pos,
119 )
120 .with_message(format!(
121 "Merged events {} and {}",
122 self.first_event_index, self.second_event_index
123 )))
124 }
125
126 fn description(&self) -> &str {
127 self.description.as_deref().unwrap_or("Merge events")
128 }
129
130 fn memory_usage(&self) -> usize {
131 core::mem::size_of::<Self>()
132 + self.merge_text_separator.len()
133 + self.description.as_ref().map_or(0, |d| d.len())
134 }
135}