A simple CPU rendered GUI IDE experience.
-rw-r--r--src/complete.rs4
-rw-r--r--src/edi/input_handlers/keyboard.rs14
-rw-r--r--src/edi/st.rs4
-rw-r--r--src/gotolist.rs8
-rw-r--r--src/main.rs19
-rw-r--r--src/menu.rs61
-rw-r--r--src/menu/generic.rs42
-rw-r--r--src/rnd.rs24
-rw-r--r--src/sym.rs13
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();
diff --git a/src/rnd.rs b/src/rnd.rs
index ae50325..d6b112b 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -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);
}
_ => {}
diff --git a/src/sym.rs b/src/sym.rs
index 7d833ba..c569741 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -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 {