A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/act.rs | 2 | ||||
| -rw-r--r-- | src/com.rs | 140 | ||||
| -rw-r--r-- | src/edi.rs | 62 | ||||
| -rw-r--r-- | src/edi/st.rs | 4 | ||||
| -rw-r--r-- | src/lsp.rs | 38 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/menu.rs | 90 | ||||
| -rw-r--r-- | src/sym.rs | 132 |
9 files changed, 260 insertions, 212 deletions
@@ -66,6 +66,7 @@ git2 = "0.20.4" imara-diff = "0.2.0" vecto = "0.1.1" rangemap = { version = "1.7.1", features = ["const_fn", "nightly", "serde1"] } +itern = "0.1.1" [profile.dev.package] rust-analyzer.opt-level = 3 @@ -21,7 +21,7 @@ pub struct CodeActions { pub vo: usize, } use crate::FG; -use crate::com::{back, next}; +use crate::menu::{back, next}; use crate::text::{col, set_a}; const N: usize = 13; @@ -1,16 +1,14 @@ -use std::cmp::Reverse; +use std::borrow::Cow; use std::iter::repeat; -use std::mem::MaybeUninit; -use std::sync::LazyLock; use Default::default; use dsb::Cell; use dsb::cell::Style; -use itertools::Itertools; use lsp_types::*; use serde::{Deserialize, Serialize}; use crate::FG; +use crate::menu::{Key, back, filter, next, score}; use crate::text::{col, color_, set_a}; #[derive(Serialize, Deserialize)] @@ -29,29 +27,6 @@ impl std::fmt::Debug for Complete { .finish() } } -#[lower::apply(saturating)] -pub fn next<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { - *sel += 1; - if *sel == n { - *vo = 0; - *sel = 0; - } - if *sel >= *vo + N { - *vo += 1; - } -} -#[lower::apply(saturating)] -pub fn back<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { - if *sel == 0 { - *vo = n - N; - *sel = n - 1; - } else { - *sel -= 1; - if *sel < *vo { - *vo -= 1; - } - } -} impl Complete { pub fn next(&mut self, f: &str) { let n = filter_c(self, f).count(); @@ -67,71 +42,17 @@ impl Complete { back::<N>(n, &mut self.selection, &mut self.vo); } } - -pub fn score<'a, T: 'a>( - x: impl Iterator<Item = &'a T>, - f: impl Fn(&'a T) -> &'a str, - filter: &'_ str, -) -> Vec<(u32, &'a 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| { - let mut utf32 = vec![]; - // std::env::args().nth(1).unwrap().as_bytes().fi .fold(0, |acc, x| acc * 10 + x - b'0'); - let hay = f(y); - 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 +impl<'a> Key<'a> for &'a CompletionItem { + fn key(&self) -> impl Into<Cow<'a, str>> { + self.filter_text.as_deref().unwrap_or(&self.label) + } } -fn com_as_str(y: &CompletionItem) -> &str { - y.filter_text.as_deref().unwrap_or(&y.label) -} fn score_c<'a>( x: impl Iterator<Item = &'a CompletionItem>, filter: &'_ str, ) -> Vec<(u32, &'a CompletionItem, Vec<u32>)> { - score(x, com_as_str, filter) -} - -pub fn filter<'a, T: 'a>( - i: impl Iterator<Item = &'a T>, - f: impl Fn(&'a T) -> &'a str, - filter: &'_ str, -) -> impl Iterator<Item = &'a T> { - i.filter(move |y| { - filter.is_empty() - || f(y).chars().any(|x| filter.chars().contains(&x)) - // .collect::<HashSet<_>>() - // .intersection(&filter.chars().collect()) - // .count() - // > 0 - }) + score(x, filter) } fn filter_c<'a>( @@ -143,7 +64,7 @@ fn filter_c<'a>( CompletionResponse::Array(x) => x, CompletionResponse::List(x) => &x.items, }; - filter(y.iter(), com_as_str, f) + filter(y.iter(), f) } pub fn s(completion: &Complete, c: usize, f: &str) -> Vec<Cell> { @@ -339,48 +260,3 @@ fn t() { // println!("{:?}", now.elapsed()); x.as_ref().save("x"); } - -pub struct Dq<T, const N: usize> { - arr: [MaybeUninit<T>; N], - front: u8, - len: u8, -} - -impl<T: Copy, const N: usize> Dq<T, N> { - pub fn new(first: T) -> Self { - let mut dq = Dq { - arr: [const { MaybeUninit::uninit() }; N], - front: 0, - len: 1, - }; - dq.arr[0].write(first); - dq - } - - pub fn first(&mut self) -> T { - unsafe { - self.arr.get_unchecked(self.front as usize).assume_init() - } - } - - pub fn push_front(&mut self, elem: T) { - // sub 1 - match self.front { - 0 => self.front = N as u8 - 1, - n => self.front = n - 1, - } - self.len += 1; - unsafe { - self.arr.get_unchecked_mut(self.front as usize).write(elem) - }; - } - - pub fn iter(&self) -> impl Iterator<Item = T> + '_ { - self.arr - .iter() - .cycle() - .skip(self.front as _) - .take((self.len as usize).min(N)) - .map(|x| unsafe { x.assume_init() }) - } -} @@ -32,7 +32,7 @@ use crate::lsp::{ self, Anonymize, Client, Map_, PathURI, RedrawAfter, RequestError, Rq, }; use crate::meta::META; -use crate::sym::{Symbols, SymbolsType}; +use crate::sym::{Symbols, SymbolsList, SymbolsType}; use crate::text::cursor::{Ronge, ceach}; use crate::text::hist::{ClickHistory, Hist}; use crate::text::{ @@ -431,7 +431,7 @@ impl Editor { x.poll( |x, (_, p)| { let Some(p) = p else { unreachable!() }; - x.ok().map(|r| sym::Symbols { + x.ok().flatten().map(|r| sym::Symbols { r, selection: 0, vo: 0, @@ -899,10 +899,19 @@ impl Editor { } Some(Do::Symbols) => if let Some(lsp) = lsp!(self) { - let mut q = - Rq::new(lsp.runtime.spawn(window.redraw_after( - lsp.symbols("".into()).map(|x| x.anonymize()), - ))); + let mut q = Rq::new( + lsp.runtime.spawn( + window.redraw_after( + lsp.workspace_symbols("".into()) + .map(|x| x.anonymize()) + .map(|x| { + x.map(|x| { + x.map(SymbolsList::Workspace) + }) + }), + ), + ), + ); q.result = Some(Symbols::new(self.tree.as_deref().unwrap())); self.state = State::Symbols(q); @@ -920,7 +929,10 @@ impl Editor { *request = Some(( DropH::new(lsp.runtime.spawn( window.redraw_after(async move { - lsp.document_symbols(&p).await.anonymize() + lsp.document_symbols(&p) + .await + .anonymize() + .map(|x| x.map(SymbolsList::Document)) }), )), (), @@ -947,10 +959,10 @@ impl Editor { DropH::new( lsp.runtime.spawn( window.redraw_after( - lsp.symbols( + lsp.workspace_symbols( x.tedit.rope.to_string(), ) - .map(|x| x.anonymize()), + .map(|x| x.anonymize().map(|x| x.map(SymbolsList::Workspace))), ), ), ), @@ -986,22 +998,28 @@ impl Editor { else { unreachable!() }; - let x = x.sel().clone(); + let x = x.sel(); if let Err(e) = try bikeshed anyhow::Result<()> { - let f = x - .location - .uri - .to_file_path() - .map_err(|()| anyhow::anyhow!("dammit"))? - .canonicalize()?; - self.state = State::Default; - self.requests.complete = CompletionState::None; - if Some(&f) != self.origin.as_ref() { - self.open(&f, window.clone())?; - } + let r = match x.at { + sym::GoTo::Loc(x) => { + let x = x.clone(); + let f = x + .uri + .to_file_path() + .map_err(|()| anyhow::anyhow!("dammit"))? + .canonicalize()?; + self.state = State::Default; + self.requests.complete = CompletionState::None; + if Some(&f) != self.origin.as_ref() { + self.open(&f, window.clone())?; + } + x.range + } + sym::GoTo::R(range) => range, + }; let p = self .text - .l_position(x.location.range.start) + .l_position(r.start) .ok_or(anyhow::anyhow!("rah"))?; if p != 0 { self.text.cursor.just(p, &self.text.rope); diff --git a/src/edi/st.rs b/src/edi/st.rs index 20fcfb3..4c3bab3 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -8,7 +8,7 @@ use winit::event::MouseButton; use winit::keyboard::{Key, NamedKey, SmolStr}; use crate::lsp::{AQErr, Rq, RqS}; -use crate::sym::Symbols; +use crate::sym::{Symbols, SymbolsList}; use crate::text::TextArea; use crate::{ BoolRequest, CLICKING, InputRequest, act, alt, ctrl, handle, shift, @@ -75,7 +75,7 @@ Symbols(Rq { result: Some(_x), request: _rq }) => { K(Key::Named(Enter)) => _ [SymbolsSelect], K(Key::Named(Escape)) => Default, }, -Symbols(Rq::<Symbols, Vec<SymbolInformation>, (), AQErr> => _rq) => { +Symbols(Rq::<Symbols, Option<SymbolsList>, (), AQErr> => _rq) => { K(Key::Character(x) if x == "d" && ctrl()) => _ [SwitchType], // crahs cond methinks K(Key::Named(Escape)) => Default, K(_) => _ [SymbolsHandleKey], @@ -425,7 +425,7 @@ impl Client { p: &Path, ) -> impl Future< Output = Result< - Vec<SymbolInformation>, + Option<DocumentSymbolResponse>, RequestError<lsp_request!("textDocument/documentSymbol")>, >, > { @@ -438,26 +438,13 @@ impl Client { ) .unwrap() .0 - .map(|x| { - x.map(|x| { - x.map(|x| { - // std::fs::write("syms", serde_json::to_string_pretty(&x).unwrap()); - match x { - DocumentSymbolResponse::Flat(x) => x, - DocumentSymbolResponse::Nested(_) => - unreachable!(), - } - }) - .unwrap_or_default() - }) - }) } - pub fn symbols( + pub fn workspace_symbols( &'static self, f: String, ) -> impl Future< Output = Result< - Vec<SymbolInformation>, + Option<WorkspaceSymbolResponse>, RequestError<lsp_request!("workspace/symbol")>, >, > { @@ -471,19 +458,6 @@ impl Client { ) .unwrap() .0 - .map(|x| { - x.map(|x| { - x.map(|x| { - // std::fs::write("syms", serde_json::to_string_pretty(&x).unwrap()); - match x { - WorkspaceSymbolResponse::Flat(x) => x, - WorkspaceSymbolResponse::Nested(_) => - unreachable!(), - } - }) - .unwrap_or_default() - }) - }) } pub fn matching_brace<'a>( &'static self, @@ -695,6 +669,12 @@ pub fn run( inlay_hint: Some(InlayHintClientCapabilities { dynamic_registration: None, resolve_support: Some(InlayHintResolveClientCapabilities { properties: vec!["textEdits".into(), "tooltip".into(), "label.tooltip".into(), "label.command".into()], }) }), + document_symbol: Some(DocumentSymbolClientCapabilities { + tag_support: Some(TagSupport { value_set: SymbolTag::ALL.to_vec() }), + symbol_kind: Some(SymbolKindCapability { value_set: Some(SymbolKind::ALL.to_vec()) }), + hierarchical_document_symbol_support: Some(true), + ..default() + }), definition: Some(GotoCapability { link_support: Some(true), ..default() }), code_action: Some( CodeActionClientCapabilities { diff --git a/src/main.rs b/src/main.rs index e9e69b9..65be56b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,9 +79,10 @@ use crate::edi::st::*; use crate::lsp::RqS; use crate::text::{TextArea, col, is_word}; mod bar; -pub mod com; +mod com; pub mod hov; mod lsp; +pub mod menu; mod sig; mod sni; mod text; diff --git a/src/menu.rs b/src/menu.rs new file mode 100644 index 0000000..5c2d092 --- /dev/null +++ b/src/menu.rs @@ -0,0 +1,90 @@ +use std::borrow::Cow; +use std::cmp::Reverse; +use std::sync::LazyLock; + +use itertools::Itertools; + +#[lower::apply(saturating)] +pub fn next<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { + *sel += 1; + if *sel == n { + *vo = 0; + *sel = 0; + } + if *sel >= *vo + N { + *vo += 1; + } +} +#[lower::apply(saturating)] +pub fn back<const N: usize>(n: usize, sel: &mut usize, vo: &mut usize) { + if *sel == 0 { + *vo = n - N; + *sel = n - 1; + } else { + *sel -= 1; + if *sel < *vo { + *vo -= 1; + } + } +} + +pub fn score<'a, T: Key<'a>>( + x: impl Iterator<Item = T>, + filter: &'_ str, +) -> 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| { + 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 filter<'a, T: Key<'a>>( + i: impl Iterator<Item = T>, + filter: &'_ str, +) -> impl Iterator<Item = T> { + i.filter(move |y| { + filter.is_empty() + || y.k().chars().any(|x| filter.chars().contains(&x)) + // .collect::<HashSet<_>>() + // .intersection(&filter.chars().collect()) + // .count() + // > 0 + }) +} + +pub trait Key<'a> { + fn key(&self) -> impl Into<Cow<'a, str>>; + fn k(&self) -> Cow<'a, str> { + self.key().into() + } +} @@ -1,24 +1,80 @@ +use std::collections::VecDeque; use std::iter::{chain, repeat}; use std::path::{Path, PathBuf}; use Default::default; use dsb::Cell; use dsb::cell::Style; +use itern::Iter3; use lsp_types::*; use crate::FG; -use crate::com::{back, filter, next, score}; +use crate::menu::{Key, back, filter, next, score}; use crate::text::{TextArea, col, color_, set_a}; #[derive(Debug, Default)] pub struct Symbols { - pub r: Vec<SymbolInformation>, + pub r: SymbolsList, pub tree: Vec<SymbolInformation>, pub tedit: TextArea, pub selection: usize, pub vo: usize, pub ty: SymbolsType, } +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum GoTo<'a> { + Loc(&'a Location), + R(Range), +} +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct UsedSI<'a> { + pub name: &'a str, + pub kind: SymbolKind, + pub tags: Option<&'a [SymbolTag]>, + pub at: GoTo<'a>, + pub right: Option<&'a str>, +} +impl<'a> From<&'a SymbolInformation> for UsedSI<'a> { + fn from( + SymbolInformation { + name, + kind, + tags, + location, + container_name, + .. + }: &'a SymbolInformation, + ) -> Self { + UsedSI { + name: &name, + kind: *kind, + tags: tags.as_deref(), + at: GoTo::Loc(location), + right: container_name.as_deref(), + } + } +} +impl<'a> From<&'a DocumentSymbol> for UsedSI<'a> { + fn from( + DocumentSymbol { + name, + detail, + kind, + tags, + range, + selection_range: _, + .. + }: &'a DocumentSymbol, + ) -> Self { + UsedSI { + name: &name, + kind: *kind, + tags: tags.as_deref(), + at: GoTo::R(*range), + right: detail.as_deref(), + } + } +} #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub enum SymbolsType { @@ -26,6 +82,16 @@ pub enum SymbolsType { #[default] Workspace, } +#[derive(Debug)] +pub enum SymbolsList { + Document(DocumentSymbolResponse), + Workspace(WorkspaceSymbolResponse), +} +impl Default for SymbolsList { + fn default() -> Self { + Self::Workspace(WorkspaceSymbolResponse::Flat(vec![])) + } +} const N: usize = 30; impl Symbols { pub fn new(tree: &[PathBuf]) -> Self { @@ -58,7 +124,7 @@ impl Symbols { back::<N>(n, &mut self.selection, &mut self.vo); } - pub fn sel(&self) -> &SymbolInformation { + pub fn sel(&self) -> UsedSI<'_> { let f = self.f(); score_c(filter_c(self, &f), &f)[self.selection].1 } @@ -123,39 +189,50 @@ impl Symbols { out } } + +impl<'a> Key<'a> for UsedSI<'a> { + fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> { + self.name + } +} fn score_c<'a>( - x: impl Iterator<Item = &'a SymbolInformation>, + x: impl Iterator<Item = UsedSI<'a>>, filter: &'_ str, -) -> Vec<(u32, &'a SymbolInformation, Vec<u32>)> { - score(x, sym_as_str, filter) +) -> Vec<(u32, UsedSI<'a>, Vec<u32>)> { + score(x, filter) } fn filter_c<'a>( - completion: &'a Symbols, + syms: &'a Symbols, f: &'_ str, -) -> impl Iterator<Item = &'a SymbolInformation> { - let x = &completion.r; +) -> impl Iterator<Item = UsedSI<'a>> { + let x = &syms.r; filter( - chain(&completion.tree, x).skip( - if completion.ty == SymbolsType::Document { - completion.tree.len() - } else { - 0 - }, - ), - sym_as_str, + match x { + SymbolsList::Document(DocumentSymbolResponse::Flat(x)) => + Iter3::A(x.iter().map(UsedSI::from)), + SymbolsList::Document(DocumentSymbolResponse::Nested(x)) => + Iter3::B(x.iter().flat_map(|x| gen move { + let mut q = VecDeque::with_capacity(12); + q.push_back(x); + while let Some(x) = q.pop_front() { + q.extend(x.children.iter().flatten()); + yield x.into(); + } + })), + SymbolsList::Workspace(WorkspaceSymbolResponse::Flat(x)) => + Iter3::C(chain(&syms.tree, x.iter()).map(UsedSI::from)), + _ => unreachable!("please no"), + }, f, ) } -fn sym_as_str(x: &SymbolInformation) -> &str { - &x.name -} fn charc(c: &str) -> usize { c.chars().count() } #[implicit_fn::implicit_fn] -fn r( - x: &SymbolInformation, +fn r<'a>( + x: UsedSI<'a>, workspace: &Path, c: usize, selected: bool, @@ -203,7 +280,7 @@ fn r( }); let i = &mut b[2..]; let qualifier = x - .container_name + .right .as_ref() .into_iter() // .flat_map(|x| &x.detail) @@ -211,9 +288,14 @@ fn r( let left = i.len() as i32 - (charc(&x.name) as i32 + qualifier.clone().count() as i32) - 3; - let loc = x.location.uri.to_file_path().unwrap(); + let loc = match x.at { + GoTo::Loc(x) => Some(x.uri.to_file_path().unwrap()), + GoTo::R(_) => None, + }; let locs = if sty == SymbolsType::Workspace { - loc.strip_prefix(workspace).unwrap_or(&loc).to_str().unwrap_or("") + loc.as_ref() + .and_then(|x| x.strip_prefix(workspace).unwrap_or(&x).to_str()) + .unwrap_or("") } else { "" }; |