abacus/abacus-ui/src/data/editor_data.rs

335 lines
9.8 KiB
Rust

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<usize>,
}
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<usize> {
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<char> {
self.content.get_char(self.cursor_pos)
}
}
#[cfg(test)]
mod tests {
// use super::*;
}