A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/com.rs | 172 | ||||
| -rw-r--r-- | src/main.rs | 38 | ||||
| -rw-r--r-- | src/text.rs | 15 |
4 files changed, 150 insertions, 77 deletions
@@ -17,7 +17,7 @@ tree-sitter = "0.25.0" car = "0.1.2" memchr = "2.7.5" lower = "0.2.0" -amap = "0.1.3" +amap = { version = "0.1.3", path = "../amap" } run_times = "0.1.0" array_chunks = "1.0.0" rust-fsm = { version = "0.8.0", path = "../rust-fsm/rust-fsm", features = [ @@ -1,4 +1,3 @@ -use std::collections::HashSet; use std::mem::MaybeUninit; use std::sync::LazyLock; @@ -9,16 +8,46 @@ use fimg::Image; use itertools::Itertools; use lsp_types::*; -use crate::text::color; -use crate::{Complete, FG}; +use crate::FG; +use crate::text::{color, color_, set_a}; -pub fn s(completion: &Complete, c: usize, filter: &str) -> Vec<Cell> { - let mut out = vec![]; - let x = &completion.r; - let y = match x { - CompletionResponse::Array(x) => x, - CompletionResponse::List(x) => &x.items, - }; +#[derive(Debug)] +pub struct Complete { + pub r: CompletionResponse, + pub start: usize, + pub selection: usize, + pub vo: usize, +} +impl Complete { + pub fn next(&mut self, f: &str) { + let n = filter(self, f).count(); + self.selection += 1; + if self.selection == n { + self.vo = 0; + self.selection = 0; + } + if self.selection >= self.vo + N { + self.vo += 1; + } + } + + pub fn back(&mut self, f: &str) { + let n = filter(self, f).count(); + if self.selection == 0 { + self.vo = n - N; + self.selection = n - 1; + } else { + self.selection -= 1; + if self.selection < self.vo { + self.vo -= 1; + } + } + } +} +fn score<'a>( + x: impl Iterator<Item = &'a CompletionItem>, + filter: &'_ str, +) -> impl Iterator<Item = (u32, &'a CompletionItem, Vec<u32>)> { #[thread_local] static mut MATCHER: LazyLock<nucleo::Matcher> = LazyLock::new(|| nucleo::Matcher::new(nucleo::Config::DEFAULT)); @@ -28,37 +57,50 @@ pub fn s(completion: &Complete, c: usize, filter: &str) -> Vec<Cell> { nucleo::pattern::CaseMatching::Smart, nucleo::pattern::Normalization::Smart, ); - dbg!(filter); - let mut i = y - .iter() - .filter(|y| { - filter.is_empty() - || y.filter_text - .as_deref() - .unwrap_or(&y.label) - .chars() - .collect::<HashSet<_>>() - .intersection(&filter.chars().collect()) - .count() - > 0 - }) - .map(|y| { - let mut utf32 = vec![]; + x.map(move |y| { + let mut utf32 = vec![]; - let hay = y.filter_text.as_deref().unwrap_or(&y.label); - let mut indices = vec![]; - let score = p - .indices( - nucleo::Utf32Str::new(hay, &mut utf32), - unsafe { &mut *MATCHER }, - &mut indices, - ) - .unwrap_or(0); - indices.sort_unstable(); - indices.dedup(); + let hay = y.filter_text.as_deref().unwrap_or(&y.label); + let mut indices = vec![]; + let score = p + .indices( + nucleo::Utf32Str::new(hay, &mut utf32), + unsafe { &mut *MATCHER }, + &mut indices, + ) + .unwrap_or(0); + indices.sort_unstable(); + indices.dedup(); + + (score, y, indices) + }) +} +fn filter<'a>( + completion: &'a Complete, + filter: &'_ str, +) -> impl Iterator<Item = &'a CompletionItem> { + let x = &completion.r; + let y = match x { + CompletionResponse::Array(x) => x, + CompletionResponse::List(x) => &x.items, + }; + y.iter().filter(|y| { + filter.is_empty() + || y.filter_text + .as_deref() + .unwrap_or(&y.label) + .chars() + .any(|x| filter.chars().contains(&x)) + // .collect::<HashSet<_>>() + // .intersection(&filter.chars().collect()) + // .count() + // > 0 + }) +} - (score, y, indices) - }) +pub fn s(completion: &Complete, c: usize, f: &str) -> Vec<Cell> { + let mut out = vec![]; + let i = score(filter(completion, f), f) .sorted_by_key(|x| x.0) .rev() .zip(0..) @@ -130,32 +172,36 @@ fn r( let ds: Style = Style { bg: bg, color: FG, flags: 0 }; let d: Cell = Cell { letter: None, style: ds }; let mut b = vec![d; c]; - let ty = match x.kind { - Some(CompletionItemKind::TEXT) => " ", - Some( - CompletionItemKind::METHOD | CompletionItemKind::FUNCTION, - ) => "λ ", - Some(CompletionItemKind::CONSTRUCTOR) => "->", - Some(CompletionItemKind::FIELD) => "x.", - Some(CompletionItemKind::VARIABLE) => "x", - Some(CompletionItemKind::MODULE) => "::", - Some(CompletionItemKind::PROPERTY) => "x.", - Some(CompletionItemKind::VALUE) => "4 ", - Some(CompletionItemKind::ENUM) => "u", - Some(CompletionItemKind::SNIPPET) => "! ", - Some(CompletionItemKind::INTERFACE) => "t ", - Some(CompletionItemKind::REFERENCE) => "& ", - Some(CompletionItemKind::CONSTANT) => "N ", - Some(CompletionItemKind::STRUCT) => "X{", - Some(CompletionItemKind::OPERATOR) => "+ ", - Some(CompletionItemKind::TYPE_PARAMETER) => "T ", - Some(CompletionItemKind::KEYWORD) => " ", - - _ => " ", + const MAP: [([u8; 3], [u8; 3], &str); 26] = { + car::map!( + amap::amap! { + const { CompletionItemKind::TEXT.0 as usize } => ("#9a9b9a", " "), + const { CompletionItemKind::METHOD.0 as usize } | const { CompletionItemKind::FUNCTION.0 as usize } => ("#FFD173", "λ "), + const { CompletionItemKind::CONSTRUCTOR.0 as usize } => ("#FFAD66", "->"), + const { CompletionItemKind::FIELD.0 as usize } => ("#E06C75", "x."), + const { CompletionItemKind::VARIABLE.0 as usize } => ("#E06C75", "x "), + const { CompletionItemKind::MODULE.0 as usize } => ("#D5FF80", "::"), + const { CompletionItemKind::PROPERTY.0 as usize } => ("#e6e1cf", "x."), + const { CompletionItemKind::VALUE.0 as usize } => ("#DFBFFF", "4 "), + const { CompletionItemKind::ENUM.0 as usize } => ("#73b9ff", "u"), + const { CompletionItemKind::ENUM_MEMBER.0 as usize } => ("#73b9ff", ":"), + const { CompletionItemKind::SNIPPET.0 as usize } => ("#9a9b9a", "! "), + const { CompletionItemKind::INTERFACE.0 as usize } => ("#E5C07B", "t "), + const { CompletionItemKind::REFERENCE.0 as usize } => ("#9a9b9a", "& "), + const { CompletionItemKind::CONSTANT.0 as usize } => ("#DFBFFF", "N "), + const { CompletionItemKind::STRUCT.0 as usize } => ("#73D0FF", "X{"), + const { CompletionItemKind::OPERATOR.0 as usize } => ("#F29E74", "+ "), + const { CompletionItemKind::TYPE_PARAMETER.0 as usize } => ("#9a9b9a", "T "), + const { CompletionItemKind::KEYWORD.0 as usize } => ("#FFAD66", "as"), + _ => ("#9a9b9a", " ") + }, + |(x, y)| (set_a(color_(x), 0.5), color_(x), y) + ) }; + let (bgt, col, ty) = + MAP[x.kind.unwrap_or(CompletionItemKind(50)).0 as usize]; b.iter_mut().zip(ty.chars()).for_each(|(x, c)| { - *x = Style { bg: T_BG, color: [154, 155, 154], ..default() } - .basic(c) + *x = Style { bg: bgt, color: col, flags: Style::BOLD }.basic(c) }); let i = &mut b[2..]; diff --git a/src/main.rs b/src/main.rs index b84dda5..68d46fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ // this looks pretty good though #![feature(tuple_trait, unboxed_closures, fn_traits)] #![feature( + const_convert, + const_result_trait_fn, thread_local, result_option_map_or_default, iter_intersperse, @@ -474,11 +476,7 @@ r:x,start:c,selection:0,vo:0, ) }; if let CompletionState::Complete(Some(ref x,),_) = complete { - let c = com::s(x, 40,&if matches!(text.rope.get_char(text.cursor-1), Some('.' | ':')) { - "".to_string() - } else { - text.rope.slice(text.word_left_p()..text.cursor).chars().collect::<String>() - }); + let c = com::s(x, 40,&filter(&text)); let ppem = 20.0; let met = FONT.metrics(&[]); @@ -861,6 +859,16 @@ RUNNING.remove(&hover,&RUNNING.guard()); let CompletionState::Complete(c, x) = &mut complete else { panic!()}; *x = Some((h,c.as_ref().map(|x|x.start).or(x.as_ref().map(|x|x.1)).unwrap_or(text.cursor))); } + Some(CDo::SelectNext) => { + let CompletionState::Complete(Some(c), x) = &mut complete else { panic!()}; + c.next(&filter(&text)); + } + Some(CDo::SelectPrevious) => { + let CompletionState::Complete(Some(c), x) = &mut complete else { panic!()}; + c.back(&filter(&text)); + } + Some(CDo::Abort(())) => {} + None => {return}, _ => panic!(), }; @@ -1231,8 +1239,8 @@ rust_fsm::state_machine! { None => K(_) => _, // when - Complete((_x,_y)) => K(Key::Named(NamedKey::Tab) if shift()) => _ [SelectPrevious], - Complete((_x,_y)) => K(Key::Named(NamedKey::Tab)) => _ [SelectNext], + Complete((Some(_x),_y)) => K(Key::Named(NamedKey::Tab) if shift()) => _ [SelectPrevious], + Complete((Some(_x),_y)) => K(Key::Named(NamedKey::Tab)) => _ [SelectNext], // exit cases Complete((_x, None)) => Click => None, @@ -1245,15 +1253,19 @@ rust_fsm::state_machine! { task.map(|(task, _)| task.abort()); x })] } +use com::Complete; impl Default for CompletionState { fn default() -> Self { Self::None } } -#[derive(Debug)] -struct Complete { - r: CompletionResponse, - start: usize, - selection: usize, - vo: usize, +fn filter(text: &TextArea) -> String { + if matches!(text.rope.get_char(text.cursor - 1), Some('.' | ':')) { + "".to_string() + } else { + text.rope + .slice(text.word_left_p()..text.cursor) + .chars() + .collect::<String>() + } } diff --git a/src/text.rs b/src/text.rs index 13edb6e..e37c8c7 100644 --- a/src/text.rs +++ b/src/text.rs @@ -12,6 +12,7 @@ use atools::prelude::*; use diff_match_patch_rs::{DiffMatchPatch, Patches}; use dsb::Cell; use dsb::cell::Style; +use fimg::pixels::convert::PFrom; use helix_core::Syntax; use helix_core::syntax::{HighlightEvent, Loader}; use implicit_fn::implicit_fn; @@ -147,6 +148,20 @@ const fn of(x: &'static str) -> usize { panic!() } +pub const fn color_(x: &str) -> [u8; 3] { + let Some([_, x @ ..]): Option<[u8; 7]> = x.as_bytes().try_into().ok() + else { + panic!() + }; + color(x) +} +pub const fn set_a([a, b, c]: [u8; 3], to: f32) -> [u8; 3] { + [ + (((a as f32 / 255.0) * to) * 255.0) as u8, + (((b as f32 / 255.0) * to) * 255.0) as u8, + (((c as f32 / 255.0) * to) * 255.0) as u8, + ] +} pub const fn color(x: [u8; 6]) -> [u8; 3] { car::map!( car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(), |