tui-canvas 0.7.5

Form/textarea for TUI
Documentation
use crate::{
    canvas::state::SelectionState,
    textarea::{TextAreaDataProvider, TextAreaState},
};

impl<P: TextAreaDataProvider> TextAreaState<P> {
    pub(crate) fn delete_selection_once(&mut self, yank: bool) -> bool {
        match self.selection_state().clone() {
            SelectionState::Linewise { anchor_field } => {
                let current = self.current_field();
                let start = anchor_field.min(current);
                let end = anchor_field.max(current);
                let lines = self.editor.data_provider().capture_content();
                if start >= lines.len() {
                    return false;
                }

                if yank {
                    self.editor.behavior_state.yank_mut().set_line_register(
                        lines[start..=end.min(lines.len() - 1)]
                            .to_vec(),
                    );
                }

                self.editor
                    .record_checkpoint(crate::editor::features::history::EditKind::Delete);

                let mut content = lines;
                if content.len() <= 1 {
                    content = vec![String::new()];
                } else {
                    content.drain(start..=end.min(content.len() - 1));
                    if content.is_empty() {
                        content.push(String::new());
                    }
                }
                self.editor.data_provider_mut().restore_content(&content);
                let target = start.min(content.len().saturating_sub(1));
                let _ = self.transition_to_field(target);
                self.move_line_start();
                #[cfg(feature = "gui")]
                {
                    self.edited_this_frame = true;
                }
                true
            }
            SelectionState::Characterwise { anchor } => {
                let cursor = (self.current_field(), self.cursor_position());
                if anchor == cursor {
                    return self.delete_primary_character(yank);
                }

                let start = anchor.min(cursor);
                let end = anchor.max(cursor);
                let lines = self.editor.data_provider().capture_content();
                if start.0 >= lines.len() || end.0 >= lines.len() {
                    return false;
                }

                if yank {
                    let yanked = self.extract_characterwise_text(&lines, start, end);
                    self.editor
                        .behavior_state
                        .yank_mut()
                        .set_text_register(yanked);
                }

                self.editor
                    .record_checkpoint(crate::editor::features::history::EditKind::Delete);

                let mut content = lines;
                if start.0 == end.0 {
                    let line = &content[start.0];
                    let new_line: String = line
                        .chars()
                        .enumerate()
                        .filter_map(|(idx, ch)| {
                            if idx < start.1 || idx > end.1 {
                                Some(ch)
                            } else {
                                None
                            }
                        })
                        .collect();
                    content[start.0] = new_line;
                } else {
                    let first: String = content[start.0]
                        .chars()
                        .take(start.1)
                        .collect();
                    let last: String = content[end.0].chars().skip(end.1 + 1).collect();
                    content[start.0] = format!("{first}{last}");
                    if end.0 > start.0 {
                        content.drain(start.0 + 1..=end.0);
                    }
                }

                self.editor.data_provider_mut().restore_content(&content);
                let _ = self.transition_to_field(start.0);
                self.set_cursor_position(start.1);
                #[cfg(feature = "gui")]
                {
                    self.edited_this_frame = true;
                }
                true
            }
            SelectionState::None => self.delete_primary_character(yank),
        }
    }

    pub(crate) fn delete_primary_character(&mut self, yank: bool) -> bool {
        let line_idx = self.current_field();
        let col = self.cursor_position();
        let current = self.current_text().to_string();
        let line_len = current.chars().count();

        if col < line_len {
            if yank {
                let ch: String = current.chars().skip(col).take(1).collect();
                self.editor
                    .behavior_state
                    .yank_mut()
                    .set_text_register(vec![ch]);
            }
            self.editor
                .record_checkpoint(crate::editor::features::history::EditKind::Delete);
            let kept: String = current
                .chars()
                .enumerate()
                .filter_map(|(idx, ch)| if idx == col { None } else { Some(ch) })
                .collect();
            self.editor
                .data_provider_mut()
                .set_field_value(line_idx, kept);
            #[cfg(feature = "gui")]
            {
                self.edited_this_frame = true;
            }
            return true;
        }

        if line_idx + 1 < self.editor.data_provider().field_count() {
            if yank {
                let text = self
                    .editor
                    .data_provider()
                    .field_value(line_idx + 1)
                    .to_string();
                self.editor
                    .behavior_state
                    .yank_mut()
                    .set_text_register(vec![text]);
            }
            self.editor
                .record_checkpoint(crate::editor::features::history::EditKind::Delete);
            if let Some(new_col) = self.editor.data_provider_mut().join_with_next(line_idx) {
                self.set_cursor_position(new_col);
                #[cfg(feature = "gui")]
                {
                    self.edited_this_frame = true;
                }
                return true;
            }
        }

        false
    }

    pub(crate) fn extract_characterwise_text(
        &self,
        lines: &[String],
        start: (usize, usize),
        end: (usize, usize),
    ) -> Vec<String> {
        if start.0 == end.0 {
            let text: String = lines[start.0]
                .chars()
                .skip(start.1)
                .take(end.1.saturating_sub(start.1) + 1)
                .collect();
            return vec![text];
        }

        let mut yanked = Vec::new();
        let first: String = lines[start.0].chars().skip(start.1).collect();
        yanked.push(first);
        for line in &lines[start.0 + 1..end.0] {
            yanked.push(line.clone());
        }
        let last: String = lines[end.0].chars().take(end.1 + 1).collect();
        yanked.push(last);
        yanked
    }
}