use std::{error::Error, future::Future};
use log::error;
use lsp_server::Notification;
use lsp_types::{
notification::{
Cancel, DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles,
DidCloseTextDocument, DidOpenTextDocument, DidRenameFiles, DidSaveTextDocument,
Notification as lsp_notification, SetTrace,
},
CancelParams, NumberOrString,
};
use serde::de::DeserializeOwned;
use crate::{
context::{ServerContext, ServerContextSnapshot},
handlers::workspace::on_did_rename_files_handler,
};
use super::{
configuration::on_did_change_configuration,
text_document::{
on_did_change_text_document, on_did_change_watched_files, on_did_close_document,
on_did_open_text_document, on_did_save_text_document, on_set_trace,
},
};
pub async fn on_notification_handler(
notification: Notification,
server_context: &mut ServerContext,
) -> Result<(), Box<dyn Error + Sync + Send>> {
NotificationDispatcher::new(notification, server_context)
.on_cancel()
.await
.on_sync::<DidChangeTextDocument, _, _>(on_did_change_text_document)
.await
.on_parallel::<DidOpenTextDocument, _, _>(on_did_open_text_document)
.on_parallel::<DidSaveTextDocument, _, _>(on_did_save_text_document)
.on_parallel::<DidCloseTextDocument, _, _>(on_did_close_document)
.on_parallel::<DidChangeWatchedFiles, _, _>(on_did_change_watched_files)
.on_parallel::<SetTrace, _, _>(on_set_trace)
.on_parallel::<DidChangeConfiguration, _, _>(on_did_change_configuration)
.on_parallel::<DidRenameFiles, _, _>(on_did_rename_files_handler)
.finish();
Ok(())
}
pub struct NotificationDispatcher<'a> {
notification: Option<Notification>,
context: &'a mut ServerContext,
}
impl<'a> NotificationDispatcher<'a> {
pub fn new(notification: Notification, context: &'a mut ServerContext) -> Self {
NotificationDispatcher {
notification: Some(notification),
context,
}
}
pub fn on_parallel<R, F, Fut>(&mut self, handler: F) -> &mut Self
where
R: lsp_types::notification::Notification + 'static,
R::Params: DeserializeOwned + Send + std::fmt::Debug + 'static,
F: Fn(ServerContextSnapshot, R::Params) -> Fut + Send + 'static,
Fut: Future<Output = Option<()>> + Send + 'static,
{
let notification = match &self.notification {
Some(req) if req.method == R::METHOD => self.notification.take().unwrap(),
_ => return self,
};
if R::METHOD == notification.method {
let snapshot = self.context.snapshot();
let m = notification.extract(R::METHOD);
tokio::spawn(async move {
handler(snapshot, m.unwrap()).await;
});
}
self
}
pub async fn on_sync<R, F, Fut>(&mut self, handler: F) -> &mut Self
where
R: lsp_types::notification::Notification + 'static,
R::Params: DeserializeOwned + Send + std::fmt::Debug + 'static,
F: Fn(ServerContextSnapshot, R::Params) -> Fut + Send + 'static,
Fut: Future<Output = Option<()>> + Send + 'static,
{
let notification = match &self.notification {
Some(req) if req.method == R::METHOD => self.notification.take().unwrap(),
_ => return self,
};
if R::METHOD == notification.method {
let snapshot = self.context.snapshot();
let m = notification.extract(R::METHOD);
handler(snapshot, m.unwrap()).await;
}
self
}
pub async fn on_cancel(&mut self) -> &mut Self {
let notification = match &self.notification {
Some(req) if req.method == Cancel::METHOD => self.notification.take().unwrap(),
_ => return self,
};
if Cancel::METHOD == notification.method {
let m: Result<CancelParams, _> = notification.extract(Cancel::METHOD);
let req_id = match m.unwrap().id {
NumberOrString::Number(i) => i.into(),
NumberOrString::String(s) => s.into(),
};
self.context.cancel(req_id).await;
}
self
}
pub fn finish(&mut self) {
if let Some(notification) = &self.notification {
error!(
"handler not found for notification. [{}]",
notification.method
)
}
}
}