A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text.rs')
-rw-r--r--src/text.rs107
1 files changed, 95 insertions, 12 deletions
diff --git a/src/text.rs b/src/text.rs
index 4a13518..b6637a8 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -1,12 +1,13 @@
use std::fmt::{Debug, Display};
-use std::ops::Range;
+use std::ops::{Not as _, Range};
use std::sync::LazyLock;
use atools::Chunked;
use diff_match_patch_rs::{DiffMatchPatch, Patch, Patches};
use dsb::Cell;
use dsb::cell::Style;
-use ropey::Rope;
+use implicit_fn::implicit_fn;
+use ropey::{Rope, RopeSlice};
use tree_sitter_highlight::{
HighlightConfiguration, HighlightEvent, Highlighter,
};
@@ -127,10 +128,11 @@ impl TextArea {
[(x1, y1), (x2, y2)]
}
+ /// number of lines
pub fn l(&self) -> usize {
self.rope.len_lines()
}
- #[implicit_fn::implicit_fn]
+ #[implicit_fn]
pub fn index_at(&self, (x, y): (usize, usize)) -> usize {
let l_i = self.vo + y;
self.rope
@@ -152,7 +154,7 @@ impl TextArea {
self.setc();
}
pub fn insert(&mut self, c: &str) {
- self.rope.insert(self.cursor, &c);
+ self.rope.insert(self.cursor, c);
self.cursor += c.chars().count();
self.setc();
}
@@ -163,6 +165,10 @@ impl TextArea {
(x, y)
}
+ fn cl(&self) -> RopeSlice<'_> {
+ self.rope.line(self.rope.char_to_line(self.cursor))
+ }
+
pub fn setc(&mut self) {
self.column = self.cursor
- self.rope.line_to_char(self.rope.char_to_line(self.cursor));
@@ -174,13 +180,13 @@ impl TextArea {
self.setc();
}
- #[implicit_fn::implicit_fn]
+ #[implicit_fn]
fn indentation(&self) -> usize {
- let l = self.rope.line(self.rope.char_to_line(self.cursor));
+ let l = self.cl();
l.chars().take_while(_.is_whitespace()).count()
}
- #[implicit_fn::implicit_fn]
+ #[implicit_fn]
pub fn home(&mut self) {
let beg =
self.rope.line_to_char(self.rope.char_to_line(self.cursor));
@@ -199,8 +205,7 @@ impl TextArea {
let i = self.rope.char_to_line(self.cursor);
let beg = self.rope.line_to_char(i);
- let l = self.rope.line(self.rope.char_to_line(self.cursor));
- self.cursor = beg + l.len_chars()
+ self.cursor = beg + self.cl().len_chars()
- self.rope.get_line(i + 1).map(|_| 1).unwrap_or(0);
self.setc();
}
@@ -212,6 +217,74 @@ impl TextArea {
self.setc();
}
+ fn at(&self) -> char {
+ self.rope.get_char(self.cursor).unwrap_or('\n')
+ }
+ #[implicit_fn]
+ pub fn word_right(&mut self) {
+ self.cursor += self
+ .rope
+ .slice(self.cursor..)
+ .chars()
+ .take_while(_.is_whitespace())
+ .count();
+
+ self.cursor += if is_word(self.at()).not()
+ && !self.at().is_whitespace()
+ && !is_word(self.rope.char(self.cursor + 1))
+ {
+ self.rope
+ .slice(self.cursor..)
+ .chars()
+ .take_while(|&x| {
+ is_word(x).not() && x.is_whitespace().not()
+ })
+ .count()
+ } else {
+ self.right();
+ self.rope
+ .slice(self.cursor..)
+ .chars()
+ .take_while(|&x| is_word(x))
+ .count()
+ };
+ self.setc();
+ }
+ // from μ
+ #[lower::apply(saturating)]
+ pub fn word_left(&mut self) {
+ self.left();
+ while self.at().is_whitespace() {
+ if self.cursor == 0 {
+ return;
+ }
+ self.left();
+ }
+ if is_word(self.at()).not()
+ && !self.at().is_whitespace()
+ && !is_word(self.rope.char(self.cursor - 1))
+ {
+ while is_word(self.at()).not()
+ && self.at().is_whitespace().not()
+ {
+ if self.cursor == 0 {
+ return;
+ }
+ self.left();
+ }
+ self.right();
+ } else {
+ self.left();
+ while is_word(self.at()) {
+ if self.cursor == 0 {
+ return;
+ }
+ self.left();
+ }
+ self.right();
+ }
+ }
+
pub fn enter(&mut self) {
use run::Run;
let n = self.indentation();
@@ -289,7 +362,7 @@ impl TextArea {
}
}
- #[implicit_fn::implicit_fn]
+ #[implicit_fn]
pub fn write_to(
&mut self,
(c, r): (usize, usize),
@@ -496,6 +569,14 @@ impl TextArea {
self.end();
right!()
}
+ NamedKey::ArrowLeft if super::ctrl() => {
+ self.word_left();
+ left!()
+ }
+ NamedKey::ArrowRight if super::ctrl() => {
+ self.word_right();
+ right!()
+ }
NamedKey::ArrowLeft => {
self.left();
left!()
@@ -524,7 +605,6 @@ impl TextArea {
if [r.start, r.end].contains(&to) {
return r;
}
- dbg!(&r, to);
let r = if self.cursor == r.start {
if to < r.start {
to..r.end
@@ -545,10 +625,13 @@ impl TextArea {
} else {
panic!()
};
- dbg!(&r);
assert!(r.start < r.end);
self.cursor = to;
self.setc();
r
}
}
+
+fn is_word(r: char) -> bool {
+ matches!(r, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')
+}