Skip to main content

ass_editor/commands/event_commands/
toggle.rs

1//! Command to toggle event type between Dialogue and Comment.
2
3use super::helpers::collect_event_lines;
4use crate::commands::{CommandResult, EditorCommand};
5use crate::core::{EditorDocument, Position, Range, Result};
6
7#[cfg(not(feature = "std"))]
8use alloc::{
9    format,
10    string::{String, ToString},
11    vec,
12    vec::Vec,
13};
14
15/// Command to toggle event type between Dialogue and Comment
16#[derive(Debug, Clone)]
17pub struct ToggleEventTypeCommand {
18    pub event_indices: Vec<usize>,
19    pub description: Option<String>,
20}
21
22impl ToggleEventTypeCommand {
23    /// Create a new event type toggle command
24    pub fn new(event_indices: Vec<usize>) -> Self {
25        Self {
26            event_indices,
27            description: None,
28        }
29    }
30
31    /// Toggle a single event
32    pub fn single(event_index: usize) -> Self {
33        Self::new(vec![event_index])
34    }
35
36    /// Toggle all events
37    pub fn all() -> Self {
38        Self::new(Vec::new()) // Empty means all events
39    }
40
41    /// Set a custom description for this command
42    #[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 ToggleEventTypeCommand {
50    fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
51        let content = document.text();
52        let event_lines = collect_event_lines(&content)?;
53        let mut replacements = Vec::new();
54        let mut total_range: Option<Range> = None;
55
56        for event_line in event_lines {
57            let should_toggle =
58                self.event_indices.is_empty() || self.event_indices.contains(&event_line.index);
59
60            if !should_toggle {
61                continue;
62            }
63
64            let new_line = if event_line.line.starts_with("Dialogue:") {
65                event_line.line.replacen("Dialogue:", "Comment:", 1)
66            } else {
67                event_line.line.replacen("Comment:", "Dialogue:", 1)
68            };
69
70            let change_range = Range::new(
71                Position::new(event_line.start),
72                Position::new(event_line.start + new_line.len()),
73            );
74            total_range = Some(match total_range {
75                Some(existing) => existing.union(&change_range),
76                None => change_range,
77            });
78            replacements.push((event_line.start, event_line.end, new_line));
79        }
80
81        for (start, end, new_line) in replacements.iter().rev() {
82            let range = Range::new(Position::new(*start), Position::new(*end));
83            document.replace(range, new_line)?;
84        }
85
86        let changes_made = replacements.len();
87
88        if changes_made > 0 {
89            Ok(CommandResult::success_with_change(
90                total_range.unwrap_or(Range::new(Position::new(0), Position::new(0))),
91                Position::new(document.len_bytes()),
92            )
93            .with_message(format!("Toggled type for {changes_made} events")))
94        } else {
95            Ok(CommandResult::success().with_message("No events were toggled".to_string()))
96        }
97    }
98
99    fn description(&self) -> &str {
100        self.description.as_deref().unwrap_or("Toggle event type")
101    }
102
103    fn memory_usage(&self) -> usize {
104        core::mem::size_of::<Self>()
105            + self.event_indices.len() * core::mem::size_of::<usize>()
106            + self.description.as_ref().map_or(0, |d| d.len())
107    }
108}