A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/text.rs')
-rw-r--r--src/text.rs98
1 files changed, 58 insertions, 40 deletions
diff --git a/src/text.rs b/src/text.rs
index 0eccd6b..923a23d 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -9,12 +9,12 @@ use std::pin::pin;
use std::sync::LazyLock;
use std::vec::Vec;
-use atools::prelude::*;
use dsb::Cell;
use dsb::cell::Style;
use helix_core::Syntax;
use helix_core::syntax::{HighlightEvent, Loader};
use implicit_fn::implicit_fn;
+use itertools::Itertools;
use lsp_types::{
DocumentSymbol, Location, SemanticTokensLegend, SnippetTextEdit,
TextEdit,
@@ -45,35 +45,8 @@ pub use rope_ext::RopeExt;
use crate::sni::{Snippet, StopP};
use crate::text::hist::Action;
-
-pub const fn color_(x: &str) -> [u8; 3] {
- let x = x.as_bytes().as_array::<7>().unwrap();
- color(&x)
-}
-
-pub const fn set_a(x: [u8; 3], to: f32) -> [u8; 3] {
- x.map(const |x| (((x as f32 / 255.0) * to) * 255.0) as u8)
-}
-pub const fn color<const N: usize>(x: &[u8; N]) -> [u8; (N - 1) / 2]
-where
- [(); N - 1]:,
- [(); (N - 1) % 2 + usize::MAX]:,
-{
- let x = x.tail();
- let parse = x.map(const |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>();
- parse.map(const |[a, b]| a * 16 + b)
-}
-
-macro_rules! col {
- ($x:literal) => {{
- const __N: usize = $x.len();
- const { crate::text::color($x.as_bytes().as_array::<__N>().unwrap()) }
- }};
- ($($x:literal),+)=> {{
- ($(crate::text::col!($x),)+)
- }};
-}
-
+pub mod color;
+pub use color::*;
pub fn deserialize_from_string<'de, D: serde::de::Deserializer<'de>>(
de: D,
) -> Result<Rope, D::Error> {
@@ -284,6 +257,11 @@ impl TextArea {
m.position -= r.len() as u32;
self.inlays.insert(m);
}
+ self.cursor.each(|x| {
+ if *x >= self.rope.len_chars() {
+ x.position = self.rope.len_chars();
+ }
+ });
self.tokens.iter_mut().for_each(|d| d.manip(manip));
Ok(())
}
@@ -645,7 +623,7 @@ impl TextArea {
.and_then(|x| LOADER.language_for_filename(x))
.unwrap_or_else(|| LOADER.language_for_name("rust").unwrap());
- let s = self.rope.line_to_char(self.vo);
+ let Ok(s) = self.rope.try_line_to_char(self.vo) else { return };
let e = self
.rope
.try_line_to_char(self.vo + self.r * self.c)
@@ -653,7 +631,20 @@ impl TextArea {
for ((x1, y1), (x2, y2), s, _) in std::iter::from_coroutine(pin!(
hl(language, &self.rope, s as u32..e as u32, self.c)
)) {
- cell.get_range((x1, y1), (x2, y2)).for_each(|x| x.style |= s);
+ for (y, g) in cell
+ .range((x1, y1), (x2, y2))
+ .chunk_by(|x| x.1)
+ .into_iter()
+ {
+ let mut g = g.peekable();
+ let p = *g.peek().unwrap();
+ for (x, _) in
+ self.reverse_source_map(y).unwrap().skip(p.0).zip(g)
+ {
+ // println!("{x} {y} = {s:?}");
+ cell.get((x, y)).map(|x| x.style |= s);
+ }
+ }
}
// let mut highlight_stack = Vec::with_capacity(8);
@@ -765,6 +756,7 @@ impl TextArea {
apply: impl FnOnce((usize, usize), &Self, Output),
path: Option<&Path>,
leg: Option<&SemanticTokensLegend>,
+ l: Option<Language>,
) {
let (c, r) = (self.c, self.r);
let mut cells = Output {
@@ -786,11 +778,28 @@ impl TextArea {
// };
// (self.l().max(r) + r - 1) * c
// ];
+
+ if leg.is_none()
+ || self.tokens.is_empty()
+ || l.is_some_and(|l| {
+ LOADER.language(l).config().language_servers.iter().any(
+ |x| {
+ LOADER
+ .language_server_configs()
+ .get(&x.name)
+ .is_some_and(|x| x.requires_tree_sitting)
+ },
+ )
+ })
+ {
+ println!("treesit");
+ self.tree_sit(path, &mut cells);
+ }
let lns = self.vo..self.vo + r;
let mut tokens = self.tokens.iter();
- let mut curr: Option<&TokenD> = tokens.next();
+ let mut curr = tokens.next();
for (l, y) in lns.clone().map(self.source_map(_)).zip(lns) {
- for (e, x) in l
+ 'out: for (e, x) in l
.coerce()
.skip(self.ho)
// .flat_map(|x| x.chars().skip(self.ho))
@@ -800,7 +809,10 @@ impl TextArea {
if e.c() != '\n' {
cells.get((x + self.ho, y)).unwrap().letter =
Some(e.c());
- cells.get((x + self.ho, y)).unwrap().style = match e {
+ let s =
+ &mut cells.get((x + self.ho, y)).unwrap().style;
+
+ *s = match e {
Mapping::Char(_, _, abspos)
if let Some(leg) = leg =>
{
@@ -820,13 +832,19 @@ impl TextArea {
.contains(&(abspos as _))
{
curr.style(leg)
+ } else if *s
+ != Style::new(crate::BG, crate::BG)
+ {
+ continue 'out;
} else {
Style::new(crate::FG, crate::BG)
}
}
}
- Mapping::Char(..) =>
+ Mapping::Char(..)
+ if *s == Style::new(crate::BG, crate::BG) =>
Style::new(crate::FG, crate::BG),
+ Mapping::Char(..) => continue,
Mapping::Fake(Marking { .. }, ..) => Style::new(
const { color_("#536172") },
crate::BG,
@@ -850,9 +868,6 @@ impl TextArea {
// arc_swap::Guard<Arc<Box<[SemanticToken]>>>,
// &SemanticTokensLegend,
// )>;
- if leg.is_none() || self.tokens.is_empty() {
- self.tree_sit(path, &mut cells);
- }
if let Some(tabstops) = &self.tabstops {
for [a, b] in
tabstops.stops.iter().skip(tabstops.index - 1).flat_map(
@@ -1058,7 +1073,10 @@ pub fn is_word(r: char) -> bool {
matches!(r, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')
}
pub static LOADER: LazyLock<Loader> = LazyLock::new(|| {
- let x = helix_core::config::default_lang_loader();
+ let default_config = include_str!("../languages.toml");
+ let default_config = toml::from_str(default_config)
+ .expect("Could not parse built-in languages.toml to valid toml");
+ let x = Loader::new(default_config).unwrap();
x.set_scopes(theme_treesitter::NAMES.map(|x| x.to_string()).to_vec());
// x.languages().for_each(|(_, x)| {