A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | src/commands.rs | 14 | ||||
| -rw-r--r-- | src/edi.rs | 139 | ||||
| -rw-r--r-- | src/edi/st.rs | 20 | ||||
| -rw-r--r-- | src/lsp.rs | 43 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/rnd.rs | 80 | ||||
| -rw-r--r-- | src/runnables.rs | 139 |
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!(), } @@ -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], @@ -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 => {} @@ -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 + } +} |