A simple CPU rendered GUI IDE experience.
generic menu
bendn 7 weeks ago
parent cae20b1 · commit 03e00e5
-rw-r--r--src/commands.rs14
-rw-r--r--src/edi.rs139
-rw-r--r--src/edi/st.rs20
-rw-r--r--src/lsp.rs43
-rw-r--r--src/main.rs3
-rw-r--r--src/rnd.rs80
-rw-r--r--src/runnables.rs139
7 files changed, 314 insertions, 124 deletions
diff --git a/src/commands.rs b/src/commands.rs
index 9e01796..31e7102 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -13,7 +13,7 @@ use rust_analyzer::lsp::ext::*;
use crate::FG;
use crate::edi::{Editor, lsp_m};
-use crate::lsp::{Anonymize, PathURI};
+use crate::lsp::{Anonymize, PathURI, Rq};
use crate::menu::charc;
use crate::menu::generic::{GenericMenu, MenuData};
use crate::text::{RopeExt, SortTedits, col, color_};
@@ -264,14 +264,10 @@ impl Editor {
self.open_loc(x, w);
}
Cmd::RARunnables => {
- let x = l
- .runtime
- .block_on(l.runnables(
- o,
- self.text.to_l_position(*self.text.cursor.first()),
- )?)
- .anonymize()?;
- // self.state = State::Runnables;
+ let p = self.text.to_l_position(*self.text.cursor.first());
+ let o = o.to_owned();
+ let x = l.runtime.spawn(l.runnables(&o, None)?);
+ self.state = crate::edi::st::State::Runnables(Rq::new(x));
}
_ => unimplemented!(),
}
diff --git a/src/edi.rs b/src/edi.rs
index d840540..7594977 100644
--- a/src/edi.rs
+++ b/src/edi.rs
@@ -35,6 +35,7 @@ use crate::lsp::{
self, Anonymize, Client, Map_, PathURI, RedrawAfter, RequestError, Rq,
};
use crate::meta::META;
+use crate::runnables::Runnables;
use crate::sym::{Symbols, SymbolsList, SymbolsType};
use crate::text::cursor::{Ronge, ceach};
use crate::text::hist::{ClickHistory, Hist};
@@ -411,7 +412,7 @@ impl Editor {
std::fs::write(self.origin.as_ref().unwrap(), &t).unwrap();
self.mtime = Self::modify(self.origin.as_deref());
}
- pub fn poll(&mut self) {
+ pub fn poll(&mut self, w: Option<&Arc<Window>>) {
let Some((l, ..)) = self.lsp else { return };
for rq in l.req_rx.try_iter() {
match rq {
@@ -431,11 +432,11 @@ impl Editor {
},
r,
);
- self.requests.document_highlights.poll(|x, _| x.ok(), r);
- self.requests.diag.poll(|x, _| x.ok().flatten(), r);
+ self.requests.document_highlights.poll_r(|x, _| x.ok(), r, w);
+ self.requests.diag.poll_r(|x, _| x.ok().flatten(), r, w);
if let CompletionState::Complete(rq) = &mut self.requests.complete
{
- rq.poll(
+ rq.poll_r(
|f, (c, _)| {
f.ok().flatten().map(|x| Complete {
r: x,
@@ -445,46 +446,63 @@ impl Editor {
})
},
r,
+ w,
);
};
-
- if let State::Symbols(x) = &mut self.state {
- x.poll(
- |x, (_, p)| {
- let Some(p) = p else { unreachable!() };
- x.ok().flatten().map(|r| sym::Symbols {
- data: (r, p.data.1, p.data.2),
- selection: 0,
- vo: 0,
- ..p
- })
- },
- &r,
- );
- }
- if let State::CodeAction(x) = &mut self.state {
- x.poll(
- |x, _| {
- let lems: Vec<CodeAction> = x
- .ok()??
- .into_iter()
- .map(|x| match x {
- CodeActionOrCommand::CodeAction(x) => x,
- _ => panic!("alas we dont like these"),
+ match &mut self.state {
+ State::Symbols(x) => {
+ x.poll_r(
+ |x, (_, p)| {
+ let Some(p) = p else { unreachable!() };
+ x.ok().flatten().map(|r| sym::Symbols {
+ data: (r, p.data.1, p.data.2),
+ selection: 0,
+ vo: 0,
+ ..p
})
- .collect();
- if lems.is_empty() {
- self.bar.last_action =
- "no code actions available".into();
- None
- } else {
- Some(act::CodeActions::new(lems))
- }
- },
- &r,
- );
+ },
+ r,
+ w,
+ );
+ }
+ State::CodeAction(x) => {
+ x.poll_r(
+ |x, _| {
+ let lems: Vec<CodeAction> = x
+ .ok()??
+ .into_iter()
+ .map(|x| match x {
+ CodeActionOrCommand::CodeAction(x) => x,
+ _ => panic!("alas we dont like these"),
+ })
+ .collect();
+ if lems.is_empty() {
+ self.bar.last_action =
+ "no code actions available".into();
+ None
+ } else {
+ Some(act::CodeActions::new(lems))
+ }
+ },
+ r,
+ w,
+ );
+ }
+ State::Runnables(x) => {
+ x.poll_r(
+ |x, ((), old)| {
+ Some(Runnables {
+ data: x.ok()?,
+ ..old.unwrap_or_default()
+ })
+ },
+ r,
+ w,
+ );
+ }
+ _ => {}
}
- self.requests.def.poll(
+ self.requests.def.poll_r(
|x, _| {
x.ok().flatten().and_then(|x| match &x {
GotoDefinitionResponse::Link([x, ..]) =>
@@ -492,13 +510,15 @@ impl Editor {
_ => None,
})
},
- &r,
+ r,
+ w,
);
- self.requests.semantic_tokens.poll(
+ self.requests.semantic_tokens.poll_r(
|x, _| x.ok().inspect(|x| self.text.set_toks(&x)),
&l.runtime,
+ w,
);
- self.requests.sig_help.poll(
+ self.requests.sig_help.poll_r(
|x, ((), y)| {
x.ok().flatten().map(|x| {
if let Some((old_sig, vo, max)) = y
@@ -510,18 +530,20 @@ impl Editor {
}
})
},
- &r,
+ r,
+ w,
);
- self.requests.hovering.poll(|x, _| x.ok().flatten(), &r);
- self.requests.git_diff.poll(|x, _| x.ok(), &r);
- self.requests.document_symbols.poll(
+ self.requests.hovering.poll_r(|x, _| x.ok().flatten(), r, w);
+ self.requests.git_diff.poll_r(|x, _| x.ok(), r, w);
+ self.requests.document_symbols.poll_r(
|x, _| {
x.ok().flatten().map(|x| match x {
DocumentSymbolResponse::Flat(_) => None,
DocumentSymbolResponse::Nested(x) => Some(x),
})
},
- &r,
+ r,
+ w,
);
}
#[implicit_fn]
@@ -618,11 +640,22 @@ impl Editor {
.as_ref()
.is_none_or(|&(_, x)| x != cursor_position)
{
- let handle = cl.runtime.spawn(w.redraw_after(cl.request::<lsp_request!("textDocument/definition")>(&GotoDefinitionParams {
- text_document_position_params: z.clone(),
- work_done_progress_params: default(),
- partial_result_params: default(),
- }).unwrap().0));
+ let handle =
+ cl.runtime.spawn(
+ cl.request::<lsp_request!(
+ "textDocument/definition"
+ )>(
+ &GotoDefinitionParams {
+ text_document_position_params: z
+ .clone(),
+ work_done_progress_params: default(
+ ),
+ partial_result_params: default(),
+ },
+ )
+ .unwrap()
+ .0,
+ );
self.requests.def.request =
Some((DropH::new(handle), cursor_position));
} else if self
diff --git a/src/edi/st.rs b/src/edi/st.rs
index de25b1b..a8f5b0a 100644
--- a/src/edi/st.rs
+++ b/src/edi/st.rs
@@ -75,9 +75,27 @@ Command(_) => K(Key::Named(Escape)) => Default,
Command(t) => K(Key::Named(Enter)) => Default [ProcessCommand(Commands => t)],
Command(mut t) => K(Key::Named(Tab) if shift()) => Command({ t.back();t }),
Command(mut t) => K(Key::Named(Tab)) => Command({ t.next(); t }),
-Command(mut t) => K(k) => Command({ handle2(&k, &mut t.tedit, None); t }),
+Command(mut t) => K(k) => Command({ if let Some(_) = handle2(&k, &mut t.tedit, None) {
+ t.selection = 0; t.vo = 0;
+}; t }),
Command(t) => C(_) => _,
Command(t) => K(_) => _,
+Runnables(_x) => {
+ K(Key::Named(Escape)) => Default,
+},
+Runnables(RqS::<crate::runnables::Runnables, rust_analyzer::lsp::ext::Runnables> => Rq { result: Some(mut x), request }) => {
+ 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(k) => Runnables({
+ if let Some(_) = handle2(&k, &mut x.tedit, None) {
+ x.selection = 0; x.vo = 0;
+}; Rq { result: Some(x), request } }),
+},
+Runnables(_x) => {
+ C(_) => _,
+ M(_) => _,
+},
Symbols(Rq { result: Some(_x), request: _rq }) => {
K(Key::Named(Tab) if shift()) => _ [SymbolsSelectNext],
K(Key::Named(ArrowDown)) => _ [SymbolsSelectNext],
diff --git a/src/lsp.rs b/src/lsp.rs
index 88ab350..5533f07 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -1,6 +1,6 @@
use std::backtrace::Backtrace;
use std::collections::HashMap;
-use std::fmt::Display;
+use std::fmt::{Debug, Display};
use std::marker::PhantomData;
use std::mem::forget;
use std::path::{Path, PathBuf};
@@ -94,7 +94,7 @@ impl<X> From<oneshot::error::RecvError> for RequestError<X> {
Self::Rx(PhantomData)
}
}
-impl<X: Request + std::fmt::Debug> std::error::Error for RequestError<X> {
+impl<X: Request + Debug> std::error::Error for RequestError<X> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
@@ -623,16 +623,12 @@ impl Client {
});
}
pub fn runnables(
- &self,
+ &'static self,
t: &Path,
c: Option<Position>,
) -> Result<
- impl Future<
- Output = Result<
- Vec<Runnable>,
- RequestError<Runnables>,
- >,
- >,
+ impl Future<Output = Result<Vec<Runnable>, RequestError<Runnables>>>
+ + use<>,
SendError<Message>,
> {
self.request::<Runnables>(&RunnablesParams {
@@ -1109,7 +1105,7 @@ impl<T, U, F: FnMut(T) -> U, Fu: Future<Output = T>> Map_<T, U, F> for Fu {
}
}
-impl<R: Request> std::fmt::Debug for RequestError<R> {
+impl<R: Request> Debug for RequestError<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self, f)
}
@@ -1123,14 +1119,21 @@ impl<T: Clone, R, D, E> Clone for Rq<T, R, D, E> {
Self { result: self.result.clone(), request: None }
}
}
-#[derive(Debug, serde_derive::Serialize, serde_derive::Deserialize)]
-
+#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
pub struct Rq<T, R, D = (), E = RequestError<R>> {
#[serde(skip_serializing_if = "Option::is_none", default = "none")]
pub result: Option<T>,
#[serde(skip, default = "none")]
pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>,
}
+impl<T: Debug, R, D: Debug, E> Debug for Rq<T, R, D, E> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct(&format!("Rq<{}>", std::any::type_name::<R>()))
+ .field("result", &self.result)
+ .field("request", &self.request)
+ .finish()
+ }
+}
pub type RqS<T, R: Request, D = ()> = Rq<T, R::Result, D, RequestError<R>>;
impl<T, R, D, E> Default for Rq<T, R, D, E> {
@@ -1176,6 +1179,22 @@ impl<T, R, D, E> Rq<T, R, D, E> {
self.result = f(x, (d, self.result.take()));
}
}
+
+ pub fn poll_r(
+ &mut self,
+ f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>,
+ runtime: &tokio::runtime::Runtime,
+ w: Option<&Arc<Window>>,
+ ) {
+ self.poll(
+ |x, y| {
+ f(x, y).inspect(|_| {
+ w.map(|x| x.request_redraw());
+ })
+ },
+ runtime,
+ )
+ }
}
pub trait RedrawAfter {
diff --git a/src/main.rs b/src/main.rs
index cdde4c4..7327c59 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -46,6 +46,7 @@ mod edi;
mod git;
mod meta;
mod rnd;
+mod runnables;
mod sym;
mod trm;
@@ -192,7 +193,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
ed.state.consume(Action::Changed).unwrap();
window.request_redraw();
}
- ed.poll();
+ ed.poll(Some(window));
match event {
Event::AboutToWait => {}
diff --git a/src/rnd.rs b/src/rnd.rs
index 8e2ba9b..4453769 100644
--- a/src/rnd.rs
+++ b/src/rnd.rs
@@ -644,51 +644,16 @@ pub fn render(
);
})
});
- match &ed.state {
- State::CodeAction(Rq { result: Some(x), .. }) => 'out: {
- let m = x.maxc();
- let c = x.write(m);
- let (_x, _y) = (cx, cy);
- let _x = _x + text.line_number_offset() + 1;
- let Some(_y) = _y.checked_sub(text.vo) else {
- println!("rah");
- break 'out;
- };
- let Ok((_is_above, left, top, w, h)) = place_around(
- (_x, _y),
- i.copy(),
- &c,
- m,
- ppem,
- ls,
- 0.,
- 0.,
- 0.,
- ) else {
- println!("ra?");
- break 'out;
- };
- i.r#box(
- (
- left.saturating_sub(1) as _,
- top.saturating_sub(1) as _,
- ),
- w as _,
- h as _,
- BORDER,
- );
- }
- State::Command(x) => 'out: {
- let ws = ed.workspace.as_deref().unwrap();
- let c = x.cells(50, ws);
+ let mut drawb = |cells, c| {
+ // let ws = ed.workspace.as_deref().unwrap();
// let (_x, _y) = text.cursor_visual();
let _x = 0;
let _y = r - 1;
let Ok((_, left, top, w, h)) = place_around(
(_x, _y),
i.copy(),
- &c,
- 50,
+ cells,
+ c,
ppem,
ls,
0.,
@@ -696,7 +661,7 @@ pub fn render(
0.,
) else {
println!("ra?");
- break 'out;
+ return;
};
i.r#box(
(
@@ -707,18 +672,22 @@ pub fn render(
h as _,
BORDER,
);
- }
- State::Symbols(Rq { result: Some(x), .. }) => 'out: {
- let ws = ed.workspace.as_deref().unwrap();
- let c = x.cells(50, ws);
- // let (_x, _y) = text.cursor_visual();
- let _x = 0;
- let _y = r - 1;
+ };
+ match &ed.state {
+ State::CodeAction(Rq { result: Some(x), .. }) => 'out: {
+ let m = x.maxc();
+ let c = x.write(m);
+ let (_x, _y) = (cx, cy);
+ let _x = _x + text.line_number_offset() + 1;
+ let Some(_y) = _y.checked_sub(text.vo) else {
+ println!("rah");
+ break 'out;
+ };
let Ok((_is_above, left, top, w, h)) = place_around(
(_x, _y),
i.copy(),
&c,
- 50,
+ m,
ppem,
ls,
0.,
@@ -738,6 +707,21 @@ pub fn render(
BORDER,
);
}
+ State::Command(x) => {
+ let ws = ed.workspace.as_deref().unwrap();
+ let c = x.cells(50, ws);
+ drawb(&c, 50);
+ }
+ State::Symbols(Rq { result: Some(x), .. }) => {
+ let ws = ed.workspace.as_deref().unwrap();
+ let c = x.cells(50, ws);
+ drawb(&c, 50);
+ }
+ State::Runnables(Rq { result:Some(x), .. }) => {
+ let ws = ed.workspace.as_deref().unwrap();
+ let c = x.cells(50, ws);
+ drawb(&c, 50);
+ }
_ => {}
}
let com = match ed.requests.complete {
diff --git a/src/runnables.rs b/src/runnables.rs
new file mode 100644
index 0000000..3c1b261
--- /dev/null
+++ b/src/runnables.rs
@@ -0,0 +1,139 @@
+use std::iter::{empty, repeat};
+
+use Default::default;
+use dsb::Cell;
+use dsb::cell::Style;
+use lsp_types::LocationLink;
+use rust_analyzer::lsp::ext::{Runnable, RunnableKind};
+
+use crate::menu::generic::{GenericMenu, MenuData};
+use crate::menu::{Key, charc};
+use crate::text::{col, color_, set_a};
+
+pub enum Runb {}
+impl MenuData for Runb {
+ type Data = Vec<Runnable>;
+
+ type Element<'a> = &'a Runnable;
+
+ fn gn<'a>(
+ x: &'a Self::Data,
+ ) -> impl Iterator<Item = Self::Element<'a>> {
+ x.iter()
+ }
+
+ fn r(
+ _: &'_ Self::Data,
+ x: Self::Element<'_>,
+ workspace: &std::path::Path,
+ columns: usize,
+ selected: bool,
+ indices: &[u32],
+ to: &mut Vec<dsb::Cell>,
+ ) {
+ let bg = if selected { col!("#262d3b") } else { col!("#1c212b") };
+
+ let ds: Style = Style::new(crate::FG, bg);
+ let d: Cell = Cell { letter: None, style: ds };
+ let mut b = vec![d; columns];
+ const MAP: [([u8; 3], [u8; 3], &str); 70] = {
+ car::map!(
+ amap::amap! {
+ const { RunnableKind::Cargo as usize } => ("#9a9b9a", "󱣘 "),
+ const { RunnableKind::Shell as usize } => ("#FFAD66", "$ "),
+ _ => ("#9a9b9a", " "),
+ },
+ |(x, y)| (set_a(color_(x), 0.5), color_(x), y)
+ )
+ };
+ let (bgt, col, ty) = MAP[x.kind.clone() as usize];
+ b.iter_mut().zip(ty.chars()).for_each(|(x, c)| {
+ *x = (Style::new(col, bgt) | Style::BOLD).basic(c)
+ });
+ let i = &mut b[2..];
+ // let qualifier = x
+ // .location
+ // .as_ref()
+ // .into_iter()
+ // .flat_map(|x| &x.detail)
+ // .flat_map(_.chars());
+ let qualifier = empty();
+ let left = i.len() as i32
+ - (charc(&x.label) as i32 + qualifier.clone().count() as i32)
+ - 3;
+ let loc = match &x.location {
+ Some(LocationLink { target_uri, .. }) =>
+ Some(target_uri.to_file_path().unwrap()),
+ _ => None,
+ // GoTo::R(_) => None,
+ };
+ let locs = {
+ loc.as_ref()
+ .and_then(|x| {
+ x.strip_prefix(workspace).unwrap_or(&x).to_str()
+ })
+ .unwrap_or("")
+ };
+ let loc = locs.chars().rev().collect::<Vec<_>>().into_iter();
+ let q = if left < charc(&locs) as i32 {
+ locs.chars()
+ .take(left as _)
+ .chain(['…'])
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ .collect::<Vec<_>>()
+ .into_iter()
+ } else {
+ loc
+ };
+
+ i.iter_mut()
+ .rev()
+ .zip(q.map(|x| {
+ Style { bg, fg: color_("#979794"), ..default() }.basic(x)
+ }))
+ .for_each(|(a, b)| *a = b);
+
+ // i.iter_mut()
+ // .rev()
+ // .zip(loc.map(|x| {
+ // Style { bg, fg: color_("#979794"), ..default() }
+ // .basic(x)
+ // }))
+ // .for_each(|(a, b)| *a = b);
+ i.iter_mut()
+ .zip(
+ x.label
+ .chars()
+ .chain([' '])
+ .map(|x| ds.basic(x))
+ .zip(0..)
+ .chain(
+ qualifier
+ .map(|x| {
+ Style {
+ bg,
+ fg: color_("#858685"),
+ ..default()
+ }
+ .basic(x)
+ })
+ .zip(repeat(u32::MAX)),
+ ),
+ )
+ .for_each(|(a, (b, i))| {
+ *a = b;
+ if indices.contains(&i) {
+ a.style |= (Style::BOLD, color_("#ffcc66"));
+ }
+ });
+ to.extend(b);
+ }
+}
+pub type Runnables = GenericMenu<Runb>;
+impl<'a> Key<'a> for &'a Runnable {
+ fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> {
+ &self.label
+ }
+}