A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text.rs')
| -rw-r--r-- | src/text.rs | 166 |
1 files changed, 163 insertions, 3 deletions
diff --git a/src/text.rs b/src/text.rs index e2cffa9..3245f82 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,26 +1,74 @@ use std::iter::{repeat, repeat_n}; +use std::os::fd::AsRawFd; +use atools::Chunked; use dsb::Cell; use dsb::cell::Style; use ropey::Rope; -#[derive(Debug, Default, Clone)] +use tree_sitter::{InputEdit, Language, Parser, Point, Tree}; +use tree_sitter_highlight::{ + HighlightConfiguration, HighlightEvent, Highlighter, +}; +#[rustfmt::skip] +const NAMES: [&str; 13] = ["attribute", "comment", "constant", "function", "keyword", "number", "operator", "punctuation", + "string", "tag", "type", "variable", "variable.parameter"]; +#[rustfmt::skip] +const COLORS: [[u8; 3]; 13] = car::map!( + [ + b"ffd173", b"5c6773", b"DFBFFF", b"FFD173", b"FFAD66", b"dfbfff", b"F29E74", b"cccac2", + b"D5FF80", b"DFBFFF", b"73D0FF", b"5ccfe6", b"5ccfe6" + ], + |x| color(x) +); + +const fn color(x: &[u8; 6]) -> [u8; 3] { + car::map!( + car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(), + |[a, b]| a * 16 + b + ) +} +#[test] +fn x() { + dbg!(color(b"ffd173")); +} + pub struct TextArea { rope: Rope, cursor: usize, + // parser: Parser, + highlighter: Highlighter, + tree: Option<Tree>, } impl TextArea { pub fn insert(&mut self, c: &str) { + dbg!(c); self.rope.insert(self.cursor, c); self.cursor += c.chars().count(); } + pub fn backspace(&mut self) { + _ = self.rope.try_remove(self.cursor - 1..self.cursor); + self.cursor = self.cursor.saturating_sub(1); + } + pub fn cells( - &self, + &mut self, (c, r): (usize, usize), color: [u8; 3], bg: [u8; 3], ) -> Vec<Cell> { + let mut x = HighlightConfiguration::new( + tree_sitter_rust::LANGUAGE.into(), + "rust", + tree_sitter_rust::HIGHLIGHTS_QUERY, + tree_sitter_rust::INJECTIONS_QUERY, + "", + ) + .unwrap(); + + x.configure(&NAMES); + let mut cells = vec![ Cell { style: Style { @@ -32,11 +80,123 @@ impl TextArea { }; c * r ]; + + // dbg!(unsafe { + // String::from_utf8_unchecked( + // self.rope.bytes().collect::<Vec<_>>(), + // ) + // }); + let mut s = 0; + for hl in self + .highlighter + .highlight( + &x, + &self.rope.bytes().collect::<Vec<_>>(), + None, + |_| None, + ) + .unwrap() + .map(Result::unwrap) + { + match hl { + HighlightEvent::Source { start, end } => { + // for elem in start..end { + // styles[elem] = s; + // } + let y1 = self.rope.char_to_line(start); + let y2 = self.rope.char_to_line(start); + let x1 = start - self.rope.line_to_char(y1); + let x2 = end - self.rope.line_to_char(y2); + // dbg!((x1, y1), (x2, y2)); + cells.get_mut(y1 * c + x1..y2 * c + x2).map(|x| { + x.iter_mut() + .for_each(|x| x.style.color = COLORS[s]) + }); + // println!( + // "highlight {} {s} {}: {:?}", + // self.rope.byte_slice(start..end), + // NAMES[s], + // COLORS[s], + // ) + } + HighlightEvent::HighlightStart(s_) => s = s_.0, + HighlightEvent::HighlightEnd => s = 0, + } + } + + // let (a, b) = code.into_iter().collect::<(Vec<_>, Vec<_>)>(); + + // let mut i = 0; + // let mut y = 0; + // let mut x = 0; + // while i < code.len() { + // if code[i] == b'\n' { + // x = 0; + // y += 1; + // if y == r { + // break; + // } + // } else { + // cells[y * c + x].letter = Some(code[i] as char); + // cells[y * c + x].style.color = COLORS[styles[i]]; + // x += 1 + // } + // i += 1; + // } + for (l, y) in self.rope.lines().take(r).zip(0..) { for (e, x) in l.chars().take(c).zip(0..) { - cells[y * c + x].letter = Some(e); + if e != '\n' { + cells[y * c + x].letter = Some(e); + } } } cells } } + +impl Default for TextArea { + fn default() -> Self { + // let mut parser = Highlighter::new(); + + let mut x = Self { + rope: Default::default(), + cursor: 0, + tree: None, + highlighter: Highlighter::new(), + }; + + x + } +} + +pub trait TakeLine<'b> { + fn take_line<'a>(&'a mut self) -> Option<&'b [u8]>; + fn take_backline<'a>(&'a mut self) -> Option<&'b [u8]>; +} + +impl<'b> TakeLine<'b> for &'b [u8] { + fn take_line<'a>(&'a mut self) -> Option<&'b [u8]> { + match memchr::memchr(b'\n', self) { + None if self.is_empty() => None, + None => Some(std::mem::replace(self, b"")), + Some(end) => { + let line = &self[..end]; + *self = &self[end + 1..]; + Some(line) + } + } + } + + fn take_backline<'a>(&'a mut self) -> Option<&'b [u8]> { + let end = self.len().checked_sub(1)?; + match memchr::memrchr(b'\n', &self[..end]) { + None => Some(std::mem::replace(self, b"")), + Some(end) => { + let line = &self[end + 1..]; + *self = &self[..end]; + Some(line) + } + } + } +} |