use std::ops::Range; use druid::{Data, Lens}; use ropey::Rope; #[derive(Clone, Data, PartialEq, Eq, Debug)] pub enum EditMode { Normal, Insert, } #[derive(Data, Lens, Clone, PartialEq, Debug)] pub struct EditorData { #[data(same_fn = "PartialEq::eq")] pub content: Rope, pub cursor_pos: usize, pub mode: EditMode, pub cursor_opactiy: f64, pub cursor_fade: f64, pub selection_pos: Option, } impl Default for EditorData { fn default() -> Self { Self { content: Rope::from_str(""), cursor_pos: 0, selection_pos: None, mode: EditMode::Normal, cursor_opactiy: 255.0, cursor_fade: 1.0, } } } impl EditorData { pub fn new(content: &str) -> Self { Self { content: Rope::from_str(content), ..Default::default() } } pub fn move_cursor(&mut self, idx: usize) { self.cursor_pos = idx; } pub fn select_range(&self) -> Range { if let Some(selection_pos) = self.selection_pos { if self.cursor_pos > selection_pos { selection_pos..self.cursor_pos } else { self.cursor_pos..selection_pos } } else { self.cursor_pos..self.cursor_pos } } pub fn push_str(&mut self, s: &str) { if self.selection_pos.is_some() { self.content.remove(self.select_range()); self.content.insert(self.select_range().start, s); } else { self.content.insert(self.cursor_pos, s); } self.move_cursor(self.select_range().start + s.len()); } pub fn push(&mut self, c: char) { self.content.insert_char(self.cursor_pos, c); self.cursor_right(); } // pub fn cursor_to_end(&mut self) { // self.move_cursor(self.content.len_chars()); // } pub fn deselect(&mut self) { self.selection_pos = None; } pub fn delete_current_line(&mut self) { if self.current_line_index() < self.content.len_lines() { self.content.remove( self.current_line_start() ..(self.current_line_start() + self .current_line() .len_chars() .min(self.content.len_chars())), ); } else { self.cursor_up(); self.content.remove( self.current_line_start()..(self.cursor_pos + self.current_line().len_chars()), ); } if self.cursor_pos > self.content.len_chars() && self.content.len_chars() > 0 { self.cursor_pos = self.content.len_chars() - 1; } else if self.cursor_pos > self.content.len_chars() { self.cursor_pos = 0; } self.deselect(); } pub fn word_scan_forward(&mut self) { while self.cursor_pos < self.content.len_chars() { self.cursor_pos += 1; if !self .current_char() .map(|c| c.is_alphanumeric()) .unwrap_or(false) { break; } self.deselect(); } } pub fn word_scan_backward(&mut self) { if self.cursor_pos > 0 { self.cursor_pos -= 1; } while self.cursor_pos > 0 { if !self .content .get_char(self.cursor_pos - 1) .map(|c| c.is_alphanumeric()) .unwrap_or(false) { break; } else { self.cursor_pos -= 1; } self.deselect(); } } pub fn delete_char_forward(&mut self) { if !self.select_range().is_empty() { let range = self.select_range(); self.content.remove(self.select_range()); self.move_cursor(range.start); self.deselect(); return; } if self.cursor_pos < self.content.len_chars() { self.content.remove((self.cursor_pos)..self.cursor_pos + 1); } } pub fn delete_char_back(&mut self) { // Delete selection if !self.select_range().is_empty() { let range = self.select_range(); self.content.remove(self.select_range()); self.move_cursor(range.start); self.deselect(); return; } // Cant delete character sif we are at the start of the buffer if self.cursor_pos > 0 { self.content.remove((self.cursor_pos - 1)..self.cursor_pos); self.cursor_left(); } } pub fn delete_to_eol(&mut self) { self.content.remove( self.cursor_pos ..(self.current_line_start() + self.current_line().to_string().len() - 1), ) } pub fn cursor_left(&mut self) { match self.mode { EditMode::Insert => { if self.cursor_pos > 0 { self.move_cursor(self.cursor_pos - 1); } } EditMode::Normal => { if self.cursor_pos > self.current_line_start() { self.move_cursor(self.cursor_pos - 1); } } } } pub fn cursor_up(&mut self) { let line_idx = self.content.char_to_line(self.cursor_pos); if line_idx > 0 { let up_line_start = self.content.line_to_char(line_idx - 1); let up_line = self.content.line(line_idx - 1); self.move_cursor(up_line_start + self.current_column().min(up_line.len_chars() - 1)); } } pub fn select_up(&mut self) { let line_idx = self.content.char_to_line(self.cursor_pos); if line_idx > 0 { let start_of_current_line = self.content.line_to_char(line_idx); let line_pos = self.cursor_pos - start_of_current_line; let up_line_start = self.content.line_to_char(line_idx - 1); let up_line = self.content.line(line_idx - 1); self.cursor_pos = up_line_start + line_pos.min(up_line.len_chars() - 1); } } pub fn cursor_down(&mut self) { let line_idx = self.content.char_to_line(self.cursor_pos); if line_idx < self.content.len_lines() - 1 { let start_of_next_line = self.content.line_to_char(self.current_line_index() + 1); let next_line_len = self.content.line(line_idx + 1).len_chars().max(1) - 1; let new_pos = start_of_next_line + self.current_column().min(next_line_len); self.move_cursor(new_pos); } } pub fn select_down(&mut self) { //12 -> 11 let line_idx = self.content.char_to_line(self.cursor_pos); if line_idx < self.content.len_lines() - 1 { let start_of_current_line = self.content.line_to_char(line_idx); let line_pos = self.cursor_pos - start_of_current_line; let start_of_next_line = self.content.line_to_char(line_idx + 1) + 1; let next_line_len = self.content.line(line_idx + 1).len_chars(); self.cursor_pos = (start_of_next_line + line_pos.min(next_line_len)).min(start_of_next_line); } } pub fn cursor_right(&mut self) { if self.cursor_pos < self.content.len_chars() { self.move_cursor(self.cursor_pos + 1); } } pub fn select_to_end_of_line(&mut self) { self.selection_pos = Some(self.cursor_pos); self.cursor_to_end_of_line(); } pub fn cursor_to_end_of_line(&mut self) { let line_idx = self.content.char_to_line(self.cursor_pos); let start_of_line = self.content.line_to_char(line_idx); let line = self.content.line(line_idx); if line_idx == self.content.len_lines() - 1 { self.move_cursor(start_of_line + line.len_chars()); } else { self.move_cursor(start_of_line + line.len_chars() - 1); } } pub fn select_to_start_of_line(&mut self) { self.selection_pos = Some(self.cursor_pos); self.cursor_to_start_of_line(); } pub fn cursor_to_start_of_line(&mut self) { let start_of_line = self .content .line_to_char(self.content.char_to_line(self.cursor_pos)); self.move_cursor(start_of_line); } pub fn normal_mode(&mut self) { self.mode = EditMode::Normal; if self.cursor_pos == self.content.len_chars() && self.content.len_chars() != 0 { self.cursor_pos -= 1; } self.deselect(); } pub fn select_all(&mut self) { self.selection_pos = Some(0); self.cursor_pos = self.content.len_chars(); } pub fn select_left(&mut self) { if self.cursor_pos > 0 { self.cursor_pos -= 1 } } pub fn select_right(&mut self) { if self.cursor_pos < self.content.len_chars() { self.cursor_pos += 1; } } pub fn current_line(&self) -> ropey::RopeSlice { self.content .get_slice( self.current_line_start() ..self .content .line_to_char(self.current_line_index() + 1) .min(self.content.len_chars()), ) .unwrap() } pub fn current_line_index(&self) -> usize { self.content.char_to_line(self.cursor_pos) } pub fn current_line_start(&self) -> usize { self.content .line_to_char(self.content.char_to_line(self.cursor_pos)) } pub fn current_column(&self) -> usize { self.cursor_pos - self.current_line_start() } pub fn current_char(&self) -> Option { self.content.get_char(self.cursor_pos) } } #[cfg(test)] mod tests { // use super::*; }