Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'src/lsp/communication.rs')
-rw-r--r--src/lsp/communication.rs126
1 files changed, 103 insertions, 23 deletions
diff --git a/src/lsp/communication.rs b/src/lsp/communication.rs
index 7bc6b7c..efc44ee 100644
--- a/src/lsp/communication.rs
+++ b/src/lsp/communication.rs
@@ -7,21 +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;
+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>,
@@ -32,19 +40,20 @@ pub fn handler(
) {
let mut map = HashMap::new();
let w = window_rx.blocking_recv().unwrap();
+ println!("got w");
loop {
crossbeam::select! {
recv(req_rx) -> x => match x {
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);
@@ -65,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:?}");
@@ -80,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(()) => {}
@@ -98,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();
@@ -107,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());
@@ -129,36 +139,95 @@ pub fn handler(
}
}
}
-impl super::Client {
+#[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,
y: &X::Params,
) -> Result<(), SendError<Message>> {
- self.tx.send(Message::Notification(N {
+ self.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() });
+ _ = self.notify::<CancelNotification>(&CancelParams {
+ id: rid.into(),
+ });
}
+}
+impl super::Client {
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)
+ let _guard = self.runtime.enter();
+ self.runtime
+ .block_on(tokio::time::timeout(
+ tokio::time::Duration::from_secs(20),
+ self.request_::<X, { Nil }>(y)?.0,
+ ))
+ .unwrap()
+ }
+ pub fn request_by<'me, X: Request>(
+ &'me self,
+ y: &X::Params,
+ d: tokio::time::Duration,
+ ) -> Result<Result<X::Result, RequestError<X>>, Elapsed> {
+ let _guard = self.runtime.enter();
+ self.runtime.block_on(tokio::time::timeout(
+ d,
+ match self.request_::<X, { Nil }>(y) {
+ Err(e) => return Ok(Err(e.into())),
+ Ok((x, _)) => x,
+ },
+ ))
}
+ pub fn by<T>(
+ &self,
+ x: impl Future<Output = T>,
+ d: tokio::time::Duration,
+ ) -> Result<T, Elapsed> {
+ let _guard = self.runtime.enter();
+ self.runtime.block_on(tokio::time::timeout(d, x))
+ }
pub fn request<'me, X: Request>(
&'me self,
y: &X::Params,
) -> Result<
(
- impl Future<Output = Result<X::Result, RequestError<X>>>
- + use<'me, X>,
+ impl Future<Output = Result<X::Result, RequestError<X>>> + use<X>,
i32,
),
- SendError<Message>,
+ RqSendError<X>,
> {
self.request_::<X, { Redraw }>(y)
}
@@ -169,10 +238,10 @@ impl super::Client {
) -> Result<
(
impl Future<Output = Result<X::Result, RequestError<X>>>
- + use<'me, X, THEN>,
+ + use<X, THEN>,
i32,
),
- SendError<Message>,
+ RqSendError<X>,
> {
let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
self.tx.send(Message::Request(LRq {
@@ -187,10 +256,11 @@ impl super::Client {
.send((id, tx, THEN))
.expect("oughtnt really fail");
}
+ let tx = self.tx.clone();
Ok((
async move {
let g = scopeguard::guard((), |()| {
- self.cancel(id);
+ tx.cancel(id);
});
let mut x = rx.await?;
@@ -234,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;