A simple CPU rendered GUI IDE experience.
support nested document symbols (we flatten em)
bendn 6 days ago
parent b8f37cd · commit 64a2a6e
-rw-r--r--Cargo.toml1
-rw-r--r--src/act.rs2
-rw-r--r--src/com.rs140
-rw-r--r--src/edi.rs62
-rw-r--r--src/edi/st.rs4
-rw-r--r--src/lsp.rs38
-rw-r--r--src/main.rs3
-rw-r--r--src/menu.rs90
-rw-r--r--src/sym.rs132
9 files changed, 260 insertions, 212 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 46a3063..b59988a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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
diff --git a/src/act.rs b/src/act.rs
index eb2ccf6..3912c65 100644
--- a/src/act.rs
+++ b/src/act.rs
@@ -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;
diff --git a/src/com.rs b/src/com.rs
index 0c35613..e157787 100644
--- a/src/com.rs
+++ b/src/com.rs
@@ -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() })
- }
-}
diff --git a/src/edi.rs b/src/edi.rs
index 0777b21..8014348 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -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],
diff --git a/src/lsp.rs b/src/lsp.rs
index 6190d72..40df88a 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -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()
+ }
+}
diff --git a/src/sym.rs b/src/sym.rs
index 283789c..327e3e7 100644
--- a/src/sym.rs
+++ b/src/sym.rs
@@ -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 {
""
};