A simple CPU rendered GUI IDE experience.
holy hell
| -rw-r--r-- | src/com.rs | 5 | ||||
| -rw-r--r-- | src/lsp.rs | 102 | ||||
| -rw-r--r-- | src/main.rs | 90 | ||||
| -rw-r--r-- | src/sni.rs | 11 | ||||
| -rw-r--r-- | src/text.rs | 52 |
5 files changed, 179 insertions, 81 deletions
@@ -278,9 +278,10 @@ fn t() { let (c, r) = dsb::fit(&crate::FONT, ppem, lh, (w, h)); dbg!(dsb::size(&crate::FONT, ppem, lh, (c, r))); let y = serde_json::from_str(include_str!("../complete_")).unwrap(); - let cells = s(&Complete { r: y, selection: 0, vo: 0 }, c, ""); - dbg!(c, r); + let cells = + s(&Complete { r: y, selection: 0, vo: 0, start: 0 }, c, ""); dbg!(w, h); + dbg!(c, r); let mut fonts = dsb::Fonts::new( F::FontRef(*crate::FONT, &[(2003265652, 550.0)]), @@ -1,6 +1,8 @@ +use std::backtrace::Backtrace; use std::collections::HashMap; use std::fmt::Display; -use std::mem::forget; +use std::marker::PhantomData; +use std::mem::{MaybeUninit, forget}; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::atomic::AtomicI32; @@ -14,7 +16,7 @@ use anyhow::bail; use crossbeam::channel::{ Receiver, RecvError, SendError, Sender, unbounded, }; -use log::{debug, error}; +use log::{debug, error, trace}; use lsp_server::{ ErrorCode, Message, Notification as N, Request as LRq, Response as Re, ResponseError, @@ -49,26 +51,34 @@ impl Drop for Client { } } #[derive(Debug)] -pub enum RequestError { - Rx, +pub enum RequestError<X> { + Rx(PhantomData<X>), + Failure(Re, Backtrace), Cancelled(Re, DiagnosticServerCancellationData), } -impl From<oneshot::error::RecvError> for RequestError { +// impl<X> Debug for RequestError<X> {} +impl<X> From<oneshot::error::RecvError> for RequestError<X> { fn from(_: oneshot::error::RecvError) -> Self { - Self::Rx + Self::Rx(PhantomData) } } -impl std::error::Error for RequestError { +impl<X: Request + std::fmt::Debug> std::error::Error for RequestError<X> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } -impl Display for RequestError { +impl<X: Request> Display for RequestError<X> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Rx => write!(f, "couldnt get from thingy"), + Self::Rx(_) => + write!(f, "{} failed; couldnt get from thingy", X::METHOD), + Self::Failure(x, bt) => write!( + f, + "{} failed; returned badge :( {x:?} ({bt:?})", + X::METHOD + ), Self::Cancelled(x, y) => - write!(f, "server cancelled us. {x:?} {y:?}"), + write!(f, "server cancelled {}. {x:?} {y:?}", X::METHOD), } } } @@ -91,7 +101,8 @@ impl Client { y: &X::Params, ) -> Result< ( - impl Future<Output = Result<X::Result, RequestError>> + use<'me, X>, + impl Future<Output = Result<X::Result, RequestError<X>>> + + use<'me, X>, i32, ), SendError<Message>, @@ -104,7 +115,7 @@ impl Client { }))?; let (tx, rx) = oneshot::channel(); if self.initialized.is_some() { - debug!("sent request {id}'s handler"); + debug!("sent request {} ({id})'s handler", X::METHOD); self.send_to.send((id, tx)).expect("oughtnt really fail"); } Ok(( @@ -117,12 +128,15 @@ impl Client { forget(g); if let Some(ResponseError { code, ref mut data, .. }) = x.error - && code == ErrorCode::ServerCancelled as i32 { - let e = serde_json::from_value( - data.take().unwrap_or_default(), - ); - Err(RequestError::Cancelled(x, e.expect("lsp??"))) + if code == ErrorCode::ServerCancelled as i32 { + let e = serde_json::from_value( + data.take().unwrap_or_default(), + ); + Err(RequestError::Cancelled(x, e.expect("lsp??"))) + } else { + Err(RequestError::Failure(x, Backtrace::capture())) + } } else { Ok(serde_json::from_value::<X::Result>( x.result.unwrap_or_default(), @@ -172,7 +186,12 @@ impl Client { &self, x: CompletionItem, ) -> Result< - impl Future<Output = Result<CompletionItem, RequestError>>, + impl Future< + Output = Result< + CompletionItem, + RequestError<ResolveCompletionItem>, + >, + >, SendError<Message>, > { self.request::<ResolveCompletionItem>(&x).map(|x| x.0) @@ -184,7 +203,10 @@ impl Client { (x, y): (usize, usize), c: CompletionContext, ) -> impl Future< - Output = Result<Option<CompletionResponse>, RequestError>, + Output = Result< + Option<CompletionResponse>, + RequestError<Completion>, + >, > + use<'me> { let (rx, _) = self .request::<Completion>(&CompletionParams { @@ -204,8 +226,12 @@ impl Client { &'me self, f: &Path, (x, y): (usize, usize), - ) -> impl Future<Output = Result<Option<SignatureHelp>, RequestError>> - + use<'me> { + ) -> impl Future< + Output = Result< + Option<SignatureHelp>, + RequestError<SignatureHelpRequest>, + >, + > + use<'me> { self.request::<SignatureHelpRequest>(&SignatureHelpParams { context: None, text_document_position_params: TextDocumentPositionParams { @@ -332,8 +358,12 @@ impl Client { &'static self, f: &Path, t: &TextArea, - ) -> impl Future<Output = Result<Vec<InlayHint>, RequestError>> + use<> - { + ) -> impl Future< + Output = Result< + Vec<InlayHint>, + RequestError<lsp_request!("textDocument/inlayHint")>, + >, + > + use<> { self.request::<lsp_request!("textDocument/inlayHint")>(&InlayHintParams { work_done_progress_params: default(), text_document: f.tid(), @@ -359,7 +389,12 @@ impl Client { pub fn rq_semantic_tokens( &'static self, - to: &mut Rq<Box<[SemanticToken]>, Box<[SemanticToken]>>, + to: &mut Rq< + Box<[SemanticToken]>, + Box<[SemanticToken]>, + (), + RequestError<SemanticTokensFullRequest>, + >, f: &Path, w: Option<Arc<Window>>, ) -> anyhow::Result<()> { @@ -377,7 +412,8 @@ impl Client { }, )?; let x = self.runtime.spawn(async move { - let y = rx.await?.unwrap(); + let t = rx.await; + let y = t?.unwrap(); debug!("received semantic tokens"); let r = match y { SemanticTokensResult::Partial(_) => @@ -691,16 +727,20 @@ pub fn run( } } Ok(Message::Response(x)) => { - if let Some(e) =& x.error { + if let Some(e) = &x.error { if e.code == ErrorCode::RequestCanceled as i32 {} else if e.code == ErrorCode::ServerCancelled as i32 { if let Some((s, _)) = map.remove(&x.id.i32()) { log::info!("request {} cancelled", x.id); _ = s.send(x); } - } - else { - error!("received error from lsp for response {x:?}"); + } else { + if let Some((s, _)) = map.remove(&x.id.i32()) { + _ = s.send(x.clone()); + trace!("received error from lsp for response {x:?}"); + } else { + error!("received error from lsp for response {x:?}"); + } } } else if let Some((s, took)) = map.remove(&x.id.i32()) { @@ -875,11 +915,11 @@ impl<T> OnceOff<T> { } #[derive(Debug)] -pub struct Rq<T, R, D = (), E = RequestError> { +pub struct Rq<T, R, D = (), E = RequestError<R>> { pub result: Option<T>, pub request: Option<(AbortOnDropHandle<Result<R, E>>, D)>, } -pub type RqS<T, R: Request, D = ()> = Rq<T, R::Result, D>; +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> { fn default() -> Self { Self { result: None, request: None } diff --git a/src/main.rs b/src/main.rs index 6b7f05b..32c0530 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ // this looks pretty good though #![feature(tuple_trait, unboxed_closures, fn_traits)] #![feature( + vec_try_remove, iter_next_chunk, iter_array_chunks, array_windows, @@ -32,6 +33,7 @@ )] #![allow(incomplete_features, redundant_semicolons)] use std::borrow::Cow; +use std::collections::HashMap; use std::iter::once; mod act; use std::num::NonZeroU32; @@ -71,7 +73,7 @@ use winit::window::Icon; use crate::bar::Bar; use crate::hov::Hovr; -use crate::lsp::{RedrawAfter, RqS}; +use crate::lsp::{RedrawAfter, RequestError, RqS}; use crate::text::{Diff, Mapping, TextArea, col, is_word}; mod bar; pub mod com; @@ -82,6 +84,7 @@ mod sni; mod text; mod winit_app; fn main() { + let x = 4; unsafe { std::env::set_var("CARGO_UNSTABLE_RUSTC_UNICODE", "true") }; env_logger::init(); // lsp::x(); @@ -218,7 +221,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { std::thread::Builder::new() .name("Rust Analyzer".into()) .stack_size(1024 * 1024 * 8) - .spawn(|| rust_analyzer::bin::run_server(b)) + .spawn(|| { + std::panic::set_hook(Box::new(|info| { + println!("RA panic @ {}", info.location().unwrap()); + })); + rust_analyzer::bin::run_server(b) + }) .unwrap(); let (c, t2, changed) = lsp::run( (a.sender, a.receiver), @@ -257,11 +265,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { let mut semantic_tokens = default(); let mut diag = Rq::<String, Option<String>, (), anyhow::Error>::default(); - let mut inlay: Rq<Vec<InlayHint>, Vec<InlayHint>> = default(); + let mut inlay: Rq<Vec<InlayHint>, Vec<InlayHint>, (), RequestError<lsp_request!("textDocument/inlayHint")>> = default(); let mut def = Rq::< LocationLink, Option<GotoDefinitionResponse>, (usize, usize), + RequestError<lsp_request!("textDocument/definition")>, >::default(); // let mut complete = None::<(CompletionResponse, (usize, usize))>; // let mut complete_ = None::<( @@ -660,7 +669,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { let mut r = c.len()/columns; assert_eq!(c.len()%columns, 0); let (w, h) = dsb::size(&fonts.regular, ppem, ls, (columns, r)); - if position.0 + w >= window.inner_size().width as usize || position.1 + h >= window.inner_size().height as usize { + if w >= window.inner_size().width as usize || position.1 + h >= window.inner_size().height as usize { + unsafe { dsb::render_owned(c, (columns, c.len() / columns), 18.0, fonts, 1.1, true).save("fail.png") }; return Err(()); } assert!(w < window.inner_size().width as _ &&h < window.inner_size().height as _); @@ -690,7 +700,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { let mut pass = true; if let Some((lsp, p)) = lsp!() && let Some(diag) = lsp.diagnostics.get(&Url::from_file_path(p).unwrap(), &lsp.diagnostics.guard()) { let dawg = diag.iter().filter(|diag| text.l_range(diag.range).is_some_and(|x| x.contains(&text.mapped_index_at(cursor_position)) && (text.vo..text.vo+r).contains(&(diag.range.start.line as _)))); - for diag in dawg { + for diag in dawg { match diag.data.as_ref().unwrap_or_default().get("rendered") { Some(x) if let Some(x) = x.as_str() => { let mut t = pattypan::term::Terminal::new((95, (r.saturating_sub(5)) as _), false); @@ -816,7 +826,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { &mut fonts, i.as_mut(), &c, 40, ppem, ls, 0., 0., 0. - ) else { return ; }; + ) else { break 'out; }; i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, BORDER); } } @@ -1026,6 +1036,9 @@ hovering.request = (DropH::new(handle), cursor_position).into(); match state.consume(Action::M(button)).unwrap() { Some(Do::MoveCursor) => { text.cursor = text.mapped_index_at(cursor_position); + if let Some((lsp, path)) = lsp!() { + sig_help.request(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(path, text.cursor())))); + } text.setc(); } Some(Do::ExtendSelectionToMouse) => { @@ -1151,21 +1164,33 @@ hovering.request = (DropH::new(handle), cursor_position).into(); let act = lsp.runtime.block_on( lsp.request::<CodeActionResolveRequest>(&act).unwrap().0 ).unwrap(); - dbg!(&act); + let mut f_ = |edits: &[SnippetTextEdit]|{ + let mut first = false; + for SnippetTextEdit { text_edit, insert_text_format ,..}in edits { + match insert_text_format { + Some(InsertTextFormat::SNIPPET) => { + text.apply_snippet(&text_edit).unwrap() + }, + _ => { + if first { + let (b, e) = text.apply(&text_edit).unwrap(); + log::error!("dont account for this case yet"); + } else { + text.apply_adjusting(text_edit).unwrap(); + } + } + } + first = false; + } + }; match act.edit { Some(WorkspaceEdit { document_changes:Some(DocumentChanges::Edits(x)), .. }) => { - for TextDocumentEdit { edits, text_document } in x { - if text_document.uri!= f.tid().uri { continue } - for SnippetTextEdit { text_edit, insert_text_format ,..}in edits { - match insert_text_format { - Some(InsertTextFormat::SNIPPET) => - text.apply_snippet(&text_edit).unwrap(), - _ => text.apply(&text_edit).unwrap() - } - } + for x in x { + if x.text_document.uri!= f.tid().uri { return } + f_(&x.edits); } } @@ -1178,14 +1203,9 @@ hovering.request = (DropH::new(handle), cursor_position).into(); DocumentChangeOperation::Edit(TextDocumentEdit { edits, text_document,.. }) => { - - for SnippetTextEdit { text_edit, insert_text_format ,..}in edits { - match insert_text_format { - Some(InsertTextFormat::SNIPPET) => - text.apply_snippet(&text_edit).unwrap(), - _ => text.apply(&text_edit).unwrap() + if text_document.uri!= f.tid().uri { return } + f_(&edits); } - }} x=>log::error!("didnt apply {x:?}"), }; // if text_document.uri!= f.tid().uri { continue } @@ -1273,27 +1293,19 @@ hovering.request = (DropH::new(handle), cursor_position).into(); Some(CDo::Finish(x)) => { let sel = x.sel(&filter(&text)); let sel = lsp.runtime.block_on(lsp.resolve(sel.clone()).unwrap()).unwrap(); - let CompletionItem { text_edit: Some(CompletionTextEdit::Edit(ed)), additional_text_edits, insert_text_format, .. } = sel else { panic!() }; + let CompletionItem { text_edit: Some(CompletionTextEdit::Edit(ed)), additional_text_edits, insert_text_format, .. } = sel.clone() else { panic!() }; match insert_text_format { - Some(InsertTextFormat::SNIPPET) => - text.apply_snippet(&ed).unwrap(), + Some(InsertTextFormat::SNIPPET) =>{ + text.apply_snippet(&ed).unwrap(); + }, _ => { - text.apply(&ed).unwrap(); - text.cursor = text.l_position(ed.range.start).unwrap() + ed.new_text.chars().count(); + let (s, _) = text.apply(&ed).unwrap(); + text.cursor = s + ed.new_text.chars().count(); } } for additional in additional_text_edits.into_iter().flatten() { - let p = text.l_position(additional.range.end).unwrap(); - if p < text.cursor { - if !text.visible(p) { - // line added behind, not visible - text.vo += additional.new_text.chars().filter(|x|x.is_newline()).count(); - } - text.apply(&additional).unwrap(); - text.cursor += additional.new_text.chars().count(); // compensate - } + text.apply_adjusting(&additional).unwrap(); } - if hist.record(&text) { change!();} sig_help = Rq::new(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(o, text.cursor())))); } @@ -1571,7 +1583,7 @@ CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => Rq { result : K(Key::Named(ArrowUp)) => _ [CASelectPrev], K(Key::Named(Enter)) => Default [CASelect(act::CodeActions => act)], }, -CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => rq) => { +CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,(), RequestError<lsp_request!("textDocument/codeAction")>> => rq) => { K(Key::Named(Escape)) => Default, C(_) => _, M(_) => _, @@ -5,7 +5,7 @@ use helix_core::snippets::parser::SnippetElement; #[derive(Debug, Clone)] pub struct Snippet { pub stops: Vec<(Stop, StopP)>, - pub last: StopP, + pub last: Option<StopP>, pub index: usize, } @@ -45,13 +45,12 @@ impl Snippet { } stops.sort_by_key(|x| x.0); stops.iter_mut().for_each(|x| x.1.manipulate(|x| x + at)); - let last = stops.remove(0); - assert_eq!(last.0, 0); - Some((Snippet { last: last.1, stops, index: 0 }, o)) + let last = stops.try_remove(0); + Some((Snippet { last: last.map(|x| x.1), stops, index: 0 }, o)) } pub fn manipulate(&mut self, f: impl FnMut(usize) -> usize + Clone) { self.stops.iter_mut().for_each(|x| x.1.manipulate(f.clone())); - self.last.manipulate(f); + self.last.as_mut().map(|x| x.manipulate(f)); } pub fn next(&mut self) -> Option<StopP> { self.stops.get(self.index).map(|x| { @@ -64,7 +63,7 @@ impl Snippet { .iter() .skip(self.index) .map(|x| &x.1) - .chain([&self.last]) + .chain(self.last.iter()) .cloned() } diff --git a/src/text.rs b/src/text.rs index 48b1db1..adefe0b 100644 --- a/src/text.rs +++ b/src/text.rs @@ -460,11 +460,26 @@ impl TextArea { self.set_ho(); } - pub fn apply(&mut self, x: &TextEdit) -> Result<(), ()> { + pub fn apply(&mut self, x: &TextEdit) -> Result<(usize, usize), ()> { let begin = self.l_position(x.range.start).ok_or(())?; let end = self.l_position(x.range.end).ok_or(())?; self.rope.try_remove(begin..end).map_err(|_| ())?; self.rope.try_insert(begin, &x.new_text).map_err(|_| ())?; + Ok((begin, end)) + } + pub fn apply_adjusting(&mut self, x: &TextEdit) -> Result<(), ()> { + let (b, e) = self.apply(&x)?; + if e < self.cursor { + if !self.visible(e) { + // line added behind, not visible + self.vo += + x.new_text.chars().filter(|&x| x == '\n').count(); + } + let removed = dbg!(e - b); + self.cursor += x.new_text.chars().count(); + self.cursor -= removed; // compensate + // text.cursor += additional.new_text.chars().count(); // compensate + } Ok(()) } pub fn apply_snippet(&mut self, x: &TextEdit) -> anyhow::Result<()> { @@ -486,7 +501,9 @@ impl TextArea { } None => { self.tabstops = None; - begin + x.new_text.chars().count() + sni.last + .map(|x| x.r().end) + .unwrap_or_else(|| begin + x.new_text.chars().count()) } }; Ok(()) @@ -709,7 +726,7 @@ impl TextArea { self.cursor = x.r().end; } None => { - self.cursor = x.last.clone().r().end; + self.cursor = x.last.clone().unwrap().r().end; self.tabstops = None; } }, @@ -1763,3 +1780,32 @@ impl Mapping<'_> { *x } } + +#[test] +fn apply() { + let mut t = TextArea::default(); + t.insert( + r#"fn main() { + let x = 4; +} +"#, + ); + + t.apply_snippet(&TextEdit { + range: lsp_types::Range { + start: Position { line: 0, character: 8 }, + end: Position { line: 0, character: 9 }, + }, + new_text: "$0var_name".into(), + }) + .unwrap(); + t.apply_adjusting(&TextEdit { + range: lsp_types::Range { + start: Position { line: 1, character: 4 }, + end: Position { line: 1, character: 4 }, + }, + new_text: "let x = var_name;\n ".to_owned(), + }) + .unwrap(); + assert_eq!(t.cursor, 8); +} |