A simple CPU rendered GUI IDE experience.
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/act.rs | 201 | ||||
| -rw-r--r-- | src/lsp.rs | 7 | ||||
| -rw-r--r-- | src/main.rs | 83 | ||||
| -rw-r--r-- | src/text.rs | 2 |
5 files changed, 233 insertions, 61 deletions
@@ -56,6 +56,7 @@ tokio-util = { version = "0.7.17", features = ["rt"] } scopeguard = "1.2.0" arc-swap = "1.7.1" atools = "0.1.10" +swizzle = "0.1.0" [profile.dev.package] rust-analyzer.opt-level = 3 @@ -1,58 +1,185 @@ use dsb::Cell; use dsb::cell::Style; -use lsp_types::CodeAction; +use itertools::Itertools; +use lsp_types::{CodeAction, CodeActionKind}; + +#[derive(Debug, Clone)] +enum N<T> { + One(T), + Many(Vec<T>, Entry, Vec<String>), +} +#[derive(Debug, Clone, Copy)] +enum Entry { + Inside(usize), + Outside(usize), +} #[derive(Debug, Clone)] pub struct CodeActions { - pub inner: Vec<CodeAction>, + pub inner: N<Vec<CodeAction>>, pub selection: usize, pub vo: usize, } use crate::FG; -use crate::text::col; +use crate::text::{col, set_a}; const N: usize = 13; impl CodeActions { - pub fn next(&mut self) { - let n = self.inner.len(); - self.selection += 1; - if self.selection == n { - self.vo = 0; - self.selection = 0; + /// there is a clear most intuitive way to do this, but. it is hard. + pub fn new(x: Vec<CodeAction>) -> Self { + let has_groups = x.iter().any(|x| x.group.is_some()); + let inner = if has_groups { + let lem: Vec<Vec<CodeAction>> = x + .into_iter() + .chunk_by(|x| x.group.clone().unwrap_or("0".into())) + .into_iter() + .map(|x| x.1.collect::<Vec<_>>()) + .collect(); + let g = lem + .iter() + .map(|x| x[0].group.clone().unwrap_or("misc".into())) + .collect::<Vec<_>>(); + N::Many(lem, Entry::Outside(0), g) + } else { + N::One(x) + }; + Self { inner, selection: 0, vo: 0 } + } + + pub fn down(&mut self) { + let mut adj = |y: &mut usize, max| { + *y += 1; + if *y == max { + self.vo = 0; + *y = 0; + } + if *y >= self.vo + 13 { + self.vo += 1; + } + }; + match &mut self.inner { + N::Many(x, Entry::Outside(y), so) => { + let n = x.len(); + adj(y, n); + } + N::Many(x, Entry::Inside(g_sel), _) => { + let z = &x[*g_sel]; + let n = z.len(); + + // TODO: think about this + adj(&mut self.selection, n); + } + N::One(x) => { + let n = x.len(); + adj(&mut self.selection, n); + } + }; + } + pub fn innr(&self) -> Option<&[CodeAction]> { + match &self.inner { + N::One(x) => Some(x), + N::Many(x, Entry::Inside(y), _) => Some(&x[*y]), + N::Many(_, Entry::Outside(_), _) => None, } - if self.selection >= self.vo + 13 { - self.vo += 1; + } + pub fn left(&mut self) { + match &mut self.inner { + N::Many(items, x @ Entry::Inside(_), items1) => { + let Entry::Inside(y) = x else { unreachable!() }; + *x = Entry::Outside(*y); + } + _ => {} } } - - pub fn sel(&self) -> &CodeAction { - &self.inner[self.selection] + pub fn right(&mut self) -> Option<&CodeAction> { + match &mut self.inner { + N::One(x) => Some(&x[self.selection]), + N::Many(y, Entry::Inside(x), _) => + Some(&y[*x][self.selection]), + N::Many(_, y, _) => { + let x = + if let Entry::Outside(x) = y { *x } else { panic!() }; + *y = Entry::Inside(x); + None + } + } } #[lower::apply(saturating)] - pub fn back(&mut self) { - let n = self.inner.len(); - if self.selection == 0 { - self.vo = n - N; - self.selection = n - 1; + pub fn up(&mut self) { + if let Some(x) = self.innr() { + let n = x.len(); + if self.selection == 0 { + self.vo = n - N; + self.selection = n - 1; + } else { + self.selection -= 1; + if self.selection < self.vo { + self.vo -= 1; + } + } } else { - self.selection -= 1; - if self.selection < self.vo { - self.vo -= 1; + match &mut self.inner { + N::Many(_, Entry::Outside(y), z) => { + let n = z.len(); + if *y == 0 { + self.vo = n - N; + *y = n - 1; + } else { + *y = *y - 1; + if *y < self.vo { + self.vo -= 1; + } + } + } + _ => unreachable!(), } } } pub fn maxc(&self) -> usize { - self.inner - .iter() - .map(|x| x.title.chars().count()) - .max() - .unwrap_or(0) + match &self.inner { + N::One(x) => x + .iter() + .map(|x| x.title.chars().count() + 2) + .max() + .unwrap_or(0), + N::Many(x, _, g) => x + .iter() + .flatten() + .map(|x| &x.title) + .chain(g) + .map(|x| x.chars().count() + 2) + .max() + .unwrap_or(0), + } } pub fn write(&self, c: usize) -> Vec<Cell> { let mut into = vec![]; - for (el, i) in self.inner.iter().zip(0..) { - write(el, c, self.selection == i, &mut into); + if let Some(x) = self.innr() { + for (el, i) in x.iter().zip(0..).skip(self.vo).take(13) { + write(el, c, self.selection == i, &mut into); + } + } else if let N::Many(_, Entry::Outside(n), z) = &self.inner { + for (el, i) in z.iter().skip(self.vo).zip(0..).take(13) { + let bg = if *n == i { + col!("#262d3b") + } else { + col!("#1c212b") + }; + + let mut to = vec![ + Cell { + style: Style { bg, color: FG, flags: 0 }, + ..Default::default() + }; + c + ]; + to.iter_mut() + .zip(el.chars()) + .for_each(|(a, b)| a.letter = Some(b)); + into.extend(to); + // write(el, c, self.selection == i, &mut into); + } } into } @@ -66,7 +193,23 @@ fn write(x: &CodeAction, c: usize, selected: bool, to: &mut Vec<Cell>) { }; c ]; + + let t = match &x.kind { + Some(x) if x == &CodeActionKind::QUICKFIX => '', + Some(x) + if x == &CodeActionKind::REFACTOR + || x == &CodeActionKind::REFACTOR_EXTRACT + || x == &CodeActionKind::REFACTOR_INLINE + || x == &CodeActionKind::REFACTOR_REWRITE => + '', + Some(x) if x == &CodeActionKind::SOURCE => '', + _ => '', /* ☭ */ + }; + into[0].style.color = col!("#E5C07B"); + into[0].style.bg = set_a(into[0].style.color, 0.5); + into[0].letter = Some(t); into.iter_mut() + .skip(1) .zip(x.title.chars()) .for_each(|(a, b)| a.letter = Some(b)); to.extend(into); @@ -316,6 +316,7 @@ impl Client { }, Err(e) => return Err(e.into()), }; + // dbg!(&x); match x.clone() { DocumentDiagnosticReportResult::Report( DocumentDiagnosticReport::Full( @@ -483,7 +484,10 @@ pub fn run( data_support: Some(true), resolve_support: Some(CodeActionCapabilityResolveSupport { properties: vec!["edit".to_string()] }), code_action_literal_support: Some(CodeActionLiteralSupport { code_action_kind: CodeActionKindLiteralSupport { value_set: [ - "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" + "", "Empty", "QuickFix", "Refactor", + "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", + "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", + "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" ].map(String::from).into()} }), ..default() } @@ -648,6 +652,7 @@ pub fn run( "rangeExclusiveHints": { "enable": true }, "closureCaptureHints": { "enable": true }, }, + "assist": { "preferSelf": true }, "checkOnSave": true, "diagnostics": { "enable": true }, "semanticHighlighting": { diff --git a/src/main.rs b/src/main.rs index 962ccde..f1cca39 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( + current_thread_id, vec_try_remove, iter_next_chunk, iter_array_chunks, @@ -33,7 +34,6 @@ )] #![allow(incomplete_features, redundant_semicolons)] use std::borrow::Cow; -use std::collections::HashMap; use std::iter::once; mod act; use std::num::NonZeroU32; @@ -84,7 +84,8 @@ mod sni; mod text; mod winit_app; fn main() { - let x = 4; + let _x = 4; + // let x = HashMap::new(); unsafe { std::env::set_var("CARGO_UNSTABLE_RUSTC_UNICODE", "true") }; env_logger::init(); // lsp::x(); @@ -211,6 +212,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { .and_then(|x| x.canonicalize().ok()); let c = workspace.as_ref().zip(origin.clone()).map( |(workspace, origin)| { + let dh = std::panic::take_hook(); + let main = std::thread::current_id(); // let mut c = Command::new("rust-analyzer") // .stdin(Stdio::piped()) // .stdout(Stdio::piped()) @@ -221,9 +224,15 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { std::thread::Builder::new() .name("Rust Analyzer".into()) .stack_size(1024 * 1024 * 8) - .spawn(|| { - std::panic::set_hook(Box::new(|info| { - println!("RA panic @ {}", info.location().unwrap()); + .spawn(move || { + let ra = std::thread::current_id(); + std::panic::set_hook(Box::new(move |info| { + // iz + if std::thread::current_id() == main { + dh(info); + } else if std::thread::current_id() == ra || std::thread::current().name().is_some_and(|x| x.starts_with("RA")) { + println!("RA panic @ {}", info.location().unwrap()); + } })); rust_analyzer::bin::run_server(b) }) @@ -380,8 +389,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { for rq in l.req_rx.try_iter() { match rq { LRq { method: "workspace/diagnostic/refresh", .. } => { - let x = l.pull_diag(o.into(), diag.result.clone()); - diag.request(l.runtime.spawn(x)); + // let x = l.pull_diag(o.into(), diag.result.clone()); + // diag.request(l.runtime.spawn(x)); }, rq => log::debug!("discarding request {rq:?}"), @@ -398,11 +407,17 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { }; if let State::CodeAction(x) = &mut state { x.poll(|x, _| { - Some(act::CodeActions {selection:0, vo:0, inner: x.ok()??.into_iter().map(|x| match x { + let lems: Vec<CodeAction> = x.ok()??.into_iter().map(|x| match x { CodeActionOrCommand::CodeAction(x) => x, _ => panic!("alas we dont like these"), - }).collect(),}) - },&l.runtime); + }).collect(); + if lems.is_empty() { + bar.last_action = "no code actions available".into(); + None + } else { + Some(act::CodeActions::new(lems)) + } + },&l.runtime); } def.poll(|x, _| x.ok().flatten().and_then(|x| match &x { @@ -704,7 +719,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { 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); - for b in x.replace('\n', "\r\n").bytes(){ t.rx(b,std::fs::File::open("/dev/null").unwrap().as_fd()); } + for b in x.replace('\n', "\r\n").replace("⸬", ":").replace("/home/os", "").bytes(){ t.rx(b,std::fs::File::open("/dev/null").unwrap().as_fd()); } let y_lim = t.cells.rows().position(|x| x.iter().all(_.letter.is_none())).unwrap_or(20); let c =t.cells.c() as usize; let Some(x_lim) = t.cells.rows().map(_.iter().rev().take_while(_.letter.is_none()).count()).map(|x| @@ -756,14 +771,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { State::CodeAction(Rq{ result :Some(x), ..}) => 'out: { let m = x.maxc(); let c = x.write(m); - dbg!(&c); let (_x, _y) = text.cursor_visual(); 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, mut h)) = place_around((_x, _y), &mut fonts, i.as_mut(), &c, m, ppem, ls, 0., 0., 0.)else { println!("ra?"); break 'out}; - dbg!(c); i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), w as _,h as _, BORDER); }, _ =>{}, @@ -1039,6 +1052,7 @@ hovering.request = (DropH::new(handle), cursor_position).into(); if let Some((lsp, path)) = lsp!() { sig_help.request(lsp.runtime.spawn(window.redraw_after(lsp.request_sig_help(path, text.cursor())))); } + hist.last.cursor = text.cursor; text.setc(); } Some(Do::ExtendSelectionToMouse) => { @@ -1145,8 +1159,14 @@ hovering.request = (DropH::new(handle), cursor_position).into(); Some(Do::CodeAction) => { if let Some((lsp, f)) = lsp!() { let r = lsp.request::<lsp_request!("textDocument/codeAction")>(&CodeActionParams { - text_document: f.tid(), range: text.to_l_range(text.beginning_of_line(text.cursor).unwrap() - ..text.eol(text.cursor)).unwrap(), context: CodeActionContext { trigger_kind: Some(CodeActionTriggerKind::INVOKED), ..default() }, work_done_progress_params: default(), partial_result_params: default() }).unwrap(); + text_document: f.tid(), range: text.to_l_range(text.cursor..text.cursor).unwrap(), context: CodeActionContext { + trigger_kind: Some(CodeActionTriggerKind::INVOKED), + // diagnostics: if let Some((lsp, p)) = lsp!() && let uri = Url::from_file_path(p).unwrap() && let Some(diag) = lsp.diagnostics.get(&uri, &lsp.diagnostics.guard()) { dbg!(diag.iter().filter(|x| { + + // text.l_range(x.range).unwrap().contains(&text.cursor) + // }).cloned().collect()) } else { vec![] }, + ..default() + }, work_done_progress_params: default(), partial_result_params: default() }).unwrap(); let mut r2 = Rq::default(); r2.request(lsp.runtime.spawn( @@ -1158,29 +1178,32 @@ hovering.request = (DropH::new(handle), cursor_position).into(); ); } } - Some(Do::CASelect(act)) if let Some((lsp,f)) = lsp!() => { + Some(Do::CASelectLeft) => { + let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()}; + c.left(); + } + Some(Do::CASelectRight) =>'out: { + let Some((lsp,f)) = lsp!() else {unreachable!()}; + let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()}; + let Some(act) = c.right() else { break 'out }; + let act = act.clone(); + state = State::Default; + hist.last.cursor = text.cursor; hist.test_push(&text); - let act = act.sel(); let act = lsp.runtime.block_on( lsp.request::<CodeActionResolveRequest>(&act).unwrap().0 ).unwrap(); let mut f_ = |edits: &[SnippetTextEdit]|{ - let mut first = false; + // 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(); - } + text.apply_adjusting(text_edit).unwrap(); } } - first = false; } }; match act.edit { @@ -1219,14 +1242,13 @@ hovering.request = (DropH::new(handle), cursor_position).into(); change!(); hist.record(&text); } - Some(Do::CASelect(_)) => {} Some(Do::CASelectNext) => { let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()}; - c.next(); + c.down(); } Some(Do::CASelectPrev) => { let State::CodeAction(Rq{ result: Some(c), .. }) = &mut state else { panic!()}; - c.back(); + c.up(); } Some(Do::Reinsert | Do::GoToDefinition) => panic!(), Some(Do::Save) => match &origin { @@ -1577,11 +1599,12 @@ Default => { K(_) => _ [Edit], M(_) => _, }, -CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => Rq { result : Some(act), request: None, }) => { +CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,()> => Rq { result : Some(_x), request: None, }) => { K(Key::Named(Tab) if shift()) => _ [CASelectPrev], K(Key::Named(ArrowDown | Tab)) => _ [CASelectNext], K(Key::Named(ArrowUp)) => _ [CASelectPrev], - K(Key::Named(Enter)) => Default [CASelect(act::CodeActions => act)], + K(Key::Named(Enter | ArrowRight)) => _ [CASelectRight], + K(Key::Named(ArrowLeft)) => _ [CASelectLeft], }, CodeAction(Rq<act::CodeActions, Option<CodeActionResponse>,(), RequestError<lsp_request!("textDocument/codeAction")>> => rq) => { K(Key::Named(Escape)) => Default, diff --git a/src/text.rs b/src/text.rs index 80d30b2..0c963fc 100644 --- a/src/text.rs +++ b/src/text.rs @@ -487,7 +487,7 @@ impl TextArea { self.vo += x.new_text.chars().filter(|&x| x == '\n').count(); } - let removed = dbg!(e - b); + let removed = e - b; self.cursor += x.new_text.chars().count(); self.cursor -= removed; // compensate // text.cursor += additional.new_text.chars().count(); // compensate |