Skip to main content

matrixcode_core/lsp/
registry.rs

1//! LSP Client Registry
2//!
3//! 管理多个语言的 LSP 客户端。
4
5use anyhow::Result;
6use std::collections::HashMap;
7use std::path::Path;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use tokio::time::{Duration, timeout};
11
12use super::client::LspClient;
13use super::constants::LSP_WAIT_TIMEOUT_SECS;
14use super::progress::LspProgressCallback;
15use super::types::LspServerConfig;
16
17/// LSP 客户端注册表
18pub struct LspClientRegistry {
19    /// 语言 -> 客户端映射
20    clients: Arc<RwLock<HashMap<String, Arc<LspClient>>>>,
21}
22
23impl LspClientRegistry {
24    /// 创建空注册表
25    pub fn new() -> Self {
26        Self {
27            clients: Arc::new(RwLock::new(HashMap::new())),
28        }
29    }
30
31    /// 启动并注册 LSP 客户端
32    pub async fn register(&self, config: &LspServerConfig, project_root: &Path) -> Result<()> {
33        let client = LspClient::from_config(config, project_root.to_path_buf());
34        client.spawn(config).await?;
35        let mut clients = self.clients.write().await;
36        clients.insert(config.language.clone(), Arc::new(client));
37        log::info!("LSP client registered for language: {}", config.language);
38        Ok(())
39    }
40
41    /// 启动并注册 LSP 客户端(带进度回调)
42    ///
43    /// 使用异步初始化 + 进度回调,适合 TUI 环境显示进度条。
44    ///
45    /// # Arguments
46    /// - `config`: LSP 服务器配置
47    /// - `project_root`: 项目根目录
48    /// - `progress_callback`: 进度回调(可以使用 `NoOpProgressCallback` 如果不需要进度显示)
49    ///
50    /// # Progress Flow
51    /// - 0.1: Starting process...
52    /// - 0.3: Process started, initializing server...
53    /// - 0.5-0.9: Loading workspace...
54    /// - 1.0: Ready
55    ///
56    /// # Timeouts
57    /// - 进程启动:5 秒(快速失败)
58    /// - 服务器初始化:60 秒(大型项目可能需要)
59    pub async fn register_with_progress(
60        &self,
61        config: &LspServerConfig,
62        project_root: &Path,
63        progress_callback: Arc<dyn LspProgressCallback>,
64    ) -> Result<()> {
65        let client = LspClient::from_config(config, project_root.to_path_buf());
66        
67        // 使用异步初始化 + 进度回调
68        client.spawn_async(config, progress_callback.clone()).await?;
69        
70        let mut clients = self.clients.write().await;
71        clients.insert(config.language.clone(), Arc::new(client));
72        log::info!("LSP client registered for language: {}", config.language);
73        Ok(())
74    }
75
76    /// 获取指定语言的客户端(立即返回,不等待)
77    pub async fn get_client(&self, language: &str) -> Option<Arc<LspClient>> {
78        let clients = self.clients.read().await;
79        clients.get(language).cloned()
80    }
81
82    /// 获取指定语言的客户端,等待启动完成(最多 30 秒)
83    pub async fn get_client_or_wait(&self, language: &str) -> Result<Arc<LspClient>> {
84        // 先尝试立即获取
85        if let Some(client) = self.get_client(language).await {
86            return Ok(client);
87        }
88
89        // 等待客户端启动
90        log::info!("Waiting for LSP client '{}' to start...", language);
91        let wait_duration = Duration::from_secs(LSP_WAIT_TIMEOUT_SECS);
92
93        timeout(wait_duration, async {
94            loop {
95                if let Some(client) = self.get_client(language).await {
96                    log::info!("LSP client '{}' is now available", language);
97                    return Ok(client);
98                }
99                tokio::time::sleep(Duration::from_millis(500)).await;
100            }
101        })
102        .await
103        .map_err(|_| anyhow::anyhow!(
104            "LSP 客户端 '{}' 启动超时({}秒)。\n\
105            提示:LSP 服务器可能正在后台启动,请稍后再试。\n\
106            状态:检查 TUI 状态栏 LSP 是否显示 'starting...'",
107            language, LSP_WAIT_TIMEOUT_SECS
108        ))?
109    }
110
111    /// 是否有活跃客户端
112    pub async fn has_active_clients(&self) -> bool {
113        let clients = self.clients.read().await;
114        !clients.is_empty()
115    }
116
117    /// 获取所有活跃语言
118    pub async fn active_languages(&self) -> Vec<String> {
119        let clients = self.clients.read().await;
120        clients.keys().cloned().collect()
121    }
122
123    /// 关闭所有客户端
124    pub async fn shutdown_all(&self) -> Result<()> {
125        let mut clients = self.clients.write().await;
126        for (language, client) in clients.iter() {
127            if let Err(e) = client.shutdown().await {
128                log::warn!("Failed to shutdown LSP client '{}': {}", language, e);
129            }
130        }
131        clients.clear();
132        Ok(())
133    }
134}
135
136impl Default for LspClientRegistry {
137    fn default() -> Self {
138        Self::new()
139    }
140}