use anyhow::{Result, anyhow};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{info, warn};
use crate::config::Config;
use crate::lsp::LspClient;
use crate::lsp::state::ServerStatus;
use crate::session::EventBroadcaster;
pub struct ClientManager {
config: Config,
root: PathBuf,
active_clients: Mutex<HashMap<String, Arc<Mutex<LspClient>>>>,
broadcaster: EventBroadcaster,
}
impl ClientManager {
#[must_use]
pub fn new(config: Config, root: PathBuf, broadcaster: EventBroadcaster) -> Self {
Self {
config,
root,
active_clients: Mutex::new(HashMap::new()),
broadcaster,
}
}
pub async fn get_client(&self, lang: &str) -> Result<Arc<Mutex<LspClient>>> {
if let Some(client) = self.active_clients.lock().await.get(lang) {
let is_alive = client.lock().await.is_alive();
if is_alive {
return Ok(client.clone());
}
warn!("LSP server for {} died, restarting...", lang);
self.active_clients.lock().await.remove(lang);
}
let mut clients = self.active_clients.lock().await;
let server_config = self
.config
.server
.get(lang)
.ok_or_else(|| anyhow!("No LSP server configured for language '{lang}'"))?;
info!(
"Spawning LSP server for {}: {} {}",
lang,
server_config.command,
server_config.args.join(" ")
);
let args: Vec<&str> = server_config
.args
.iter()
.map(|s: &String| s.as_str())
.collect();
let mut client = LspClient::spawn(
&server_config.command,
&args,
lang,
self.broadcaster.clone(),
)?;
client.initialize(&self.root).await?;
let client_mutex = Arc::new(Mutex::new(client));
clients.insert(lang.to_string(), client_mutex.clone());
drop(clients);
Ok(client_mutex)
}
pub async fn active_clients(&self) -> HashMap<String, Arc<Mutex<LspClient>>> {
self.active_clients.lock().await.clone()
}
pub async fn all_server_status(&self) -> Vec<ServerStatus> {
let clients = self.active_clients.lock().await.clone();
let mut statuses = Vec::new();
for (lang, client_mutex) in clients {
let status = client_mutex.lock().await.status(lang).await;
statuses.push(status);
}
statuses
}
pub async fn shutdown_client(&self, lang: &str) {
let mut clients = self.active_clients.lock().await;
if let Some(client_mutex) = clients.remove(lang) {
info!("Shutting down idle LSP server for {}", lang);
let mut client = client_mutex.lock().await;
if client.is_alive()
&& let Err(e) = client.shutdown().await
{
warn!("Failed to shutdown LSP server for {}: {}", lang, e);
}
}
}
pub async fn shutdown_all(&self) {
let mut clients = self.active_clients.lock().await;
for (lang, client_mutex) in clients.drain() {
{
let mut client = client_mutex.lock().await;
if client.is_alive()
&& let Err(e) = client.shutdown().await
{
warn!("Failed to shutdown LSP server for {}: {}", lang, e);
}
}
}
}
}