A simple CPU rendered GUI IDE experience.
-rw-r--r--src/bar.rs1
-rw-r--r--src/main.rs35
-rw-r--r--src/text.rs107
3 files changed, 106 insertions, 37 deletions
diff --git a/src/bar.rs b/src/bar.rs
index f596a51..e3dbc87 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -63,7 +63,6 @@ impl Bar {
*y = Cell {
letter: Some(x),
style: Style { flags: z, ..y.style },
- ..*y
}
});
}
diff --git a/src/main.rs b/src/main.rs
index 08ffb50..2b831b6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,20 +10,13 @@
portable_simd
)]
#![allow(incomplete_features, redundant_semicolons)]
-use std::collections::VecDeque;
use std::convert::identity;
-use std::fs::File;
-use std::hint::assert_unchecked;
-use std::iter::zip;
use std::num::NonZeroU32;
-use std::simd::prelude::*;
-use std::sync::{LazyLock, Mutex};
+use std::sync::LazyLock;
use std::time::Instant;
use Default::default;
-use array_chunks::*;
-use atools::prelude::*;
-use diff_match_patch_rs::{Efficient, PatchInput};
+use diff_match_patch_rs::PatchInput;
use dsb::cell::Style;
use dsb::{Cell, F};
use fimg::Image;
@@ -79,19 +72,13 @@ impl Hist {
self.changed = false;
}
fn undo_(&mut self) -> Option<Diff> {
- self.history.pop().map(|x| {
- self.redo_history.push(x.clone());
- x
- })
+ self.history.pop().inspect(|x| self.redo_history.push(x.clone()))
}
fn redo_(&mut self) -> Option<Diff> {
- self.redo_history.pop().map(|x| {
- self.history.push(x.clone());
- x
- })
+ self.redo_history.pop().inspect(|x| self.history.push(x.clone()))
}
pub fn undo(&mut self, t: &mut TextArea) {
- self.push_if_changed(&t);
+ self.push_if_changed(t);
self.undo_().map(|x| {
x.apply(t, false);
self.last = t.clone();
@@ -273,7 +260,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
FG,
(&mut cells, (c, r)),
r - 1,
- &origin.as_deref().unwrap_or("new buffer"),
+ origin.as_deref().unwrap_or("new buffer"),
&state,
&text,
);
@@ -567,7 +554,6 @@ fn handle2(key: Key, text: &mut TextArea) {
match key {
Named(Space) => text.insert(" "),
Named(Backspace) => text.backspace(),
- Named(ArrowLeft) => text.left(),
Named(Home) if ctrl() => {
text.cursor = 0;
text.vo = 0;
@@ -578,13 +564,15 @@ fn handle2(key: Key, text: &mut TextArea) {
}
Named(Home) => text.home(),
Named(End) => text.end(),
- // Named(Tab)
+ Named(ArrowLeft) if ctrl() => text.word_left(),
+ Named(ArrowRight) if ctrl() => text.word_right(),
+ Named(ArrowLeft) => text.left(),
Named(ArrowRight) => text.right(),
Named(ArrowUp) => text.up(),
Named(ArrowDown) => text.down(),
Named(Enter) => text.enter(),
Character(x) => {
- text.insert(&*x);
+ text.insert(&x);
}
_ => {}
};
@@ -615,7 +603,7 @@ pub static BIFONT: LazyLock<Instance<'static>> = LazyLock::new(|| {
pub static BFONT: LazyLock<Instance<'static>> =
LazyLock::new(|| FONT.instances().find_by_name("Bold").unwrap());
fn shift() -> bool {
- dbg!(unsafe { MODIFIERS }.shift_key())
+ unsafe { MODIFIERS }.shift_key()
}
fn ctrl() -> bool {
unsafe { MODIFIERS }.control_key()
@@ -628,7 +616,6 @@ impl State {
}
}
-// use NamedKey::Arrow
use std::ops::Range;
rust_fsm::state_machine! {
#[derive(Clone, Debug)]
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' | '_')
+}