Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'src/lsp/communication.rs')
| -rw-r--r-- | src/lsp/communication.rs | 74 |
1 files changed, 61 insertions, 13 deletions
diff --git a/src/lsp/communication.rs b/src/lsp/communication.rs index 4c4f49d..efc44ee 100644 --- a/src/lsp/communication.rs +++ b/src/lsp/communication.rs @@ -7,22 +7,29 @@ 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, + ErrorCode, ExtractError, Message, Notification as N, Request as LRq, + RequestId, Response as Re, ResponseError, }; -use lsp_types::notification::*; -use lsp_types::request::*; use lsp_types::*; +use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; use tokio::time::error::Elapsed; +use url::Url; use winit::window::Window; +#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)] +#[serde(untagged)] +pub enum WorkDoneProgress { + Begin(WorkDoneProgressBegin), + Report(WorkDoneProgressReport), + End(WorkDoneProgressEnd), +} use crate::lsp::BehaviourAfter::{self, *}; use crate::lsp::{RequestError, RqSendError}; pub fn handler( window_rx: oneshot::Receiver<Arc<dyn Window + 'static>>, progress: &papaya::HashMap< - NumberOrString, + ProgressToken, Option<(WorkDoneProgress, WorkDoneProgressBegin)>, >, _req_tx: Sender<LRq>, @@ -40,13 +47,13 @@ pub fn handler( Ok((.., BehaviourAfter::RedrawNow)) => w.request_redraw(), Ok((x, y, and)) => { debug!("received request {x}"); - assert!(map.insert(x, (y, Instant::now(), and)).is_none()); + assert!(map.insert(RequestId::from(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>() { + match load_rq::<lsp_request!("window/workDoneProgress/create")>(rq) { Ok((_, x)) => { let g = progress.guard(); progress.insert(x.token, None, &g); @@ -67,13 +74,13 @@ pub fn handler( 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()) { + if let Some((s, _, t)) = map.remove(&x.id) { 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()) { + if let Some((s, _, t)) = map.remove(&x.id) { _ = s.send(x.clone()); if t == Redraw { w.request_redraw() } trace!("received error from lsp for response {x:?}"); @@ -82,7 +89,7 @@ pub fn handler( } } } - else if let Some((s, took, t)) = map.remove(&x.id.i32()) { + else if let Some((s, took, t)) = map.remove(&x.id) { log::debug!("request {} took {:?}", x.id, took.elapsed()); match s.send(x) { Ok(()) => {} @@ -100,7 +107,7 @@ pub fn handler( } Ok(Message::Notification(rq @ N { method: "textDocument/publishDiagnostics", .. })) => { debug!("got diagnostics"); - match rq.load::<PublishDiagnostics>() { + match load_n::<lsp_notification!("textDocument/publishDiagnostics")>(rq) { Ok(x) => { d.insert(x.uri, x.diagnostics, &d.guard()); w.request_redraw(); @@ -109,7 +116,8 @@ pub fn handler( } }, Ok(Message::Notification(x @ N { method: "$/progress", .. })) => { - let ProgressParams {token,value:ProgressParamsValue::WorkDone(x) } = x.load::<Progress>().unwrap(); + let ProgressParams {token,value:x } = load_n::<lsp_notification!("$/progress")>(x).unwrap(); + let Ok(x) = serde_json::from_value::<WorkDoneProgress>(x.clone()) else { error!("{x:?}"); continue }; match x.clone() { WorkDoneProgress::Begin(y) => { progress.update(token, move |_| Some((x.clone(), y.clone())), &progress.guard()); @@ -131,6 +139,34 @@ pub fn handler( } } } +#[derive(Debug)] +#[allow(dead_code)] +pub enum ExtractErr<T> { + /// The extracted message was of a different method than expected. + MethodMismatch(T), + /// Failed to deserialize the message. + JsonError { method: String, error: serde_json::Error }, +} +fn load_n<T: lsp_types::Notification>( + me: lsp_server::Notification, +) -> Result<T::Params, ExtractErr<lsp_server::Notification>> { + (T::METHOD.as_str() == me.method) + .ok_or(ExtractErr::MethodMismatch(me.clone()))?; + serde_json::from_value(me.params) + .map_err(|e| ExtractErr::JsonError { method: me.method, error: e }) +} +pub fn load_rq<P: lsp_types::Request>( + me: LRq, +) -> Result<(RequestId, P::Params), ExtractError<LRq>> { + if me.method != P::METHOD.to_string() { + return Err(ExtractError::MethodMismatch(me)); + } + match serde_json::from_value(me.params) { + Ok(params) => Ok((me.id, params)), + Err(error) => + Err(ExtractError::JsonError { method: me.method, error }), + } +} impl super::Tx { pub fn notify<X: Notification>( &self, @@ -142,7 +178,9 @@ impl super::Tx { })) } pub fn cancel(&self, rid: i32) { - _ = self.notify::<Cancel>(&CancelParams { id: rid.into() }); + _ = self.notify::<CancelNotification>(&CancelParams { + id: rid.into(), + }); } } impl super::Client { @@ -266,3 +304,13 @@ impl super::Client { self.send_to.send((0, tx, BehaviourAfter::RedrawNow)) } } +macro_rules! send + { + ($c:ident.$call:ident$(::<$($arg:block),*>)?, $request:tt, _ { $($field:ident: $expr:expr),* $(,)? $(,..$base:expr)? } $(,)?) => {{ + type R = lsp_request!($request); + type P = <R as Request>::Params; + $c.$call::<R $($(,$arg)*)?>(&P { $($field: $expr),* }) + }}; +} + +pub(crate) use send; |