A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | src/complete.rs | 4 | ||||
| -rw-r--r-- | src/edi/input_handlers/keyboard.rs | 14 | ||||
| -rw-r--r-- | src/edi/st.rs | 4 | ||||
| -rw-r--r-- | src/gotolist.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 19 | ||||
| -rw-r--r-- | src/menu.rs | 61 | ||||
| -rw-r--r-- | src/menu/generic.rs | 42 | ||||
| -rw-r--r-- | src/rnd.rs | 24 | ||||
| -rw-r--r-- | src/sym.rs | 13 |
9 files changed, 153 insertions, 36 deletions
diff --git a/src/complete.rs b/src/complete.rs index 11ded6c..655b4c8 100644 --- a/src/complete.rs +++ b/src/complete.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::FG; use crate::edi::{Editor, change, lsp}; use crate::lsp::Rq; -use crate::menu::{Key, back, charc, filter, next, score}; +use crate::menu::{Key, back, charc, filter, next, score, score_basic}; use crate::text::{SortTedits, col, color_, set_a}; #[derive(Serialize, Deserialize)] @@ -54,7 +54,7 @@ fn score_c<'a>( x: impl Iterator<Item = &'a CompletionItem>, filter: &'_ str, ) -> Vec<(u32, &'a CompletionItem, Vec<u32>)> { - score(x, filter) + score_basic(x, filter) } fn filter_c<'a>( diff --git a/src/edi/input_handlers/keyboard.rs b/src/edi/input_handlers/keyboard.rs index c789851..aaf3fa3 100644 --- a/src/edi/input_handlers/keyboard.rs +++ b/src/edi/input_handlers/keyboard.rs @@ -17,6 +17,7 @@ use winit::event::KeyEvent; use winit::keyboard::Key; use winit::window::Window; +use crate::Freq; use crate::edi::*; impl Editor { @@ -24,6 +25,7 @@ impl Editor { &mut self, event: KeyEvent, window: &mut Arc<dyn Window>, + freq: &mut Freq, ) -> ControlFlow<()> { let mut o: Option<Do> = self .state @@ -135,7 +137,7 @@ impl Editor { unreachable!() }; if let Some(Ok(crate::commands::Cmd::GoTo(Some(x)))) = - x.sel() + x.sel(None) { self.text.scroll_to_ln_centering(x as _); } @@ -189,7 +191,7 @@ impl Editor { unreachable!() }; x.next(); - if let Some(Ok(x)) = x.sel() + if let Some(Ok(x)) = x.sel(None) && Some(&*x.at.path) == self.origin.as_deref() { match x.at { @@ -209,7 +211,7 @@ impl Editor { unreachable!() }; x.back(); - if let Some(Ok(x)) = x.sel() + if let Some(Ok(x)) = x.sel(None) && Some(&*x.at.path) == self.origin.as_deref() { match x.at.at { @@ -223,7 +225,7 @@ impl Editor { } } Some(Do::SymbolsSelect(x)) => - if let Some(Ok(x)) = x.sel() + if let Some(Ok(x)) = x.sel(Some(freq)) && let Err(e) = self.go(x.at, window.clone()) { log::error!("alas! {e}") @@ -574,7 +576,7 @@ impl Editor { } } Some(Do::GTLSelect(x)) => - if let Some(Ok((g, _))) = x.sel() + if let Some(Ok((g, _))) = x.sel(None) && let Err(e) = self.go(g, window.clone()) { eprintln!("go-to-list select fail: {e}"); @@ -584,7 +586,7 @@ impl Editor { unreachable!() }; if let Some(Ok((GoTo { path: p, at: At::R(r) }, _))) = - x.sel() + x.sel(None) && Some(&*p) == self.origin.as_deref() { // let x = self.text.l_range(r).unwrap(); diff --git a/src/edi/st.rs b/src/edi/st.rs index ad3eec4..01a3474 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -135,7 +135,7 @@ Hovering(x) => { M(_) => _ [ClickedHover], }, Command(_) => K(Key::Named(Escape)) => Default, -Command(t) => K(Key::Named(Enter) if let Some(Ok(x)) = t.sel()) => Default [ProcessCommand((Commands, crate::commands::Cmd) => (t, x))], +Command(t) => K(Key::Named(Enter) if let Some(Ok(x)) = t.sel(None)) => Default [ProcessCommand((Commands, crate::commands::Cmd) => (t, x))], Command(t) => K(Key::Named(Enter)) => _, Command(mut t) => K(Key::Named(Tab) if shift()) => Command({ t.back();t }), Command(mut t) => K(Key::Named(Tab)) => Command({ t.next(); t }), @@ -151,7 +151,7 @@ Runnables(RqS::<crate::runnables::Runnables, rust_analyzer::lsp::ext::Runnables> K(Key::Named(Tab) if shift()) => Runnables({ x.next(); Rq { result: Some(x), request }}), K(Key::Named(ArrowDown)) => Runnables({ x.next(); Rq { result: Some(x), request }}), K(Key::Named(ArrowUp | Tab)) => Runnables({ x.back(); Rq { result: Some(x), request }}), - K(Key::Named(Enter) if let Some(Ok(x_)) = x.clone().sel()) => Default [Run(Runnable => x_.clone())], + K(Key::Named(Enter) if let Some(Ok(x_)) = x.clone().sel(None)) => Default [Run(Runnable => x_.clone())], K(k) => Runnables({ if let Some(_) = handle2(&k, &mut x.tedit, None) { x.selection = 0; x.vo = 0; diff --git a/src/gotolist.rs b/src/gotolist.rs index 1268dec..8f2b9e3 100644 --- a/src/gotolist.rs +++ b/src/gotolist.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::hash::Hash; use std::path::Path; use dsb::Cell; @@ -8,6 +9,7 @@ use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyOutgoingCall, Location, LocationLink, Range, }; +use rustc_hash::FxHashMap; use crate::FG; use crate::lsp::{Rq, RqS}; @@ -35,6 +37,7 @@ impl<'a> Key<'a> for (GoTo<'a>, Option<&'a str>) { // self.display().to_string() } } + impl MenuData for GTL { type Data = (Vec<(GoTo<'static>, Option<String>)>, Option<O>); @@ -110,6 +113,11 @@ pub struct GoTo<'a> { pub at: At, } +impl<'a> Hash for GoTo<'a> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.path.hash(state); + } +} impl From<Location> for GoTo<'static> { fn from(Location { uri, range }: Location) -> Self { Self { diff --git a/src/main.rs b/src/main.rs index e199e2e..f6fa339 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,12 @@ const_trait_impl, try_blocks )] -#![allow(incomplete_features, irrefutable_let_patterns, static_mut_refs, unexpected_cfgs)] +#![allow( + incomplete_features, + irrefutable_let_patterns, + static_mut_refs, + unexpected_cfgs +)] mod act; mod edi; mod error; @@ -45,6 +50,7 @@ mod runnables; mod sym; mod trm; +use std::any::TypeId; use std::fmt::{Debug, Display}; use std::hash::Hash; use std::mem::MaybeUninit; @@ -59,7 +65,9 @@ use fimg::Image; use libc::{atexit, signal}; use lsp::Rq; use lsp_types::*; + use rust_fsm::StateMachine; +use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use swash::FontRef; use winit::dpi::{PhysicalPosition, PhysicalSize}; @@ -112,7 +120,7 @@ extern "C" fn sigint(_: i32) { cleanup(); std::process::exit(12); } - +type Freq = FxHashMap<TypeId, FxHashMap<u64, u16>>; pub(crate) fn entry(event_loop: EventLoop) { unsafe { __ED.write(match Editor::new() { @@ -126,6 +134,8 @@ pub(crate) fn entry(event_loop: EventLoop) { assert_eq!(unsafe { atexit(cleanup) }, 0); unsafe { signal(libc::SIGINT, sigint as *const () as usize) }; let ed: &'static mut Editor = unsafe { __ED.assume_init_mut() }; + + let mut freq:Freq = default(); let ppem = 18.0; let ls = 20.0; // let ed = Box::leak(Box::new(ed)); @@ -139,7 +149,7 @@ pub(crate) fn entry(event_loop: EventLoop) { let mut cursor_position = (0, 0); let mut i = Image::build(1, 1).fill(BG); - let mut cells = vec![]; + let mut cells = vec![]; let mut w = match &mut ed.lsp { Some((.., c)) => c.take(), None => None, @@ -270,6 +280,7 @@ pub(crate) fn entry(event_loop: EventLoop) { cursor_position, &mut fonts, i.as_mut(), + &freq ); } @@ -329,7 +340,7 @@ pub(crate) fn entry(event_loop: EventLoop) { ) { return; } - if ed.keyboard(event, window).is_break() { + if ed.keyboard(event, window,&mut freq).is_break() { elwt.exit(); } window.request_redraw(); diff --git a/src/menu.rs b/src/menu.rs index 3c5f7a1..b619c03 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,10 +1,15 @@ pub mod generic; +use std::any::TypeId; use std::borrow::Cow; use std::cmp::Reverse; +use std::hash::Hash; use std::sync::LazyLock; use itertools::Itertools; +use crate::menu::generic::MenuData; +use crate::{Freq, hash}; + #[lower::apply(saturating)] pub fn next<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { *sel += 1; @@ -29,14 +34,62 @@ pub fn back<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { } } -pub fn score<'a, T: Key<'a>>( +#[thread_local] +static mut MATCHER: LazyLock<nucleo::Matcher> = + LazyLock::new(|| nucleo::Matcher::new(nucleo::Config::DEFAULT)); + +pub fn score<'a, T: Key<'a>, D: MenuData<Element<'a> = T> + 'static>( x: impl Iterator<Item = T>, filter: &'_ str, + freq: Option<&Freq>, ) -> Vec<(u32, T, Vec<u32>)> { - #[thread_local] - static mut MATCHER: LazyLock<nucleo::Matcher> = - LazyLock::new(|| nucleo::Matcher::new(nucleo::Config::DEFAULT)); + let p = nucleo::pattern::Pattern::parse( + filter, + nucleo::pattern::CaseMatching::Smart, + nucleo::pattern::Normalization::Smart, + ); + let mut v = x + .map(move |y| { + if let Some(f) = freq + && filter == "" + && let Some(f) = f.get(&TypeId::of::<D>()) + { + return ( + f.get(&D::hashed(&y).unwrap()) + .copied() + .unwrap_or_default() as u32, + y, + vec![], + ); + } + let mut utf32 = vec![]; + // std::env::args().nth(1).unwrap().as_bytes().fi .fold(0, |acc, x| acc * 10 + x - b'0'); + let hay = y.k(); + 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) + }) + .collect::<Vec<_>>(); + // std::fs::write( + // "com", + // v.iter().map(|x| x.1.label.clone() + "\n").collect::<String>(), + // ); + v.sort_by_key(|x| Reverse(x.0)); + v +} +pub fn score_basic<'a, T: Key<'a>>( + x: impl Iterator<Item = T>, + filter: &'_ str, +) -> Vec<(u32, T, Vec<u32>)> { let p = nucleo::pattern::Pattern::parse( filter, nucleo::pattern::CaseMatching::Smart, diff --git a/src/menu/generic.rs b/src/menu/generic.rs index ac815c2..51be7e7 100644 --- a/src/menu/generic.rs +++ b/src/menu/generic.rs @@ -1,9 +1,12 @@ +use std::any::TypeId; use std::fmt::Debug; +use std::hash::Hash; use std::path::Path; use Default::default; use dsb::Cell; +use crate::Freq; use crate::menu::{Key, back, filter, next, score}; use crate::text::TextArea; @@ -50,7 +53,7 @@ pub enum CorA { Complete, Accept, } -pub trait MenuData: Sized { +pub trait MenuData: Sized + 'static { const HEIGHT: usize = 30; type Data; type Element<'a>: Key<'a>; @@ -68,6 +71,12 @@ pub trait MenuData: Sized { fn should_complete<'a>(_m: &GenericMenu<Self>) -> bool { true } + fn hash<'b>(_x: &'b Self::Element<'_>) -> Option<impl Hash> { + None::<()> + } + fn hashed<'b>(x: &'b Self::Element<'_>) -> Option<u64> { + Self::hash(x).map(|x| crate::hash(&x)) + } fn gn<'a>( x: &'a Self::Data, ) -> impl Iterator<Item = Self::Element<'a>>; @@ -89,8 +98,9 @@ pub trait MenuData: Sized { fn score_c<'a>( x: impl Iterator<Item = Self::Element<'a>>, f: &str, + freq: Option<&Freq>, ) -> Vec<(u32, Self::Element<'a>, Vec<u32>)> { - score(x, f) + score::<_, Self>(x, f, freq) } fn f(m: &GenericMenu<Self>) -> String { @@ -98,7 +108,7 @@ pub trait MenuData: Sized { } } -impl<T: MenuData> GenericMenu<T> { +impl<T: MenuData + 'static> GenericMenu<T> { pub type I = T; pub fn should_render(&self) -> bool { T::should_complete(self) @@ -120,12 +130,27 @@ impl<T: MenuData> GenericMenu<T> { pub fn sel( &self, + fq: Option<&mut Freq>, ) -> Option<Result<<T as MenuData>::Element<'_>, T::E>> { let f = self.f(); - T::score_c(T::filter_c(&self.data, &f), &f) + T::score_c(T::filter_c(&self.data, &f), &f, fq.as_deref()) .try_remove(self.selection) .map(|(_, x, _)| T::map(&self, x)) + .inspect(|x| { + fq.map(|fq| { + _ = x.as_ref().inspect(|x| { + let x = T::hashed(x).expect( + "if calling with freq, please impl hash", + ); + + *fq.entry(TypeId::of::<T>()) + .or_default() + .entry(x) + .or_default() += 1; + }); + }); + }) } pub fn back(&mut self) @@ -135,10 +160,15 @@ impl<T: MenuData> GenericMenu<T> { let n = T::filter_c(&self.data, &self.f()).count(); next::<{ T::HEIGHT }>(n, &mut self.selection, &mut self.vo); } - pub fn cells(&self, c: usize, ws: &Path) -> Vec<Cell> { + pub fn cells( + &self, + c: usize, + ws: &Path, + freq: Option<&Freq>, + ) -> Vec<Cell> { let f = self.f(); let mut out = vec![]; - let v = T::score_c(T::filter_c(&self.data, &f), &f); + let v = T::score_c(T::filter_c(&self.data, &f), &f, freq); let vlen = v.len(); let i = v.into_iter().zip(0..vlen).skip(self.vo).take(T::HEIGHT).rev(); @@ -26,8 +26,8 @@ use crate::lsp::Rq; use crate::sym::UsedSI; use crate::text::{CoerceOption, RopeExt, TextArea, col, color_}; use crate::{ - BG, BORDER, CompletionAction, CompletionState, FG, FONT, complete, - filter, hash, sig, + BG, BORDER, CompletionAction, CompletionState, FG, FONT, Freq, + complete, filter, hash, sig, }; mod cell_buffer; @@ -46,6 +46,7 @@ pub fn render( cursor_position: (usize, usize), fonts: &mut dsb::Fonts, mut i: Image<&mut [u8], 3>, + freq: &Freq, ) { let text = &mut ed.text; let (cx, cy) = text.primary_cursor_visual(); @@ -111,7 +112,7 @@ pub fn render( |_text, mut f, y| { if let State::GoToL(menu) = &ed.state && let Some(Ok((GoTo { at: At::R(r), path }, _))) = - menu.sel() + menu.sel(None) && Some(&*path) == ed.origin.as_deref() { if (r.start.line..=r.end.line).contains(&(y as _)) { @@ -123,7 +124,7 @@ pub fn render( && let Some(Ok(UsedSI { at: GoTo { at: At::R(x), .. }, .. - })) = menu.sel() + })) = menu.sel(None) { if (x.start.line..=x.end.line).contains(&(y as _)) { f.style.fg = col!("#FFCC66"); @@ -490,13 +491,12 @@ pub fn render( + toy) as usize, ); - let left = if px + w as usize - > window.surface_size().width as usize - { - window.surface_size().width as usize - w as usize + let left = if px + w as usize > size.width as usize { + size.width as usize - w as usize } else { px }; + assert!(left + w <= size.width as usize); // let (w, h) = // dsb::size(&fonts.regular, ppem, ls, (columns, r)); @@ -839,22 +839,22 @@ pub fn render( } State::Command(x) if x.should_render() => { let ws = ed.workspace.as_deref().unwrap(); - let c = x.cells(50, ws); + let c = x.cells(50, ws, None); drawb(&c, 50); } State::Symbols(Rq { result: Some(x), .. }) => { let ws = ed.workspace.as_deref().unwrap(); - let c = x.cells(50, ws); + let c = x.cells(50, ws, Some(freq)); drawb(&c, 50); } State::Runnables(Rq { result: Some(x), .. }) => { let ws = ed.workspace.as_deref().unwrap(); - let c = x.cells(50, ws); + let c = x.cells(50, ws, None); drawb(&c, 50); } State::GoToL(y) => { let ws = ed.workspace.as_deref().unwrap(); - let c = y.cells(50, ws); + let c = y.cells(50, ws, None); drawb(&c, 50); } _ => {} @@ -77,6 +77,10 @@ impl MenuData for Symb { ) { r(x, workspace, c, selected, indices, to, sty) } + + fn hash<'b>(x: &'b Self::Element<'_>) -> Option<impl std::hash::Hash> { + Some(x) + } } pub type Symbols = GenericMenu<Symb>; @@ -88,6 +92,15 @@ pub struct UsedSI<'a> { pub at: GoTo<'a>, pub right: Option<&'a str>, } + +impl<'a> std::hash::Hash for UsedSI<'a> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.name.hash(state); + self.kind.0.hash(state); + self.at.hash(state); + self.right.hash(state); + } +} impl<'a> From<&'a SymbolInformation> for UsedSI<'a> { fn from( SymbolInformation { |