A simple CPU rendered GUI IDE experience.
try rootcause
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/commands.rs | 8 | ||||
| -rw-r--r-- | src/edi.rs | 142 | ||||
| -rw-r--r-- | src/edi/st.rs | 1 | ||||
| -rw-r--r-- | src/git.rs | 2 | ||||
| -rw-r--r-- | src/lsp/client.rs | 87 | ||||
| -rw-r--r-- | src/lsp/rq.rs | 2 | ||||
| -rw-r--r-- | src/text.rs | 54 | ||||
| -rw-r--r-- | src/trm.rs | 54 |
9 files changed, 209 insertions, 143 deletions
@@ -43,7 +43,6 @@ crossbeam = { version = "0.8.4", features = ["nightly", "crossbeam-channel"] } test-log = "0.2.18" env_logger = "0.11.8" url = "2.5.7" -anyhow = "1.0.100" tokio = { version = "1.47.1", features = ["rt-multi-thread", "sync", "time"] } regex-cursor = "0.1.5" papaya = "0.2.3" @@ -70,6 +69,7 @@ itern = "0.1.1" kitty-rc = { version = "0.4.2", git = "https://github.com/bend-n/kitty-rc-rs" } smol_str = "0.3.6" futures = "0.3.32" +rootcause = "0.12.1" [profile.dev.package] rust-analyzer.opt-level = 3 fimg.opt-level = 3 diff --git a/src/commands.rs b/src/commands.rs index aed4a00..5b99581 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,10 +5,10 @@ use std::sync::Arc; use Default::default; use Into::into; -use anyhow::{anyhow, bail}; use dsb::Cell; use dsb::cell::Style; use lsp_types::*; +use rootcause::{bail, report}; use rust_analyzer::lsp::ext::*; use crate::FG; @@ -81,6 +81,8 @@ commands!( @ RAOpenCargoToml: "open-cargo-toml", /// Runs the test at the cursor @ RARunTest: "run-test", + // /// View child modules + // @ ViewChildModules: "child-modules", /// GoTo line, | GoTo(u32): "g", ); @@ -194,7 +196,7 @@ impl Editor { &mut self, z: Cmd, w: Arc<dyn winit::window::Window>, - ) -> anyhow::Result<()> { + ) -> rootcause::Result<()> { match z { Cmd::GoTo(Some(x)) => if let Ok(x) = self.text.try_line_to_char(x as _) { @@ -265,7 +267,7 @@ impl Editor { })?; self.text .apply_tedits(&mut { teds }) - .map_err(|_| anyhow!("couldnt apply edits"))?; + .map_err(|_| report!("couldnt apply edits"))?; } Cmd::RADocs => { let u = l.request_immediate::<ExternalDocs>( @@ -8,12 +8,13 @@ use std::sync::Arc; use std::time::SystemTime; use Default::default; -use anyhow::anyhow; use implicit_fn::implicit_fn; use lsp_server::{Connection, Request as LRq, ResponseError}; use lsp_types::request::*; use lsp_types::*; use regex::Regex; +use rootcause::handlers::Debug; +use rootcause::report; use ropey::Rope; use rust_analyzer::lsp::ext::OnTypeFormatting; use rust_fsm::StateMachine; @@ -80,7 +81,8 @@ pub fn deserialize_tokens<'de, D: serde::Deserializer<'de>>( #[derive(Default, Debug, Serialize, Deserialize)] pub struct Requests { - pub hovering: Rq<Hovr, Option<Hovr>, (usize, usize), anyhow::Error>, + pub hovering: + Rq<Hovr, Option<Hovr>, (usize, usize), RequestError<HoverRequest>>, pub document_highlights: Rq< Vec<DocumentHighlight>, Vec<DocumentHighlight>, @@ -103,7 +105,12 @@ pub struct Requests { (), RequestError<SemanticTokensFullRequest>, >, - pub diag: Rq<String, Option<String>, (), anyhow::Error>, + pub diag: Rq< + String, + Option<String>, + (), + RequestError<DocumentDiagnosticRequest>, + >, #[serde(skip)] pub inlay: Rq< Vec<InlayHint>, @@ -391,10 +398,10 @@ impl Editor { lsp!(self + p).map(|(l, o)| { let v = l.runtime.block_on(l.format(o)).unwrap(); if let Some(v) = v { - if let Err(()) = + if let Err(x) = self.text.apply_tedits_adjusting(&mut { v }) { - eprintln!("unhappy fmt") + eprintln!("unhappy fmt {x}") } } // self.text.cursor = @@ -695,7 +702,7 @@ impl Editor { .unwrap(); // println!("rq hov of {hover:?} (cur {})", requests.hovering.request.is_some()); let handle: tokio::task::JoinHandle< - Result<Option<Hovr>, anyhow::Error>, + Result<Option<Hovr>, _>, > = cl.runtime.spawn(async move { let Some(x) = rx.await? else { return Ok(None::<Hovr>); @@ -756,7 +763,7 @@ impl Editor { ] }) }); - anyhow::Ok(Some( + Ok(Some( hov::Hovr { span, item: text::CellBuffer { @@ -941,11 +948,13 @@ impl Editor { change!(self, window.clone()); } Some(Do::SpawnTerminal) => { - trm::toggle( + if let Err(e) = trm::toggle( self.workspace .as_deref() .unwrap_or(Path::new("/home/os/")), - ); + ) { + log::error!("opening terminal failed {e}"); + } } Some(Do::MatchingBrace) => { if let Some((l, f)) = lsp!(self + p) { @@ -1121,14 +1130,17 @@ impl Editor { } Some(Do::SymbolsSelect(x)) => { if let Some(Ok(x)) = x.sel() - && let Err(e) = try bikeshed anyhow::Result<()> { + && let Err(e) = try bikeshed rootcause::Result<()> { let r = match x.at { sym::GoTo::Loc(x) => { let x = x.clone(); let f = x .uri .to_file_path() - .map_err(|()| anyhow!("dammit"))? + .map_err(|()| { + report!("provided uri not path") + .context(x.uri) + })? .canonicalize()?; self.state = State::Default; self.requests.complete = @@ -1140,10 +1152,10 @@ impl Editor { } sym::GoTo::R(range) => range, }; - let p = self - .text - .l_position(r.start) - .ok_or(anyhow!("rah"))?; + let p = self.text.l_position(r.start).ok_or( + report!("provided range out of bound") + .context_custom::<Debug, _>(r), + )?; if p != 0 { self.text.cursor.just(p, &self.text.rope); } @@ -1649,43 +1661,17 @@ impl Editor { self.hist.push_if_changed(&mut self.text); change!(self, window.clone()); } - Some(Do::Paste) => { + Some(Do::PasteOver) => { self.hist.push_if_changed(&mut self.text); - let r = clipp::paste(); - if unsafe { META.hash == hash(&r) } { - let bounds = unsafe { &*META.splits }; - let pieces = bounds.windows(2).map(|w| unsafe { - std::str::from_utf8_unchecked( - &r.as_bytes()[w[0]..w[1]], - ) - }); - if unsafe { META.count } - == self.text.cursor.iter().len() - { - for (piece, cursor) in - pieces.zip(0..self.text.cursor.iter().count()) - { - let c = self - .text - .cursor - .iter() - .nth(cursor) - .unwrap(); - self.text.insert_at(*c, piece).unwrap(); - } - } else { - let new = - pieces.intersperse("\n").collect::<String>(); - // vscode behaviour: insane? - self.text.insert(&new); - eprintln!("hrmst"); - } - } else { - self.text.insert(&clipp::paste()); - } - self.hist.push_if_changed(&mut self.text); - change!(self, window.clone()); + ceach!(self.text.cursor, |cursor| { + let Some(r) = cursor.sel else { return }; + _ = self.text.remove(r.into()); + }); + self.text.cursor.clear_selections(); + self.paste(); + // self.hist.push_if_changed(&mut self.text); } + Some(Do::Paste) => self.paste(), Some(Do::OpenFile(x)) => { _ = self.open(Path::new(&x), window.clone()); } @@ -1823,11 +1809,39 @@ impl Editor { self.hist.record(&self.text); } + pub fn paste(&mut self) { + self.hist.push_if_changed(&mut self.text); + let r = clipp::paste(); + if unsafe { META.hash == hash(&r) } { + let bounds = unsafe { &*META.splits }; + let pieces = bounds.windows(2).map(|w| unsafe { + std::str::from_utf8_unchecked(&r.as_bytes()[w[0]..w[1]]) + }); + if unsafe { META.count } == self.text.cursor.iter().len() { + for (piece, cursor) in + pieces.zip(0..self.text.cursor.iter().count()) + { + let c = self.text.cursor.iter().nth(cursor).unwrap(); + self.text.insert_at(*c, piece).unwrap(); + } + } else { + let new = pieces.intersperse("\n").collect::<String>(); + // vscode behaviour: insane? + self.text.insert(&new); + eprintln!("hrmst"); + } + } else { + self.text.insert(&clipp::paste()); + } + self.hist.push_if_changed(&mut self.text); + change!(self, window.clone()); + } + pub fn open( &mut self, x: &Path, w: Arc<dyn Window>, - ) -> anyhow::Result<()> { + ) -> rootcause::Result<()> { let x = x.canonicalize()?.to_path_buf(); if Some(&*x) == self.origin.as_deref() { self.bar.last_action = "didnt open".into(); @@ -1863,7 +1877,7 @@ impl Editor { )>, w: Option<Arc<dyn Window>>, ws: Option<PathBuf>, - ) -> anyhow::Result<()> { + ) -> rootcause::Result<()> { if let Some(x) = self.files.remove(x) { let f = take(&mut self.files); *self = x; @@ -1874,7 +1888,9 @@ impl Editor { self.hist.push_if_changed(&mut self.text); self.text.rope = Rope::from_str( &std::fs::read_to_string( - self.origin.as_ref().unwrap(), + self.origin + .as_ref() + .ok_or(report!("origin missing"))?, ) .unwrap(), ); @@ -1892,9 +1908,9 @@ impl Editor { } self.lsp = lsp; - lsp!(self + p).map(|(x, origin)| { - x.open(&origin, self.text.rope.to_string()).unwrap(); - }); + if let Some((x, origin)) = lsp!(self + p) { + x.open(&origin, self.text.rope.to_string())?; + } } else { self.workspace = ws; self.origin = Some(x.to_path_buf()); @@ -1908,16 +1924,14 @@ impl Editor { self.mtime = Self::modify(self.origin.as_deref()); self.lsp = lsp; - lsp!(self + p).map(|(x, origin)| { + if let Some((x, origin)) = lsp!(self + p) { take(&mut self.requests); - x.open(&origin, new).unwrap(); - + x.open(&origin, new)?; x.rq_semantic_tokens( &mut self.requests.semantic_tokens, origin, - ) - .unwrap(); - }); + )?; + } } self.set_title(w); Ok(()) @@ -1937,7 +1951,7 @@ impl Editor { .map(|[wo, or]| format!("gracilaria - {wo} - {or}")) } - pub fn store(&mut self) -> anyhow::Result<()> { + pub fn store(&mut self) -> rootcause::Result<()> { let ws = self.workspace.clone(); let tree = self.tree.clone(); let mtime = self.mtime.clone(); @@ -2014,6 +2028,8 @@ pub fn handle2<'a>( text.cursor.just(text.rope.len_chars(), &text.rope); text.vo = text.l().saturating_sub(text.r); } + Character("e") if alt() => text.end(), + Character("q") if alt() => text.home(), Named(Home) => text.home(), Named(End) => text.end(), Named(Tab) => text.tab(), diff --git a/src/edi/st.rs b/src/edi/st.rs index 0db1017..62149b9 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -138,6 +138,7 @@ Selection => { K(Key::Named(Backspace)) => Default [Delete], K(Key::Character("x") if ctrl()) => Default [Cut], K(Key::Character("c") if ctrl()) => Default [Copy], + K(Key::Character("v") if ctrl()) => Default [PasteOver], K(Key::Character("/") if ctrl()) => Default [Comment(State::Selection)], M(_) => _, @@ -20,7 +20,7 @@ pub fn diff( p: &Path, ws: &Path, now: &Rope, -) -> Result<imara_diff::Diff, anyhow::Error> { +) -> rootcause::Result<imara_diff::Diff> { let f = String::from_utf8(load(p, ws)?)?; let now = now.to_string(); let i = InternedInput::new(&*f, &now); diff --git a/src/lsp/client.rs b/src/lsp/client.rs index b6b5bea..a137b37 100644 --- a/src/lsp/client.rs +++ b/src/lsp/client.rs @@ -4,7 +4,6 @@ use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering::Relaxed; use Default::default; -use anyhow::bail; use crossbeam::channel::{Receiver, SendError, Sender}; use futures::FutureExt; use log::debug; @@ -152,34 +151,50 @@ impl Client { pub fn _pull_all_diag( &self, _f: PathBuf, - ) -> impl Future<Output = anyhow::Result<()>> { + ) -> impl Future< + Output = Result<(), RequestError<WorkspaceDiagnosticRequest>>, + > { let r = self .request::<lsp_request!("workspace/diagnostic")>(&default()) .unwrap() .0; - log::info!("pulling diagnostics"); + + // log::info!("pulling diagnostics"); async move { let x = r.await?; log::info!("{x:?}"); // match x { - // DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(RelatedFullDocumentDiagnosticReport { - // related_documents, - // full_document_diagnostic_report:FullDocumentDiagnosticReport { items,.. }, - // })) => { + // DocumentDiagnosticReportResult::Report( + // DocumentDiagnosticReport::Full( + // RelatedFullDocumentDiagnosticReport { + // related_documents, + // full_document_diagnostic_report: + // FullDocumentDiagnosticReport { items, .. }, + // }, + // ), + // ) => { // let l = self.diagnostics.guard(); // self.diagnostics.insert(f.tid().uri, items, &l); - // for (uri, rel) in related_documents.into_iter().flatten() { + // for (uri, rel) in + // related_documents.into_iter().flatten() + // { // match rel { - // DocumentDiagnosticReportKind::Full(FullDocumentDiagnosticReport { items, .. }) => { + // DocumentDiagnosticReportKind::Full( + // FullDocumentDiagnosticReport { + // items, .. + // }, + // ) => { // self.diagnostics.insert(uri, items, &l); - // }, - // DocumentDiagnosticReportKind::Unchanged(_) => {}, + // } + // DocumentDiagnosticReportKind::Unchanged(_) => { + // } // } // } // log::info!("pulled diagnostics"); - // }, + // } // _ => bail!("fuck that"), // }; + Ok(()) } } @@ -187,7 +202,12 @@ impl Client { &self, f: PathBuf, previous: Option<String>, - ) -> impl Future<Output = anyhow::Result<Option<String>>> { + ) -> impl Future< + Output = Result< + Option<String>, + RequestError<DocumentDiagnosticRequest>, + >, + > { let p = DocumentDiagnosticParams { text_document: f.tid(), identifier: try { @@ -219,7 +239,7 @@ impl Client { Err(RequestError::Cancelled(_, y)) if y.retrigger_request => { self.request::<lsp_request!("textDocument/diagnostic")>(&p,).unwrap().0.await? }, - Err(e) => return Err(e.into()), + Err(e) => return Err(e), }; // dbg!(&x); match x.clone() { @@ -255,7 +275,7 @@ impl Client { log::info!("pulled diagnostics"); Ok(result_id) } - _ => bail!("fuck that"), + _ => unimplemented!(), } } } @@ -423,21 +443,20 @@ impl Client { RequestError<SemanticTokensFullRequest>, >, f: &Path, - ) -> anyhow::Result<()> { + ) -> Result<(), RequestError<SemanticTokensFullRequest>> { debug!("requested semantic tokens"); let Some(b"rs") = f.extension().map(|x| x.as_encoded_bytes()) else { return Ok(()); }; - let (rx, _) = self - .request_::<SemanticTokensFullRequest, { Redraw }>( - &SemanticTokensParams { - work_done_progress_params: default(), - partial_result_params: default(), - text_document: f.tid(), - }, - )?; + let (rx, _) = self.request::<SemanticTokensFullRequest>( + &SemanticTokensParams { + work_done_progress_params: default(), + partial_result_params: default(), + text_document: f.tid(), + }, + )?; let x = self.runtime.spawn(async move { let t = rx.await; let y = t?.unwrap(); @@ -491,6 +510,26 @@ impl Client { }) .map(|(x, _)| x) } + + pub fn _child_modules( + &'static self, + p: Position, + t: &Path, + ) -> Result< + impl Future< + Output = Result< + <ChildModules as Request>::Result, + RequestError<ChildModules>, + >, + >, + SendError<Message>, + > { + self.request::<ChildModules>(&TextDocumentPositionParams { + position: p, + text_document: t.tid(), + }) + .map(|x| x.0) + } } pub trait PathURI { diff --git a/src/lsp/rq.rs b/src/lsp/rq.rs index 18c6616..97ed4c5 100644 --- a/src/lsp/rq.rs +++ b/src/lsp/rq.rs @@ -48,7 +48,7 @@ impl<X> From<oneshot::error::RecvError> for RequestError<X> { Self::Rx(PhantomData) } } -impl<X: Request + Debug> std::error::Error for RequestError<X> { +impl<X: Request> std::error::Error for RequestError<X> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } diff --git a/src/text.rs b/src/text.rs index 2fa0c26..e0ab239 100644 --- a/src/text.rs +++ b/src/text.rs @@ -8,7 +8,6 @@ use std::pin::pin; use std::sync::LazyLock; use std::vec::Vec; -use anyhow::anyhow; use atools::prelude::*; use dsb::Cell; use dsb::cell::Style; @@ -19,6 +18,8 @@ use lsp_types::{ DocumentSymbol, Location, Position, SemanticTokensLegend, SnippetTextEdit, TextEdit, }; +use rootcause::prelude::{IteratorExt, ResultExt}; +use rootcause::report; use ropey::{Rope, RopeSlice}; use serde::{Deserialize, Serialize}; use tree_house::Language; @@ -32,7 +33,6 @@ pub mod hist; pub mod theme_treesitter; use hist::Changes; -use crate::lsp::Void; use crate::sni::{Snippet, StopP}; use crate::text::hist::Action; @@ -454,15 +454,23 @@ impl TextArea { self.set_ho(); } - 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.remove(begin..end).void()?; - self.insert_at(begin, &x.new_text).void()?; + pub fn apply( + &mut self, + x: &TextEdit, + ) -> rootcause::Result<(usize, usize)> { + let begin = + self.l_position(x.range.start).ok_or(report!("no range"))?; + let end = + self.l_position(x.range.end).ok_or(report!("no range"))?; + self.remove(begin..end)?; + self.insert_at(begin, &x.new_text)?; Ok((begin, end)) } - pub fn apply_adjusting(&mut self, x: &TextEdit) -> Result<(), ()> { + pub fn apply_adjusting( + &mut self, + x: &TextEdit, + ) -> rootcause::Result<()> { let (_b, e) = self.apply(&x)?; if e < self.cursor.first().position { @@ -503,36 +511,37 @@ impl TextArea { pub fn apply_snippet_tedit( &mut self, SnippetTextEdit { range,new_text, insert_text_format, .. }: &SnippetTextEdit, - ) -> anyhow::Result<()> { + ) -> rootcause::Result<()> { match insert_text_format { Some(lsp_types::InsertTextFormat::SNIPPET) => self .apply_snippet(&TextEdit { range: range.clone(), new_text: new_text.clone(), - }) - .unwrap(), + })?, _ => { self.apply_adjusting(&TextEdit { range: range.clone(), new_text: new_text.clone(), - }) - .unwrap(); + })?; } } Ok(()) } - pub fn apply_snippet(&mut self, x: &TextEdit) -> anyhow::Result<()> { + pub fn apply_snippet( + &mut self, + x: &TextEdit, + ) -> rootcause::Result<()> { let begin = self .l_position(x.range.start) - .ok_or(anyhow!("couldnt get start"))?; + .ok_or(report!("couldnt get start"))?; let end = self .l_position(x.range.end) - .ok_or(anyhow!("couldnt get end"))?; + .ok_or(report!("couldnt get end"))?; self.remove(begin..end)?; let (mut sni, tex) = crate::sni::Snippet::parse(&x.new_text, begin) - .ok_or(anyhow!("failed to parse snippet"))?; + .ok_or(report!("failed to parse snippet"))?; self.insert_at(begin, &tex)?; self.cursor.one(match sni.next() { Some(x) => { @@ -1115,7 +1124,7 @@ impl TextArea { pub(crate) fn apply_tedits_adjusting( &mut self, teds: &mut [TextEdit], - ) -> Result<(), ()> { + ) -> rootcause::Result<()> { teds.sort_tedits(); for ted in teds { self.apply_adjusting(ted)?; @@ -1126,11 +1135,12 @@ impl TextArea { pub(crate) fn apply_tedits( &mut self, teds: &mut [TextEdit], - ) -> Result<(), ()> { + ) -> rootcause::Result<(), &'static str> { teds.sort_tedits(); - for ted in teds { - self.apply(ted)?; - } + teds.iter() + .map(|x| self.apply(x).map(drop)) + .collect_reports::<(), _>() + .context("couldnt apply one or more tedits")?; Ok(()) } } @@ -78,33 +78,31 @@ pub fn runk(niri: &mut Socket, at: &Path) -> std::io::Result<()> { Ok(()) } -pub fn toggle(at: &Path) { - _ = try bikeshed anyhow::Result<()> { - let l = LAST.load(Relaxed); - let mut niri = niri::socket::Socket::connect()?; - if is_running(&mut niri)? { - _ = - niri.send(Request::Action(Action::FocusWindow { id: l }))?; +pub fn toggle(at: &Path) -> rootcause::Result<()> { + let l = LAST.load(Relaxed); + let mut niri = niri::socket::Socket::connect()?; + if is_running(&mut niri)? { + _ = niri.send(Request::Action(Action::FocusWindow { id: l }))?; - // std::thread::sleep(Duration::from_millis(500)); - // let Ok(Ok(Response::FocusedWindow(Some(x)))) = niri.send(Request::FocusedWindow) else { unreachable!() }; - // dbg!(&x); - // if x.layout.window_size.1 < 200 { - _ = niri.send(Request::Action(Action::SetColumnDisplay { - display: niri::ColumnDisplay::Normal, - }))?; - _ = niri.send(Request::Action(Action::SetWindowHeight { - id: Some(l), - change: niri::SizeChange::SetFixed(400), - }))?; - // } - return; - } - // let Ok(Ok(Response::FocusedWindow(Some(focused)))) = - // niri.send(Request::FocusedWindow) - // else { - // unreachable!() - // }; - runk(&mut niri, at)?; - }; + // std::thread::sleep(Duration::from_millis(500)); + // let Ok(Ok(Response::FocusedWindow(Some(x)))) = niri.send(Request::FocusedWindow) else { unreachable!() }; + // dbg!(&x); + // if x.layout.window_size.1 < 200 { + _ = niri.send(Request::Action(Action::SetColumnDisplay { + display: niri::ColumnDisplay::Normal, + }))?; + _ = niri.send(Request::Action(Action::SetWindowHeight { + id: Some(l), + change: niri::SizeChange::SetFixed(400), + }))?; + // } + return Ok(()); + } + // let Ok(Ok(Response::FocusedWindow(Some(focused)))) = + // niri.send(Request::FocusedWindow) + // else { + // unreachable!() + // }; + runk(&mut niri, at)?; + Ok(()) } |