A simple CPU rendered GUI IDE experience.
Diffstat (limited to 'src/lsp/communication.rs')
| -rw-r--r-- | src/lsp/communication.rs | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/lsp/communication.rs b/src/lsp/communication.rs new file mode 100644 index 0000000..8899f0f --- /dev/null +++ b/src/lsp/communication.rs @@ -0,0 +1,227 @@ +use std::backtrace::Backtrace; +use std::collections::HashMap; +use std::mem::forget; +use std::sync::Arc; +use std::time::Instant; + +use crossbeam::channel::{Receiver, RecvError, SendError, Sender}; +use log::{debug, error, trace}; +use lsp_server::{ + ErrorCode, Message, Notification as N, Request as LRq, Response as Re, + ResponseError, +}; +use lsp_types::notification::*; +use lsp_types::request::*; +use lsp_types::*; +use tokio::sync::oneshot; +use winit::window::Window; + +use crate::lsp::BehaviourAfter::{self, *}; +use crate::lsp::RequestError; +pub fn handler( + window_rx: oneshot::Receiver<Arc<dyn Window + 'static>>, + progress: &papaya::HashMap< + NumberOrString, + Option<(WorkDoneProgress, WorkDoneProgressBegin)>, + >, + _req_tx: Sender<LRq>, + d: &papaya::HashMap<Url, Vec<Diagnostic>>, + not_tx: Sender<N>, + rx: Receiver<Message>, + req_rx: Receiver<(i32, oneshot::Sender<Re>, BehaviourAfter)>, +) { + let mut map = HashMap::new(); + let w = window_rx.blocking_recv().unwrap(); + loop { + crossbeam::select! { + recv(req_rx) -> x => match x { + Ok((x, y, and)) => { + debug!("received request {x}"); + assert!(map.insert(x, (y, Instant::now(), and)).is_none()); + } + Err(RecvError) => return, + }, + recv(rx) -> x => match x { + Ok(Message::Request(rq @ LRq { method: "window/workDoneProgress/create", .. })) => { + match rq.load::<WorkDoneProgressCreate>() { + Ok((_, x)) => { + let g = progress.guard(); + progress.insert(x.token, None, &g); + }, + Err(lsp_server::ExtractError::MethodMismatch(..)) => {}, + e => { + error!("{e:?}"); + } + }; + } + Ok(Message::Request(x)) => { + if let Err(e) = _req_tx.send(x) { + let m = e.to_string(); + error!("couldnt receive request {m}: {:?}", e.into_inner()); + } + } + Ok(Message::Response(x)) => { + 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, _, t)) = map.remove(&x.id.i32()) { + log::info!("request {} cancelled", x.id); + _ = s.send(x); + if t == Redraw { w.request_redraw() } + } + } else { + if let Some((s, _, t)) = map.remove(&x.id.i32()) { + _ = s.send(x.clone()); + if t == Redraw { w.request_redraw() } + trace!("received error from lsp for response {x:?}"); + } else { + error!("received error from lsp for response {x:?}"); + } + } + } + else if let Some((s, took, t)) = map.remove(&x.id.i32()) { + log::info!("request {} took {:?}", x.id, took.elapsed()); + match s.send(x) { + Ok(()) => {} + Err(e) => { + error!( + "unable to respond to {e:?}", + ); + } + } + // w.request_redraw(); + if t == Redraw { w.request_redraw() } + } else { + error!("request {x:?} was dropped.") + } + } + Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => { + debug!("got diagnostics"); + match rq.load::<PublishDiagnostics>() { + Ok(x) => { + d.insert(x.uri, x.diagnostics, &d.guard()); + w.request_redraw(); + }, + e => error!("{e:?}"), + } + }, + Ok(Message::Notification(x @ N { method: "$/progress", .. })) => { + let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap(); + match x.clone() { + WorkDoneProgress::Begin(y) => { + progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard()); + }, + WorkDoneProgress::Report(_) | WorkDoneProgress::End(_) => { + progress.update(token, move |v| Some((x.clone(), v.clone().expect("evil lsp").1)), &progress.guard()); + } + } + w.request_redraw(); + } + Ok(Message::Notification(notification)) => { + debug!("rx {notification:?}"); + not_tx + .send(notification) + .expect("why library drop this??? no drop!!"); + } + Err(RecvError) => return, + } + } + } +} +impl super::Client { + pub fn notify<X: Notification>( + &self, + y: &X::Params, + ) -> Result<(), SendError<Message>> { + self.tx.send(Message::Notification(N { + method: X::METHOD.into(), + params: serde_json::to_value(y).unwrap(), + })) + } + pub fn cancel(&self, rid: i32) { + _ = self.notify::<Cancel>(&CancelParams { id: rid.into() }); + } + pub fn request_immediate<'me, X: Request>( + &'me self, + y: &X::Params, + ) -> Result<X::Result, RequestError<X>> { + self.runtime.block_on(self.request_::<X, { Nil }>(y)?.0) + } + + pub fn request<'me, X: Request>( + &'me self, + y: &X::Params, + ) -> Result< + ( + impl Future<Output = Result<X::Result, RequestError<X>>> + + use<'me, X>, + i32, + ), + SendError<Message>, + > { + self.request_::<X, { Redraw }>(y) + } + #[must_use] + pub(super) fn request_<'me, X: Request, const THEN: BehaviourAfter>( + &'me self, + y: &X::Params, + ) -> Result< + ( + impl Future<Output = Result<X::Result, RequestError<X>>> + + use<'me, X, THEN>, + i32, + ), + SendError<Message>, + > { + let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + self.tx.send(Message::Request(LRq { + id: id.into(), + method: X::METHOD.into(), + params: serde_json::to_value(y).unwrap(), + }))?; + let (tx, rx) = oneshot::channel(); + if self.initialized.is_some() { + debug!("sent request {} ({id})'s handler", X::METHOD); + self.send_to + .send((id, tx, THEN)) + .expect("oughtnt really fail"); + } + Ok(( + async move { + let g = scopeguard::guard((), |()| { + self.cancel(id); + }); + + let mut x = rx.await?; + forget(g); + if let Some(ResponseError { code, ref mut data, .. }) = + x.error + { + 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, + Some(Backtrace::capture()), + )) + } + } else { + Ok(serde_json::from_value::<X::Result>( + x.result.clone().unwrap_or_default(), + ) + .unwrap_or_else(|_| { + panic!( + "lsp failure for {x:?}\ndidnt follow spec \ + for {}\npossibly spec issue", + X::METHOD + ) + })) + } + }, + id, + )) + } +} |