A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text.rs')
| -rw-r--r-- | src/text.rs | 285 |
1 files changed, 232 insertions, 53 deletions
diff --git a/src/text.rs b/src/text.rs index 08c1cf2..1a66d11 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,8 +1,8 @@ use std::cmp::min; use std::fmt::{Debug, Display}; -use std::ops::{Not as _, Range}; +use std::ops::{Deref, Not as _, Range}; use std::path::Path; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use atools::prelude::*; use diff_match_patch_rs::{DiffMatchPatch, Patches}; @@ -11,33 +11,125 @@ use dsb::cell::Style; use helix_core::Syntax; use helix_core::syntax::{HighlightEvent, Loader}; use implicit_fn::implicit_fn; +use log::error; +use lsp_types::{ + SemanticToken, SemanticTokensLegend, SemanticTokensServerCapabilities, +}; use ropey::{Rope, RopeSlice}; +use tree_house::fixtures; use winit::keyboard::{NamedKey, SmolStr}; + +use crate::MODIFIERS; +use crate::text::semantic::{MCOLORS, MODIFIED, MSTYLE}; macro_rules! theme { - ($($x:literal $color:literal),+ $(,)?) => { + ($n:literal $($x:literal $color:literal $($style:expr)?),+ $(,)?) => { #[rustfmt::skip] - const NAMES: [&str; 15] = [$($x),+]; + pub const NAMES: [&str; $n] = [$($x),+]; #[rustfmt::skip] - const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| color(x.tail()) - );}; + pub const COLORS: [[u8; 3]; NAMES.len()] = car::map!([$($color),+], |x| color(x.tail())); + pub const STYLES: [u8; NAMES.len()] = [$( + ($($style, )? 0, ).0 + ),+]; + }; } -theme! { +theme! { 15 "attribute" b"#ffd173", - "comment" b"#5c6773", + "comment" b"#5c6773" Style::ITALIC, "constant" b"#DFBFFF", - "function" b"#FFD173", + "function" b"#FFD173" Style::ITALIC, "variable.builtin" b"#FFAD66", - "keyword" b"#FFAD66", + "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD, "number" b"#dfbfff", "operator" b"#F29E74", "punctuation" b"#cccac2", "string" b"#D5FF80", - "tag" b"#DFBFFF", - "type" b"#73D0FF", + "tag" b"#5CCFE6" Style::ITALIC | Style::BOLD, + "type" b"#73D0FF" Style::ITALIC | Style::BOLD, "variable" b"#cccac2", "variable.parameter" b"#DFBFFF", "namespace" b"#73d0ff", } + +mod semantic { + use atools::prelude::*; + use dsb::cell::Style; + macro_rules! modified { + ($count:literal $($x:literal . $mod:literal $color:literal $($style:expr)?,)+ $(,)?) => { + pub const MODIFIED: [(&str, &str); $count] = [ + $(($x, $mod),)+ + ]; + pub const MCOLORS: [[u8;3]; MODIFIED.len()] = car::map!([$($color),+], |x| color(x.tail())); + pub const MSTYLE: [u8; MODIFIED.len()] = [$(($($style, )? 0, ).0 ,)+]; + }; + } + use super::color; + theme! { 19 + "comment" b"#5c6773" Style::ITALIC, + // "decorator" b"#cccac2", + // "enumMember" b"#cccac2", + "function" b"#FFD173" Style::ITALIC, + "interface" b"#5CCFE6", + "keyword" b"#FFAD66" Style::ITALIC | Style::BOLD, + "macro" b"#fbc351" Style::BOLD, + "method" b"#FFD173" Style::ITALIC, + // "namespace" b"#cccac2", + "number" b"#dfbfff", + "operator" b"#F29E74", + // "property" b"#cccac2", + "string" b"#D5FF80", + // "struct" b"#cccac2", + // "typeParameter" b"#cccac2", + "enum" b"#73b9ff" Style::ITALIC | Style::BOLD, + "builtinType" b"#73d0ff" Style::ITALIC, + // "type" b"#73d0ff" Style::ITALIC | Style::BOLD, + "typeAlias" b"#5ce6d8" Style::ITALIC | Style::BOLD, + "struct" b"#73d0ff" Style::ITALIC | Style::BOLD, + // "variable" b"#cccac2", + // "angle" b"#cccac2", + // "arithmetic" b"#cccac2", + // "attributeBracket" b"#cccac2", + "parameter" b"#DFBFFF", + "namespace" b"#73d0ff", + // "attributeBracket" b"#cccac2", + // "attribute" b"#cccac2", + // "bitwise" b"#cccac2", + // "boolean" b"#cccac2", + // "brace" b"#cccac2", + // "bracket" b"#cccac2", + // "builtinAttribute" b"#cccac2", + // "character" b"#cccac2", + // "colon" b"#cccac2", + // "comma" b"#cccac2", + // "comparison" b"#cccac2", + // "constParameter" b"#cccac2", + "const" b"#DFBFFF", + // "deriveHelper" b"#cccac2", + // "derive" b"#cccac2", + // "dot" b"#cccac2", + // "escapeSequence" b"#cccac2", + // "formatSpecifier" b"#cccac2", + // "generic" b"#cccac2", + // "invalidEscapeSequence" b"#cccac2", + // "label" b"#cccac2", + // "lifetime" b"#cccac2", + // "logical" b"#cccac2", + "macroBang" b"#f28f74", + // "parenthesis" b"#cccac2", + // "procMacro" b"#cccac2", + // "punctuation" b"#cccac2", + "selfKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD, + "selfTypeKeyword" b"#FFAD66" Style::ITALIC | Style::BOLD, + // "semicolon" b"#cccac2", + // "static" b"#cccac2", + // "toolModule" b"#cccac2", + // "union" b"#cccac2", + // "unresolvedReference" b"#cccac2", + } + modified! { 2 + "function" . "library" b"#F28779", + "variable" . "mutable" b"#e6dab6", + } +} const fn of(x: &'static str) -> usize { let mut i = 0; while i < NAMES.len() { @@ -49,12 +141,6 @@ const fn of(x: &'static str) -> usize { panic!() } -const STYLES: [u8; NAMES.len()] = amap::amap_d! { - const { of("type") } | const { of("keyword") } | const { of("tag") } => Style::ITALIC | Style::BOLD, - const { of("comment") } | const { of("function") }=> Style::ITALIC, - -}; - const fn color(x: &[u8; 6]) -> [u8; 3] { car::map!( car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(), @@ -115,6 +201,7 @@ pub struct TextArea { } impl Debug for TextArea { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + String::new(); f.debug_struct("TextArea") .field("rope", &self.rope) .field("cursor", &self.cursor) @@ -168,7 +255,8 @@ impl TextArea { .unwrap_or_default()) .min(x.saturating_sub(self.line_number_offset() + 1)) }) - .unwrap_or(self.rope.len_chars()) + .unwrap_or(usize::MAX) + .min(self.rope.len_chars()) } pub fn insert_(&mut self, c: SmolStr) { @@ -413,36 +501,7 @@ impl TextArea { } } - pub fn slice<'c>( - &self, - (c, r): (usize, usize), - cell: &'c mut [Cell], - range: Range<usize>, - ) -> &'c mut [Cell] { - let [(x1, y1), (x2, y2)] = dbg!(self.position(range)); - &mut cell[y1 * c + x1..y2 * c + x2] - } - - #[implicit_fn] - pub fn write_to( - &mut self, - color: [u8; 3], - bg: [u8; 3], - (into, (w, _)): (&mut [Cell], (usize, usize)), - (ox, oy): (usize, usize), - selection: Option<Range<usize>>, - apply: impl FnOnce((usize, usize), &mut Self, &mut [Cell]), - path: Option<&Path>, - ) { - let (c, r) = (self.c, self.r); - let mut cells = vec![ - Cell { - style: Style { color, bg, flags: 0 }, - letter: None, - }; - (self.l().max(r) + r - 1) * c - ]; - + pub fn tree_sit<'c>(&self, path: Option<&Path>, cell: &'c mut [Cell]) { let language = path .and_then(|x| LOADER.language_for_filename(x)) .unwrap_or_else(|| LOADER.language_for_name("rust").unwrap()); @@ -471,8 +530,9 @@ impl TextArea { s as u32..e as u32, ); let mut at = 0; - let mut highlight_stack = Vec::with_capacity(8); + let (c, r) = (self.c, self.r); + let mut highlight_stack = Vec::with_capacity(8); loop { let (e, new_highlights) = h.advance(); if e == HighlightEvent::Refresh { @@ -498,16 +558,50 @@ impl TextArea { c, ); - cells.get_mut(y1 * c + x1..y2 * c + x2).map(|x| { + cell.get_mut(y1 * c + x1..y2 * c + x2).map(|x| { x.iter_mut().for_each(|x| { - x.style.flags = STYLES[h.idx()]; + x.style.flags |= STYLES[h.idx()]; x.style.color = COLORS[h.idx()]; }) }); } at = end; } - drop(h); + } + + pub fn slice<'c>( + &self, + (c, r): (usize, usize), + cell: &'c mut [Cell], + range: Range<usize>, + ) -> &'c mut [Cell] { + let [(x1, y1), (x2, y2)] = self.position(range); + &mut cell[y1 * c + x1..y2 * c + x2] + } + + #[implicit_fn] + pub fn write_to<'lsp>( + &mut self, + color: [u8; 3], + bg: [u8; 3], + (into, (w, _)): (&mut [Cell], (usize, usize)), + (ox, oy): (usize, usize), + selection: Option<Range<usize>>, + apply: impl FnOnce((usize, usize), &mut Self, &mut [Cell]), + path: Option<&Path>, + tokens: Option<( + arc_swap::Guard<Arc<Box<[SemanticToken]>>>, + &SemanticTokensLegend, + )>, + ) { + let (c, r) = (self.c, self.r); + let mut cells = vec![ + Cell { + style: Style { color, bg, flags: 0 }, + letter: None, + }; + (self.l().max(r) + r - 1) * c + ]; for (l, y) in self.rope.lines().zip(0..) { for (e, x) in l.chars().take(c).zip(0..) { @@ -516,6 +610,89 @@ impl TextArea { } } } + + if let Some((t, leg)) = tokens + && t.len() > 0 + { + let mut ln = 0; + let mut ch = 0; + for t in &**t { + ln += t.delta_line; + ch = match t.delta_line { + 1.. => t.delta_start, + 0 => ch + t.delta_start, + }; + let x: Result<(usize, usize), ropey::Error> = try { + let x1 = self.rope.try_byte_to_char( + self.rope.try_line_to_byte(ln as _)? + ch as usize, + )? - self.rope.try_line_to_char(ln as _)?; + let x2 = self.rope.try_byte_to_char( + self.rope.try_line_to_byte(ln as _)? + + ch as usize + + t.length as usize, + )? - self.rope.try_line_to_char(ln as _)?; + (x1, x2) + }; + let Ok((x1, x2)) = x else { + continue; + }; + + if ln as usize * c + x1 < self.vo * c { + continue; + } else if ln as usize * c + x1 > self.vo * c + r * c { + break; + } + let slice = cells + .get_mut(ln as usize * c + x1..ln as usize * c + x2) + .expect("good slice"); + let Some(tty) = leg.token_types.get(t.token_type as usize) + else { + error!( + "issue while loading semantic token {t:?}; \ + couldnt find in legend" + ); + continue; + }; + if let Some(f) = + semantic::NAMES.iter().position(|&x| x == tty.as_str()) + { + slice.iter_mut().for_each(|x| { + x.style.color = semantic::COLORS[f]; + x.style.flags |= semantic::STYLES[f]; + }); + } + // println!( + // "{tty:?}: {}", + // slice + // .iter() + // .flat_map(|x| x.letter) + // .collect::<String>() + // ); + let mut modi = t.token_modifiers_bitset; + while modi != 0 { + let bit = modi.trailing_zeros(); + + leg.token_modifiers + .get(bit as usize) + .and_then(|modi| { + MODIFIED.iter().position(|&(x, y)| { + (x == tty.as_str()) & (y == modi.as_str()) + }) + }) + .map(|i| { + slice.iter_mut().for_each(|x| { + x.style.color = MCOLORS[i]; + x.style.flags |= MSTYLE[i]; + }); + }); + + modi &= !(1 << bit); + } + } + } else { + self.tree_sit(path, &mut cells); + } + selection.map(|x| self.position(x)).map(|[(x1, y1), (x2, y2)]| { (y1 * c + x1..y2 * c + x2).for_each(|x| { cells @@ -673,6 +850,8 @@ impl TextArea { panic!() }; assert!(r.start < r.end); + dbg!(to, &r); + self.cursor = to; self.setc(); r |