use anyhow::Result;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use tokio::sync::RwLock;
use tokio::time::{Duration, timeout};
use super::client::LspClient;
use super::constants::LSP_WAIT_TIMEOUT_SECS;
use super::progress::LspProgressCallback;
use super::types::LspServerConfig;
pub struct LspClientRegistry {
clients: Arc<RwLock<HashMap<String, Arc<LspClient>>>>,
}
impl LspClientRegistry {
pub fn new() -> Self {
Self {
clients: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn register(&self, config: &LspServerConfig, project_root: &Path) -> Result<()> {
let client = LspClient::from_config(config, project_root.to_path_buf());
client.spawn(config).await?;
let mut clients = self.clients.write().await;
clients.insert(config.language.clone(), Arc::new(client));
log::info!("LSP client registered for language: {}", config.language);
Ok(())
}
pub async fn register_with_progress(
&self,
config: &LspServerConfig,
project_root: &Path,
progress_callback: Arc<dyn LspProgressCallback>,
) -> Result<()> {
let client = LspClient::from_config(config, project_root.to_path_buf());
client.spawn_async(config, progress_callback.clone()).await?;
let mut clients = self.clients.write().await;
clients.insert(config.language.clone(), Arc::new(client));
log::info!("LSP client registered for language: {}", config.language);
Ok(())
}
pub async fn get_client(&self, language: &str) -> Option<Arc<LspClient>> {
let clients = self.clients.read().await;
clients.get(language).cloned()
}
pub async fn get_client_or_wait(&self, language: &str) -> Result<Arc<LspClient>> {
if let Some(client) = self.get_client(language).await {
return Ok(client);
}
log::info!("Waiting for LSP client '{}' to start...", language);
let wait_duration = Duration::from_secs(LSP_WAIT_TIMEOUT_SECS);
timeout(wait_duration, async {
loop {
if let Some(client) = self.get_client(language).await {
log::info!("LSP client '{}' is now available", language);
return Ok(client);
}
tokio::time::sleep(Duration::from_millis(500)).await;
}
})
.await
.map_err(|_| anyhow::anyhow!(
"LSP 客户端 '{}' 启动超时({}秒)。\n\
提示:LSP 服务器可能正在后台启动,请稍后再试。\n\
状态:检查 TUI 状态栏 LSP 是否显示 'starting...'",
language, LSP_WAIT_TIMEOUT_SECS
))?
}
pub async fn has_active_clients(&self) -> bool {
let clients = self.clients.read().await;
!clients.is_empty()
}
pub async fn active_languages(&self) -> Vec<String> {
let clients = self.clients.read().await;
clients.keys().cloned().collect()
}
pub async fn shutdown_all(&self) -> Result<()> {
let mut clients = self.clients.write().await;
for (language, client) in clients.iter() {
if let Err(e) = client.shutdown().await {
log::warn!("Failed to shutdown LSP client '{}': {}", language, e);
}
}
clients.clear();
Ok(())
}
}
impl Default for LspClientRegistry {
fn default() -> Self {
Self::new()
}
}