use std::path::Path;
use crossbeam_channel::Receiver;
use tokio::sync::mpsc::UnboundedSender;
use crate::BufferId;
use crate::config::LspConfig;
use crate::event::{LspCommand, LspEvent};
use crate::runtime;
pub struct LspManager {
cmd_tx: UnboundedSender<LspCommand>,
evt_rx: Receiver<LspEvent>,
thread: Option<std::thread::JoinHandle<()>>,
}
impl LspManager {
pub fn spawn(config: LspConfig) -> Self {
let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<LspCommand>();
let (evt_tx, evt_rx) = crossbeam_channel::unbounded::<LspEvent>();
let thread = std::thread::Builder::new()
.name("hjkl-lsp".to_string())
.spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("failed to build LSP tokio runtime");
rt.block_on(runtime::dispatch(cmd_rx, evt_tx, config));
})
.expect("failed to spawn hjkl-lsp thread");
Self {
cmd_tx,
evt_rx,
thread: Some(thread),
}
}
pub fn shutdown(mut self) {
let _ = self.cmd_tx.send(LspCommand::ShutdownAll);
if let Some(handle) = self.thread.take() {
let (done_tx, done_rx) = std::sync::mpsc::channel::<()>();
std::thread::spawn(move || {
let _ = handle.join();
let _ = done_tx.send(());
});
let _ = done_rx.recv_timeout(std::time::Duration::from_secs(2));
}
}
pub fn attach_buffer(&self, id: BufferId, path: &Path, language_id: &str, text: &str) {
let _ = self.cmd_tx.send(LspCommand::AttachBuffer {
id,
path: path.to_path_buf(),
language_id: language_id.to_string(),
text: text.to_string(),
});
}
pub fn detach_buffer(&self, id: BufferId) {
let _ = self.cmd_tx.send(LspCommand::DetachBuffer { id });
}
pub fn notify_change(&self, id: BufferId, full_text: &str) {
let _ = self.cmd_tx.send(LspCommand::NotifyChange {
id,
full_text: full_text.to_string(),
});
}
pub fn send_request(
&self,
request_id: i64,
buffer_id: BufferId,
method: &str,
params: serde_json::Value,
) {
let _ = self.cmd_tx.send(LspCommand::Request {
request_id,
buffer_id,
method: method.to_string(),
params,
});
}
pub fn try_recv_event(&self) -> Option<LspEvent> {
self.evt_rx.try_recv().ok()
}
}
impl Drop for LspManager {
fn drop(&mut self) {
let _ = self.cmd_tx.send(LspCommand::ShutdownAll);
}
}