A simple CPU rendered GUI IDE experience.
word
| -rw-r--r-- | src/bar.rs | 1 | ||||
| -rw-r--r-- | src/main.rs | 35 | ||||
| -rw-r--r-- | src/text.rs | 107 |
3 files changed, 106 insertions, 37 deletions
@@ -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' | '_') +} |