use crate::lsp::{
LspManager,
client::Status,
messages::{txt_doc_id, uri},
rpc::{Message, Notification},
};
use lsp_types::{
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
DidSaveTextDocumentParams, InitializedParams, TextDocumentContentChangeEvent, TextDocumentItem,
VersionedTextDocumentIdentifier,
notification::{
self as notif, DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument,
DidSaveTextDocument, Exit, Initialized,
},
};
use std::{borrow::Cow, fmt};
pub(crate) trait LspNotification:
notif::Notification + fmt::Debug + Send + Sync + Sized + 'static
{
type Data: Send + Sync + fmt::Debug + 'static;
fn data(lsp_id: usize, data: Self::Data) -> NotificationData<Self>
where
Self: Sized,
{
NotificationData {
lsp_id,
data: Some(data),
}
}
fn build_params(data: Self::Data) -> Self::Params;
fn send(lsp_id: usize, data: Self::Data, man: &mut LspManager) {
let client = match man.clients.get_mut(&lsp_id) {
Some(client) => match client.status {
Status::Running => client,
Status::Initializing => {
man.send_status("LSP server still initializing");
return;
}
},
None => {
man.send_status("no attached LSP client for buffer");
return;
}
};
let params = Self::build_params(data);
let res = client.write(Message::Notification(Notification {
method: Cow::Borrowed(Self::METHOD),
params: serde_json::to_value(params).unwrap(),
}));
if let Err(e) = res {
man.report_error(format!(
"unable to send {} LSP notification: {e}",
Self::METHOD
));
}
}
}
#[derive(Debug)]
pub(crate) struct NotificationData<T: LspNotification> {
lsp_id: usize,
data: Option<T::Data>,
}
pub(crate) trait PreparedLspNotification: Send + Sync + fmt::Debug + 'static {
fn send(&mut self, man: &mut LspManager);
}
impl<T> PreparedLspNotification for NotificationData<T>
where
T: LspNotification,
{
fn send(&mut self, man: &mut LspManager) {
T::send(self.lsp_id, self.data.take().unwrap(), man)
}
}
impl LspNotification for DidOpenTextDocument {
type Data = (String, String, String);
fn build_params((language_id, path, text): Self::Data) -> Self::Params {
DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri(&path),
language_id,
version: 1,
text,
},
}
}
}
impl LspNotification for DidChangeTextDocument {
type Data = (String, String, i32);
fn build_params((path, text, version): Self::Data) -> Self::Params {
DidChangeTextDocumentParams {
text_document: VersionedTextDocumentIdentifier {
uri: uri(&path),
version,
},
content_changes: vec![TextDocumentContentChangeEvent {
range: None,
range_length: None,
text,
}],
}
}
}
impl LspNotification for DidSaveTextDocument {
type Data = String;
fn build_params(path: Self::Data) -> Self::Params {
DidSaveTextDocumentParams {
text_document: txt_doc_id(&path),
text: None,
}
}
}
impl LspNotification for DidCloseTextDocument {
type Data = String;
fn build_params(path: Self::Data) -> Self::Params {
DidCloseTextDocumentParams {
text_document: txt_doc_id(&path),
}
}
}
impl LspNotification for Exit {
type Data = ();
fn build_params(_: Self::Data) -> Self::Params {}
}
impl LspNotification for Initialized {
type Data = ();
fn build_params(_: Self::Data) -> Self::Params {
InitializedParams {}
}
}