A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | src/edi.rs | 85 | ||||
| -rw-r--r-- | src/edi/st.rs | 15 | ||||
| -rw-r--r-- | src/gotolist.rs | 76 | ||||
| -rw-r--r-- | src/lsp/client.rs | 46 | ||||
| -rw-r--r-- | src/main.rs | 22 | ||||
| -rw-r--r-- | src/rnd.rs | 31 | ||||
| -rw-r--r-- | src/text.rs | 12 | ||||
| -rw-r--r-- | src/text/cursor.rs | 4 |
8 files changed, 263 insertions, 28 deletions
@@ -37,7 +37,7 @@ use crate::complete::Complete; use crate::error::WDebug; use crate::hov::{self, Hovr}; use crate::lsp::{ - self, Anonymize, Client, Map_, PathURI, RequestError, Rq, + self, Anonymize, Client, Map_, PathURI, RequestError, Rq, tdpp, }; use crate::menu::generic::MenuData; use crate::meta::META; @@ -506,6 +506,54 @@ impl Editor { }) }); } + State::GoToL(z) => match &mut z.data.1 { + Some(crate::gotolist::O::Impl(y)) => { + y.poll(|x, _| { + x.ok().map(|x| { + x.and_then(|x| try { + z.data.0 = match x { + GotoDefinitionResponse::Scalar( + location, + ) => vec![( + location + .uri + .to_file_path() + .ok()?, + location.range, + )], + GotoDefinitionResponse::Array( + locations, + ) => locations + .into_iter() + .filter_map(|x| try { + ( + x.uri + .to_file_path() + .ok()?, + x.range, + ) + }) + .collect(), + GotoDefinitionResponse::Link( + location_links, + ) => location_links + .into_iter() + .filter_map(|x| try { + ( + x.target_uri + .to_file_path() + .ok()?, + x.target_range, + ) + }) + .collect(), + }; + }); + }) + }); + } + _ => {} + }, _ => {} } self.requests.def.poll(|x, _| { @@ -1743,6 +1791,39 @@ impl Editor { .block_on(crate::runnables::run(x, ws)) .unwrap(); }, + Some(Do::GoToImplementations) => { + let State::GoToL(x) = &mut self.state else { + unreachable!() + }; + if let Some(l) = lsp!(self) { + x.data.1 = Some(crate::gotolist::O::Impl(Rq::new( + l.runtime.spawn( + l.go_to_implementations(tdpp!(self)).unwrap(), + ), + ))); + } + } + Some(Do::GTLSelect(x)) => { + if let Some(Ok((p, r))) = x.sel() + && Some(p) == self.origin.as_deref() + { + let x = self.text.l_range(r).unwrap(); + self.text.vo = self.text.char_to_line(x.start); + self.text.cursor.just(x.start, &self.text.rope); + } + } + Some(Do::GT) => { + let State::GoToL(x) = &mut self.state else { + unreachable!() + }; + if let Some(Ok((p, r))) = x.sel() + && Some(p) == self.origin.as_deref() + { + // let x = self.text.l_range(r).unwrap(); + self.text.scroll_to_ln_centering(r.start.line as _); + // self.text.vo = self.text.char_to_line(x.start); + } + } None => {} } ControlFlow::Continue(()) @@ -1989,7 +2070,7 @@ pub fn handle2<'a>( Named(ArrowDown) => text.down(), Named(PageDown) => text.page_down(), Named(PageUp) => text.page_up(), - Named(Enter) if let Some((l, p)) = l => l.enter(p, text), + Named(Enter) if let Some((l, p)) = l => l.enter(p, text).unwrap(), Named(Enter) => text.enter(), Character(x) => { text.insert(&x); diff --git a/src/edi/st.rs b/src/edi/st.rs index 62149b9..cf6c243 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -9,6 +9,7 @@ use winit::keyboard::{Key, NamedKey, SmolStr}; use crate::commands::Commands; use crate::edi::handle2; +use crate::gotolist::GoToList; use crate::lsp::{AQErr, Rq, RqS}; use crate::menu::generic::{GenericMenu, MenuData}; use crate::sym::{Symbols, SymbolsList}; @@ -51,9 +52,10 @@ Default => { K(Key::Character("0") if ctrl()) => _ [MatchingBrace], K(Key::Character("`") if ctrl()) => _ [SpawnTerminal], K(Key::Character("/") if ctrl()) => Default [Comment(State => State::Default)], - K(Key::Character("p") if ctrl()) => Command(Commands => Commands::default()), + K(Key::Character("p") if ctrl()) => Command(Commands => default()), K(Key::Named(Backspace) if alt()) => _ [DeleteBracketPair], K(Key::Named(F1)) => Procure((default(), InputRequest::RenameSymbol)), + K(Key::Named(F10)) => GoToL(GoToList => default()) [GoToImplementations], K(Key::Named(k @ (ArrowUp | ArrowDown)) if alt()) => _ [InsertCursor(Direction => { if k == ArrowUp {Direction::Above} else { Direction::Below } })], @@ -100,6 +102,17 @@ Runnables(_x) => { C(_) => _, M(_) => _, }, + +GoToL(x) => { + K(Key::Named(Tab) if shift()) => GoToL({ let mut x = x; x.next(); x }) [GT], + K(Key::Named(ArrowDown)) => GoToL({ let mut x = x; x.next(); x }) [GT], + K(Key::Named(ArrowUp | Tab)) => GoToL({ let mut x = x; x.back(); x }) [GT], + K(Key::Named(Enter)) => Default [GTLSelect(GoToList => x)], + K(Key::Named(Escape)) => Default, + // K(_) => _ [GTLHandleKey], + 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/gotolist.rs b/src/gotolist.rs new file mode 100644 index 0000000..5a38fb9 --- /dev/null +++ b/src/gotolist.rs @@ -0,0 +1,76 @@ +use std::path::{Path, PathBuf}; + +use dsb::Cell; +use dsb::cell::Style; +use lsp_types::Range; +use lsp_types::request::GotoImplementation; + +use crate::FG; +use crate::lsp::RqS; +use crate::menu::Key; +use crate::menu::generic::{GenericMenu, MenuData}; +use crate::text::col; +pub type GoToList = GenericMenu<GTL>; +pub enum GTL {} +#[derive(Debug)] +pub enum O { + Impl(RqS<(), GotoImplementation>), +} +impl<'a> Key<'a> for (&'a Path, Range) { + fn key(&self) -> impl Into<std::borrow::Cow<'a, str>> { + self.0.display().to_string() + } +} +impl MenuData for GTL { + type Data = (Vec<(PathBuf, Range)>, Option<O>); + + type Element<'a> = (&'a Path, Range); + + type E = !; + + fn gn<'a>( + x: &'a Self::Data, + ) -> impl Iterator<Item = Self::Element<'a>> { + x.0.iter().map(|(x, y)| (&**x, *y)) + } + + fn r( + _data: &'_ Self::Data, + elem: 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 mut into = + vec![ + Cell { style: Style::new(FG, bg), ..Default::default() }; + columns + ]; + + into.iter_mut() + // .skip(1) + .zip( + elem.0 + .strip_prefix(workspace) + .unwrap_or(&elem.0) + .display() + .to_string() + .chars() + .chain( + format!( + " {}:{} - {}:{}", + elem.1.start.line, + elem.1.start.character, + elem.1.end.line, + elem.1.end.character + ) + .chars(), + ), + ) + .for_each(|(a, b)| a.letter = Some(b)); + to.extend(into); + } +} diff --git a/src/lsp/client.rs b/src/lsp/client.rs index a137b37..65225db 100644 --- a/src/lsp/client.rs +++ b/src/lsp/client.rs @@ -15,6 +15,7 @@ use lsp_types::request::*; use lsp_types::*; use rust_analyzer::lsp::ext::*; use tokio::sync::oneshot; +use ttools::*; use crate::lsp::BehaviourAfter::{self, *}; use crate::lsp::{RequestError, Rq}; @@ -474,26 +475,30 @@ impl Client { Ok(()) } - pub fn enter<'a>(&self, f: &Path, t: &'a mut TextArea) { - ceach!(t.cursor, |c| { + pub fn enter<'a>( + &self, + f: &Path, + t: &'a mut TextArea, + ) -> rootcause::Result<()> { + ceach!(t.cursor, |c| try bikeshed rootcause::Result<()> { let r = self .request_immediate::<OnEnter>( &TextDocumentPositionParams { text_document: f.tid(), position: t.to_l_position(*c).unwrap(), }, - ) - .unwrap(); + )?; match r { None => t.enter(), Some(mut r) => { r.sort_tedits(); for f in r { - t.apply_snippet_tedit(&f).unwrap(); + t.apply_snippet_tedit(&f)?; } } } - }); + } => ?); + Ok(()) } pub fn runnables( &'static self, @@ -508,7 +513,7 @@ impl Client { text_document: t.tid(), position: c, }) - .map(|(x, _)| x) + .map(fst) } pub fn _child_modules( @@ -528,7 +533,26 @@ impl Client { position: p, text_document: t.tid(), }) - .map(|x| x.0) + .map(fst) + } + pub fn go_to_implementations( + &self, + tdpp: TextDocumentPositionParams, + ) -> Result< + impl Future< + Output = Result< + <GotoImplementation as Request>::Result, + RequestError<GotoImplementation>, + >, + >, + SendError<Message>, + > { + self.request::<GotoImplementation>(&GotoImplementationParams { + text_document_position_params: tdpp, + work_done_progress_params: default(), + partial_result_params: default(), + }) + .map(fst) } } @@ -542,3 +566,9 @@ impl PathURI for Path { } } } +pub macro tdpp($e:expr) { + TextDocumentPositionParams { + text_document: $e.origin.as_ref().unwrap().tid(), + position: $e.text.to_l_position(*$e.text.cursor.first()).unwrap(), + } +} diff --git a/src/main.rs b/src/main.rs index 6618bc9..45bbdd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ #![feature( + trim_prefix_suffix, + const_closures, yeet_expr, adt_const_params, inherent_associated_types, @@ -48,12 +50,13 @@ #![allow(incomplete_features, irrefutable_let_patterns, static_mut_refs)] mod act; mod edi; +mod error; mod git; +mod gotolist; mod meta; mod rnd; mod runnables; mod sym; -mod error; mod trm; use std::fmt::{Debug, Display}; @@ -126,14 +129,15 @@ extern "C" fn sigint(_: i32) { #[implicit_fn::implicit_fn] pub(crate) fn entry(event_loop: EventLoop) { - - unsafe { __ED.write(match Editor::new() { - Err(e) => { - eprintln!("failure to launch: {e}"); - return - } - Ok(x) => x, - }) }; + unsafe { + __ED.write(match Editor::new() { + Err(e) => { + eprintln!("failure to launch: {e}"); + return; + } + Ok(x) => x, + }) + }; assert_eq!(unsafe { atexit(cleanup) }, 0); unsafe { signal(libc::SIGINT, sigint as *const () as usize) }; let ed = unsafe { __ED.assume_init_mut() }; @@ -19,6 +19,7 @@ use winit::window::{ImeRequestData, Window}; use crate::edi::st::State; use crate::edi::{Editor, lsp_m}; use crate::lsp::Rq; +use crate::sym::{GoTo, UsedSI}; use crate::text::{CoerceOption, RopeExt, col, color_}; use crate::{ BG, BORDER, CompletionAction, CompletionState, FG, FONT, complete, @@ -79,7 +80,7 @@ pub fn render( style: Style { fg: BG, secondary_color: BG, bg: BG, flags: 0 }, letter: None, }); - let x = match &ed.state { + let z = match &ed.state { State::Selection => Some( text.cursor .iter() @@ -95,6 +96,26 @@ pub fn render( BG, (cells, (c, r)), (1, 0), + |mut f, y| { + if let State::GoToL(menu) = &ed.state + && let Some(Ok((p, r))) = menu.sel() + && Some(p) == ed.origin.as_deref() + { + if (r.start.line..=r.end.line).contains(&(y as _)) { + f.style.fg = col!("#FFCC66"); + } + } + if let State::Symbols(Rq { result: Some(menu), .. }) = + &ed.state + && let Some(Ok(UsedSI { at: GoTo::R(x), .. })) = + menu.sel() + { + if (x.start.line..=x.end.line).contains(&(y as _)) { + f.style.fg = col!("#FFCC66"); + } + } + f + }, ); let t_ox = text.line_number_offset() + 1; text.c = c - t_ox; @@ -116,7 +137,7 @@ pub fn render( text.write_to((cells, (c, r)), (t_ox, 0), - x, + z, |(_c, _r), text, mut x| { if let Some(hl) = &ed.requests.document_highlights.result { for DocumentHighlight { range: r, .. } in hl { @@ -127,6 +148,7 @@ pub fn render( // }; let (x1, y1) = text.map_to_visual((r.start.character as _, r.start.line as _)); let (x2, y2) = text.map_to_visual((r.end.character as _, r.end.line as _)); + x.get_simple((x1, y1), (x2, y2)).coerce().for_each(|x| { x.style.bg = col!("#3a4358"); }); @@ -739,6 +761,11 @@ pub fn render( let c = x.cells(50, ws); drawb(&c, 50); } + State::GoToL(y) => { + let ws = ed.workspace.as_deref().unwrap(); + let c = y.cells(50, ws); + drawb(&c, 50); + } _ => {} } let com = match ed.requests.complete { diff --git a/src/text.rs b/src/text.rs index 04e07b5..8b892df 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1040,6 +1040,7 @@ impl TextArea { bg: [u8; 3], (into, (w, _)): (&mut [Cell], (usize, usize)), (ox, oy): (usize, usize), + mut m: impl FnMut(Cell, usize) -> Cell, ) { for y in 0..r { if (self.vo + y) < self.l() { @@ -1048,10 +1049,13 @@ impl TextArea { .chars() .zip(into[(y + oy) * w..].iter_mut().skip(ox)) .for_each(|(a, b)| { - *b = Cell { - style: Style::new(color, bg), - letter: Some(a), - } + *b = m( + Cell { + style: Style::new(color, bg), + letter: Some(a), + }, + self.vo + y, + ) }); } } diff --git a/src/text/cursor.rs b/src/text/cursor.rs index 79514ff..07fe471 100644 --- a/src/text/cursor.rs +++ b/src/text/cursor.rs @@ -91,10 +91,10 @@ impl Default for Cursors { pub fn caster<T, U>(x: impl FnMut(T) -> U) -> impl FnMut(T) -> U { x } -pub macro ceach($cursor: expr, $f:expr) { +pub macro ceach($cursor: expr, $f:expr $( => $q:tt)?) { for i in (0..$cursor.inner.len()) { let c = *$cursor.inner.get(i).expect("aw dangit"); - caster::<Cursor, _>($f)(c); + caster::<Cursor, _>($f)(c) $($q)?; } $cursor.coalesce(); } |