A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text/cursor.rs')
| -rw-r--r-- | src/text/cursor.rs | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/src/text/cursor.rs b/src/text/cursor.rs new file mode 100644 index 0000000..8b555ff --- /dev/null +++ b/src/text/cursor.rs @@ -0,0 +1,499 @@ +use std::ops::{Deref, Not, Range, RangeBounds}; + +use implicit_fn::implicit_fn; +use itertools::Itertools; +use serde_derive::{Deserialize, Serialize}; + +use crate::is_word; +use crate::text::RopeExt; +/// a..=b +#[derive(Default, Clone, Serialize, Deserialize, Debug, Copy)] +pub struct Ronge { + pub start: usize, + pub end: usize, +} +impl RangeBounds<usize> for Ronge { + fn start_bound(&self) -> std::ops::Bound<&usize> { + std::ops::Bound::Included(&self.start) + } + + fn end_bound(&self) -> std::ops::Bound<&usize> { + std::ops::Bound::Excluded(&self.end) + } +} + +impl From<Ronge> for Range<usize> { + fn from(Ronge { start, end }: Ronge) -> Self { + Range { start, end } + } +} +impl From<Range<usize>> for Ronge { + fn from(std::ops::Range { start, end }: Range<usize>) -> Self { + Ronge { start, end } + } +} +#[derive(Default, Clone, Serialize, Deserialize, Debug, Copy)] +pub struct Cursor { + pub position: usize, + pub column: usize, + pub sel: Option<Ronge>, +} +impl PartialOrd<Cursor> for Cursor { + fn partial_cmp(&self, other: &Cursor) -> Option<std::cmp::Ordering> { + self.position.partial_cmp(&other.position) + } +} +impl Ord for Cursor { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.position.cmp(&other.position) + } +} +impl PartialOrd<usize> for Cursor { + fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> { + (**self).partial_cmp(other) + } +} +impl Eq for Cursor {} +impl PartialEq<Cursor> for Cursor { + fn eq(&self, other: &Cursor) -> bool { + self.position == other.position + } +} +impl PartialEq<usize> for Cursor { + fn eq(&self, &other: &usize) -> bool { + **self == other + } +} +impl Deref for Cursor { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.position + } +} + +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct Cursors { + inner: Vec<Cursor>, +} +use Default::default; +use ropey::{Rope, RopeSlice}; +use winit::keyboard::NamedKey; + +use crate::ctrl; +impl Default for Cursors { + fn default() -> Self { + Self { inner: vec![default()] } + } +} + +pub fn caster<T, U>(x: impl FnMut(T) -> U) -> impl FnMut(T) -> U { + x +} +pub macro ceach($cursor: expr, $f:expr) { + (0..$cursor.inner.len()).for_each(|i| { + let c = *$cursor.inner.get(i).expect("aw dangit"); + caster::<Cursor, _>($f)(c); + }); + $cursor.coalesce(); +} +// macro_rules! ceach_mut { +// ($cursor: expr, |cursor| $f:block) => {{ +// let n = $cursor.inner.len(); +// { +// (0..n).map(|i: usize| { +// let cursor: &mut Cursor = &mut $cursor.inner[i]; +// unhygienic2::unhygienic! { $f } +// }) +// } +// }}; +// } +// pub(crate) use ceach_mut; +// use unhygienic2::unhygienic; +impl Cursor { + pub fn new(c: usize, r: &Rope) -> Self { + Self { column: r.x(c).unwrap(), position: c, sel: None } + } + fn cl(self, r: &Rope) -> RopeSlice<'_> { + r.line(r.char_to_line(*self)) + } + pub fn setc(&mut self, r: &Rope) { + self.column = + self.position - r.beginning_of_line(self.position).unwrap(); + } + pub fn set_ho(&mut self) {} + pub fn cursor(self, r: &Rope) -> (usize, usize) { + r.xy(*self).unwrap() + } + pub fn indentation(self, r: &Rope) -> usize { + r.indentation_of(self.cursor(r).1) + } + + #[implicit_fn] + pub fn home(&mut self, r: &Rope) { + let l = r.char_to_line(**self); + let beg = r.line_to_char(l); + let i = **self - beg; + let whitespaces = self.indentation(r); + if r.line(l).chars().all(_.is_whitespace()) { + self.position = beg; + self.column = 0; + } else if i == whitespaces { + self.position = beg; + self.column = 0; + } else { + self.position = whitespaces + beg; + self.column = whitespaces; + } + self.set_ho(); + } + pub fn end(&mut self, r: &Rope) { + let i = r.char_to_line(**self); + let beg = r.line_to_char(i); + + self.position = beg + self.cl(r).len_chars() + - r.get_line(i + 1).map(|_| 1).unwrap_or(0); + self.setc(r); + self.set_ho(); + } + pub fn at_(self, r: &Rope) -> char { + r.get_char(*self - 1).unwrap_or('\n') + } + /// ?? + pub fn at_plus_one(self, r: &Rope) -> char { + r.get_char(*self).unwrap_or('\n') + } + + #[implicit_fn] + pub fn word_right(&mut self, r: &Rope) { + self.position += r + .slice(**self..) + .chars() + .take_while(_.is_whitespace()) + .count(); + + self.position += if is_word(self.at_plus_one(r)).not() + && !self.at_plus_one(r).is_whitespace() + && !is_word(r.char(**self + 1)) + { + r.slice(**self..) + .chars() + .take_while(|&x| { + is_word(x).not() && x.is_whitespace().not() + }) + .count() + } else { + self.right(r); + r.slice(**self..).chars().take_while(|&x| is_word(x)).count() + }; + self.setc(r); + self.set_ho(); + } + // from μ + pub fn word_left(&mut self, r: &Rope) { + self.position = self.word_left_p(r); + self.setc(r); + self.set_ho(); + } + #[lower::apply(saturating)] + pub fn word_left_p(self, r: &Rope) -> usize { + let mut c = *self - 1; + if r.x(*self).unwrap() == 0 { + return c; + } + macro_rules! at { + () => { + r.get_char(c).unwrap_or('\n') + }; + } + while at!().is_whitespace() { + if r.x(c).unwrap() == 0 { + return c; + } + c -= 1 + } + if is_word(at!()).not() + && !at!().is_whitespace() + && !is_word(r.char(c - 1)) + { + while is_word(at!()).not() && at!().is_whitespace().not() { + if r.x(c).unwrap() == 0 { + return c; + } + c -= 1; + } + c += 1; + } else { + c -= 1; + while is_word(at!()) { + if r.x(c).unwrap() == 0 { + return c; + } + c -= 1; + } + c += 1; + } + c + } + + fn right(&mut self, r: &Rope) { + self.position += 1; + self.position = (**self).min(r.len_chars()); + self.setc(r); + self.set_ho(); + } + + fn left(&mut self, r: &Rope) { + self.position -= 1; + self.setc(r); + } + + pub fn down(&mut self, r: &Rope, vo: &mut usize, r_: usize) { + let l = r.try_char_to_line(**self).unwrap_or(0); + + // next line size + let Some(s) = r.get_line(l + 1) else { + return; + }; + if s.len_chars() == 0 { + return self.position += 1; + } + // position of start of next line + let b = r.line_to_char(l.wrapping_add(1)); + self.position = b + if s.len_chars() > self.column { + // if next line is long enough to position the cursor at column, do so + self.column + } else { + // otherwise, put it at the end of the next line, as it is too short. + s.len_chars() + - r.get_line(l.wrapping_add(2)).map(|_| 1).unwrap_or(0) + }; + if r.char_to_line(**self) >= (*vo + r_).saturating_sub(5) { + *vo += 1; + // self.vo = self.vo.min(self.l() - self.r); + } + self.set_ho(); + } + + pub fn up(&mut self, r: &Rope, vo: &mut usize) { + let l = r.try_char_to_line(**self).unwrap_or(0); + let Some(s) = r.get_line(l.wrapping_sub(1)) else { + return; + }; + let b = r.line_to_char(l - 1); + self.position = b + if s.len_chars() > self.column { + self.column + } else { + s.len_chars() - 1 + }; + if r.char_to_line(**self).saturating_sub(4) < *vo { + *vo = vo.saturating_sub(1); + } + self.set_ho(); + } + pub fn extend_selection( + &mut self, + key: NamedKey, + rope: &Rope, + vo: &mut usize, + r_: usize, + ) { + let Some(r) = self.sel else { unreachable!() }; + macro_rules! left { + () => { + if **self != 0 && **self >= r.start { + // left to right going left (shrink right end) + r.start..**self + } else { + // right to left going left (extend left end) + **self..r.end + } + }; + } + macro_rules! right { + () => { + if **self == rope.len_chars() { + r.into() + } else if **self > r.end { + // left to right (extend right end) + r.start..**self + } else { + // right to left (shrink left end) + **self..r.end + } + }; + } + let v = match key { + NamedKey::Home => { + let pself = *self; + self.home(rope); + if pself > *self { left!() } else { right!() } + } + NamedKey::End => { + self.end(rope); + right!() + } + NamedKey::ArrowLeft if ctrl() => { + self.word_left(rope); + left!() + } + NamedKey::ArrowRight if ctrl() => { + self.word_right(rope); + right!() + } + NamedKey::ArrowLeft => { + self.left(rope); + left!() + } + NamedKey::ArrowRight => { + self.right(rope); + right!() + } + NamedKey::ArrowUp => { + self.up(rope, vo); + left!() + } + NamedKey::ArrowDown => { + self.down(rope, vo, r_); + right!() + } + _ => unreachable!(), + }; + self.sel = Some(v.into()); + } + #[track_caller] + pub fn extend_selection_to(&mut self, to: usize, rope: &Rope) { + let Some(r) = self.sel else { unreachable!() }; + if [r.start, r.end].contains(&to) { + return; + } + let r = if **self == r.start { + if to < r.start { + to..r.end + } else if to > r.end { + r.end..to + } else { + to..r.end + } + } else if **self == r.end { + if to > r.end { + r.start..to + } else if to < r.start { + to..r.start + } else { + r.start..to + } + } else { + panic!() + }; + assert!(r.start < r.end); + // dbg!(to, &r); + self.position = to; + self.setc(rope); + self.sel = Some(r.into()); + } +} +impl Cursors { + pub fn clear_selections(&mut self) { + self.each(|x| x.sel = None); + } + pub fn iter( + &self, + ) -> impl Iterator<Item = Cursor> + ExactSizeIterator { + self.inner.iter().copied() + } + pub fn alone(&mut self) { + self.inner.truncate(1); + } + pub fn add(&mut self, c: usize, rope: &Rope) { + self.inner.push(Cursor::new(c, rope)); + } + pub fn max(&self) -> Cursor { + *self.inner.iter().max().unwrap() + } + pub fn min(&self) -> Cursor { + *self.inner.iter().min().unwrap() + } + pub fn first(&self) -> Cursor { + self.inner[0] + } + pub fn coalesce(&mut self) { + self.inner = self.inner.iter().copied().dedup().collect(); + } + pub fn first_mut(&mut self) -> &mut Cursor { + &mut self.inner[0] + } + pub fn just(&mut self, c: usize, rope: &Rope) { + self.one(Cursor::new(c, rope)); + } + pub fn one(&mut self, c: Cursor) { + self.inner = vec![c]; + } + pub fn each(&mut self, f: impl FnMut(&mut Cursor)) { + self.inner.iter_mut().rev().for_each(f); + } + pub fn each_ref(&self, f: impl FnMut(Cursor)) { + self.inner.iter().copied().rev().for_each(f); + } + pub fn manipulate(&mut self, mut f: impl FnMut(usize) -> usize) { + self.each(|lem| { + lem.position = f(lem.position); + if let Some(sel) = &mut lem.sel { + sel.start = f(sel.start); + sel.end = f(sel.end); + } + }); + } + pub fn left(&mut self, r: &Rope) { + self.each(|cursor| cursor.left(r)); + self.coalesce(); + } + pub fn right(&mut self, r: &Rope) { + self.each(|cursor| cursor.right(r)); + self.coalesce(); + } + + pub fn home(&mut self, r: &Rope) { + self.each(|c| c.home(r)); + self.coalesce(); + } + pub fn end(&mut self, r: &Rope) { + self.each(|c| c.end(r)); + self.coalesce(); + } + pub fn word_right(&mut self, r: &Rope) { + self.each(|c| c.word_right(r)); + self.coalesce(); + } + pub fn word_left(&mut self, r: &Rope) { + self.each(|c| c.word_left(r)); + self.coalesce(); + } + pub fn set_ho(&mut self) { + // let x = self.cursor_visual().0; + // if x < self.ho + 4 { + // self.ho = x.saturating_sub(4); + // } else if x + 4 > (self.ho + self.c) { + // self.ho = (x.saturating_sub(self.c)) + 4; + // } + } + + pub fn down(&mut self, rope: &Rope, vo: &mut usize, r: usize) { + self.each(|x| x.down(rope, vo, r)); + self.coalesce(); + } + pub fn up(&mut self, rope: &Rope, vo: &mut usize) { + self.each(|x| x.up(rope, vo)); + self.coalesce(); + } + // pub fn extend_selection( + // &mut self, + // key: NamedKey, + // r: Vec<std::ops::Range<usize>>, + // rope: &Rope, + // vo: &mut usize, + // r_: usize, + // ) -> Vec<std::ops::Range<usize>> { + // panic!(); + // } +} |