use lsp_server::{Connection, Message, Response};
use std::error::Error;
use std::sync::Arc;
use tokio::sync::mpsc;
const EXIT_WAIT: std::time::Duration = std::time::Duration::from_millis(500);
pub struct AsyncConnection {
pub(crate) connection: Arc<Connection>,
receiver: mpsc::UnboundedReceiver<Message>,
_receiver_task: std::thread::JoinHandle<()>,
}
impl AsyncConnection {
pub fn from_sync(connection: Connection) -> Self {
let (tx, rx) = mpsc::unbounded_channel();
let connection = Arc::new(connection);
let connection_clone = connection.clone();
let receiver_task = std::thread::Builder::new()
.name("cpp_ls-reader".to_string())
.spawn(move || {
for msg in &connection_clone.receiver {
if tx.send(msg).is_err() {
break; }
}
})
.expect("the reader thread starts");
Self {
connection,
receiver: rx,
_receiver_task: receiver_task,
}
}
pub async fn recv(&mut self) -> Option<Message> {
self.receiver.recv().await
}
pub fn send(&self, msg: Message) -> Result<(), Box<dyn Error + Send + Sync>> {
self.connection
.sender
.send(msg)
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
}
pub async fn handle_shutdown(
&mut self,
req: &lsp_server::Request,
) -> Result<bool, Box<dyn Error + Send + Sync>> {
if req.method != "shutdown" {
return Ok(false);
}
let resp = Response::new_ok(req.id.clone(), ());
let _ = self.connection.sender.send(resp.into());
match tokio::time::timeout(EXIT_WAIT, self.receiver.recv()).await {
Ok(Some(Message::Notification(n))) if n.method == "exit" => {}
Ok(Some(msg)) => {
log::debug!("after shutdown, before exit: {msg:?}");
}
Ok(None) => {
log::debug!("the client closed the connection after shutdown");
}
Err(_) => {
log::debug!("no exit notification within {EXIT_WAIT:?}; ending anyway");
}
}
Ok(true)
}
}