308 lines
9.1 KiB
Rust
308 lines
9.1 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: usize,
|
|
}
|
|
|
|
impl Default for EditorData {
|
|
fn default() -> Self {
|
|
Self {
|
|
content: Rope::from_str("1234567890\n\n\n123\n\n1234\n1234"),
|
|
cursor_pos: 0,
|
|
selection_pos: 0,
|
|
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) {
|
|
let has_selection = self.cursor_pos != self.selection_pos;
|
|
if idx <= self.content.len_chars() {
|
|
self.cursor_pos = idx;
|
|
}
|
|
if self.mode == EditMode::Normal && self.cursor_pos == 0 {
|
|
self.cursor_pos = 1;
|
|
}
|
|
|
|
if !has_selection {
|
|
self.deselect();
|
|
}
|
|
}
|
|
|
|
pub fn select_range(&self) -> Range<usize> {
|
|
if self.cursor_pos > self.selection_pos {
|
|
self.selection_pos..self.cursor_pos
|
|
} else {
|
|
self.cursor_pos..self.selection_pos
|
|
}
|
|
}
|
|
|
|
pub fn push_str(&mut self, s: &str) {
|
|
if self.selection_pos != self.cursor_pos {
|
|
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 = self.cursor_pos;
|
|
}
|
|
|
|
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);
|
|
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);
|
|
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 cursor_left(&mut self) {
|
|
if self.cursor_pos > 0 {
|
|
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 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.move_cursor(up_line_start + line_pos.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_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);
|
|
let next_line_len = self.content.line(line_idx + 1).len_chars();
|
|
|
|
let new_pos = start_of_next_line + line_pos.min(next_line_len);
|
|
|
|
if self.cursor_pos == new_pos {
|
|
self.move_cursor(new_pos + 1);
|
|
} else {
|
|
self.move_cursor(new_pos);
|
|
}
|
|
println!(" ");
|
|
dbg!(line_idx, new_pos, start_of_next_line);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
let has_selection = self.cursor_pos != self.selection_pos;
|
|
|
|
if self.cursor_pos < self.content.len_chars() {
|
|
self.move_cursor(self.cursor_pos + 1);
|
|
}
|
|
|
|
if !has_selection {
|
|
self.deselect();
|
|
}
|
|
}
|
|
|
|
pub fn select_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.selection_pos = start_of_line + line.len_chars();
|
|
} else {
|
|
self.selection_pos = start_of_line + line.len_chars() - 1;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
let start_of_line = self
|
|
.content
|
|
.line_to_char(self.content.char_to_line(self.cursor_pos));
|
|
|
|
self.selection_pos = 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 select_all(&mut self) {
|
|
self.selection_pos = self.content.len_chars();
|
|
self.cursor_pos = 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
fn current_line(&self) -> usize {
|
|
self.content.char_to_line(self.cursor_pos)
|
|
}
|
|
|
|
fn current_char(&self) -> Option<char> {
|
|
self.content.get_char(self.cursor_pos)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn cursor_left_normal_empty_line() {
|
|
let mut data = EditorData {
|
|
mode: EditMode::Normal,
|
|
..Default::default()
|
|
};
|
|
data.push_str("0123456789\n\n1234");
|
|
data.cursor_pos = 12;
|
|
data.selection_pos = 12;
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 11);
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 10);
|
|
}
|
|
|
|
#[test]
|
|
fn cursor_left_normal_double_empty_line() {
|
|
let mut data = EditorData {
|
|
mode: EditMode::Normal,
|
|
..Default::default()
|
|
};
|
|
data.push_str("0123456789\n\n\n1234");
|
|
data.cursor_pos = 14;
|
|
data.selection_pos = 14;
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 12);
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 11);
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 10);
|
|
}
|
|
|
|
#[test]
|
|
fn cursor_left_normal_end_of_line() {
|
|
let mut data = EditorData {
|
|
mode: EditMode::Normal,
|
|
..Default::default()
|
|
};
|
|
data.push_str("0123456789\n1234");
|
|
data.cursor_pos = 12;
|
|
data.selection_pos = 12;
|
|
assert_eq!(data.current_char().unwrap(), '1');
|
|
data.cursor_left();
|
|
assert_eq!(data.cursor_pos, 10);
|
|
}
|
|
}
|