Skip to main content

sh_layer3/lsp/
server.rs

1//! LSP 服务器管理
2//!
3//! 管理 LSP 服务器的启动、停止和生命周期。
4
5use super::LspError;
6use super::LspResult;
7use std::collections::HashMap;
8use std::path::PathBuf;
9use std::process::{Child, Command, Stdio};
10use std::sync::Arc;
11use tokio::sync::Mutex;
12
13/// 语言服务器配置
14#[derive(Debug, Clone)]
15pub struct LanguageServerConfig {
16    /// 服务器名称
17    pub name: String,
18    /// 命令路径
19    pub command: String,
20    /// 命令参数
21    pub args: Vec<String>,
22    /// 支持的文件扩展名
23    pub extensions: Vec<String>,
24    /// 初始化超时(秒)
25    pub init_timeout_secs: u64,
26}
27
28impl LanguageServerConfig {
29    pub fn new(
30        name: impl Into<String>,
31        command: impl Into<String>,
32        args: Vec<String>,
33        extensions: Vec<String>,
34    ) -> Self {
35        Self {
36            name: name.into(),
37            command: command.into(),
38            args,
39            extensions,
40            init_timeout_secs: 30,
41        }
42    }
43}
44
45/// 语言服务器实例
46#[derive(Debug)]
47pub struct LanguageServer {
48    /// 配置
49    pub config: LanguageServerConfig,
50    /// 子进程
51    pub process: Option<Child>,
52    /// 根目录
53    pub root_path: PathBuf,
54}
55
56impl LanguageServer {
57    /// 启动服务器
58    pub fn new(config: LanguageServerConfig, root_path: PathBuf) -> LspResult<Self> {
59        Ok(Self {
60            config,
61            process: None,
62            root_path,
63        })
64    }
65
66    /// 启动服务器进程
67    pub fn start(&mut self) -> LspResult<()> {
68        let mut command = Command::new(&self.config.command);
69
70        // 设置工作目录
71        command.current_dir(&self.root_path);
72
73        // 设置参数
74        for arg in &self.config.args {
75            command.arg(arg);
76        }
77
78        // 设置标准输入/输出
79        command
80            .stdin(Stdio::piped())
81            .stdout(Stdio::piped())
82            .stderr(Stdio::piped());
83
84        let child = command.spawn().map_err(|e| {
85            LspError::ServerCrashed(format!("Failed to start {}: {}", self.config.name, e))
86        })?;
87
88        self.process = Some(child);
89        tracing::info!("Started LSP server: {}", self.config.name);
90
91        Ok(())
92    }
93
94    /// 停止服务器
95    pub fn stop(&mut self) -> LspResult<()> {
96        if let Some(mut process) = self.process.take() {
97            let _ = process.kill();
98            tracing::info!("Stopped LSP server: {}", self.config.name);
99        }
100        Ok(())
101    }
102
103    /// 获取配置
104    pub fn config(&self) -> &LanguageServerConfig {
105        &self.config
106    }
107
108    /// 获取进程 stdin
109    pub fn stdin(&self) -> Option<&std::process::ChildStdin> {
110        self.process.as_ref().and_then(|p| p.stdin.as_ref())
111    }
112
113    /// 获取进程 stdout
114    pub fn stdout(&self) -> Option<&std::process::ChildStdout> {
115        self.process.as_ref().and_then(|p| p.stdout.as_ref())
116    }
117
118    /// 检查服务器是否运行
119    pub fn is_running(&self) -> bool {
120        self.process.is_some()
121    }
122
123    /// 获取根路径
124    pub fn root_path(&self) -> &PathBuf {
125        &self.root_path
126    }
127}
128
129impl Drop for LanguageServer {
130    fn drop(&mut self) {
131        let _ = self.stop();
132    }
133}
134
135/// 语言服务器管理器
136pub struct LanguageServerManager {
137    /// 已启动的服务器
138    pub servers: Arc<Mutex<HashMap<String, LanguageServer>>>,
139    /// 服务器配置
140    configs: HashMap<String, LanguageServerConfig>,
141}
142
143impl LanguageServerManager {
144    /// 创建新的管理器,使用默认配置
145    pub fn new() -> Self {
146        let mut configs = HashMap::new();
147
148        // Rust - rust-analyzer
149        configs.insert(
150            "rust".to_string(),
151            LanguageServerConfig::new(
152                "rust-analyzer",
153                "rust-analyzer",
154                vec![],
155                vec!["rs".to_string()],
156            ),
157        );
158
159        // Python - pyright
160        configs.insert(
161            "python".to_string(),
162            LanguageServerConfig::new(
163                "pyright",
164                "pyright-langserver",
165                vec!["--stdio".to_string()],
166                vec!["py".to_string()],
167            ),
168        );
169
170        // TypeScript/JavaScript
171        configs.insert(
172            "typescript".to_string(),
173            LanguageServerConfig::new(
174                "typescript-language-server",
175                "typescript-language-server",
176                vec!["--stdio".to_string()],
177                vec![
178                    "ts".to_string(),
179                    "tsx".to_string(),
180                    "js".to_string(),
181                    "jsx".to_string(),
182                ],
183            ),
184        );
185
186        // Go
187        configs.insert(
188            "go".to_string(),
189            LanguageServerConfig::new(
190                "gopls",
191                "gopls",
192                vec!["serve".to_string()],
193                vec!["go".to_string()],
194            ),
195        );
196
197        // C/C++
198        configs.insert(
199            "cpp".to_string(),
200            LanguageServerConfig::new(
201                "clangd",
202                "clangd",
203                vec!["--background-index".to_string()],
204                vec![
205                    "c".to_string(),
206                    "cpp".to_string(),
207                    "h".to_string(),
208                    "hpp".to_string(),
209                ],
210            ),
211        );
212
213        Self {
214            servers: Arc::new(Mutex::new(HashMap::new())),
215            configs,
216        }
217    }
218
219    /// 根据文件扩展名获取语言名称
220    pub fn get_language_from_extension(ext: &str) -> Option<&'static str> {
221        match ext {
222            "rs" => Some("rust"),
223            "py" => Some("python"),
224            "ts" | "tsx" => Some("typescript"),
225            "js" | "jsx" => Some("typescript"), // JS 也用 typescript-language-server
226            "go" => Some("go"),
227            "c" | "cpp" | "h" | "hpp" => Some("cpp"),
228            _ => None,
229        }
230    }
231
232    /// 获取服务器配置
233    pub fn get_config(&self, language: &str) -> Option<&LanguageServerConfig> {
234        self.configs.get(language)
235    }
236
237    /// 启动服务器
238    pub async fn start_server(&self, language: &str, root_path: PathBuf) -> LspResult<()> {
239        let config = self
240            .configs
241            .get(language)
242            .ok_or_else(|| LspError::ServerNotFound(language.to_string()))?
243            .clone();
244
245        let mut server = LanguageServer::new(config, root_path)?;
246        server.start()?;
247
248        let mut servers = self.servers.lock().await;
249        servers.insert(language.to_string(), server);
250
251        Ok(())
252    }
253
254    /// 获取服务器
255    pub async fn get_server(&self, language: &str) -> Option<LanguageServerHandle> {
256        let servers = self.servers.lock().await;
257        servers.get(language).map(|_s| LanguageServerHandle {
258            language: language.to_string(),
259            servers: self.servers.clone(),
260        })
261    }
262
263    /// 停止服务器
264    pub async fn stop_server(&self, language: &str) -> LspResult<()> {
265        let mut servers = self.servers.lock().await;
266        if let Some(mut server) = servers.remove(language) {
267            server.stop()?;
268        }
269        Ok(())
270    }
271
272    /// 停止所有服务器
273    pub async fn stop_all(&self) -> LspResult<()> {
274        let mut servers = self.servers.lock().await;
275        for (_, mut server) in servers.drain() {
276            let _ = server.stop();
277        }
278        Ok(())
279    }
280
281    /// 检查服务器是否运行
282    pub async fn is_running(&self, language: &str) -> bool {
283        let servers = self.servers.lock().await;
284        servers
285            .get(language)
286            .map(|s| s.is_running())
287            .unwrap_or(false)
288    }
289
290    /// 添加自定义服务器配置
291    pub fn add_custom_config(&mut self, language: String, config: LanguageServerConfig) {
292        self.configs.insert(language, config);
293    }
294}
295
296impl Default for LanguageServerManager {
297    fn default() -> Self {
298        Self::new()
299    }
300}
301
302/// 语言服务器句柄(用于安全访问)
303pub struct LanguageServerHandle {
304    language: String,
305    servers: Arc<Mutex<HashMap<String, LanguageServer>>>,
306}
307
308impl LanguageServerHandle {
309    /// 获取服务器名称
310    pub fn name(&self) -> String {
311        self.language.clone()
312    }
313
314    /// 获取服务器配置
315    pub async fn config(&self) -> Option<LanguageServerConfig> {
316        let servers = self.servers.lock().await;
317        servers.get(&self.language).map(|s| s.config().clone())
318    }
319
320    /// 获取根路径
321    pub async fn root_path(&self) -> Option<PathBuf> {
322        let servers = self.servers.lock().await;
323        servers.get(&self.language).map(|s| s.root_path().clone())
324    }
325}
326
327// ============================================================================
328// 预定义的 LSP 服务器配置
329// ============================================================================
330
331/// 默认的 rust-analyzer 配置
332pub fn rust_analyzer_config() -> LanguageServerConfig {
333    LanguageServerConfig::new(
334        "rust-analyzer",
335        "rust-analyzer",
336        vec!["--stdio".to_string()],
337        vec!["rs".to_string()],
338    )
339}
340
341/// 默认的 pyright 配置
342pub fn pyright_config() -> LanguageServerConfig {
343    LanguageServerConfig::new(
344        "pyright",
345        "pyright-langserver",
346        vec!["--stdio".to_string()],
347        vec!["py".to_string()],
348    )
349}
350
351/// 默认的 TypeScript 语言服务器配置
352pub fn typescript_config() -> LanguageServerConfig {
353    LanguageServerConfig::new(
354        "typescript-language-server",
355        "typescript-language-server",
356        vec!["--stdio".to_string()],
357        vec![
358            "ts".to_string(),
359            "tsx".to_string(),
360            "js".to_string(),
361            "jsx".to_string(),
362        ],
363    )
364}
365
366/// 默认的 gopls 配置
367pub fn gopls_config() -> LanguageServerConfig {
368    LanguageServerConfig::new(
369        "gopls",
370        "gopls",
371        vec!["serve".to_string(), "--stdio".to_string()],
372        vec!["go".to_string()],
373    )
374}
375
376/// 默认的 clangd 配置
377pub fn clangd_config() -> LanguageServerConfig {
378    LanguageServerConfig::new(
379        "clangd",
380        "clangd",
381        vec![
382            "--background-index".to_string(),
383            "--header-insertion=iwyu".to_string(),
384        ],
385        vec![
386            "c".to_string(),
387            "cpp".to_string(),
388            "h".to_string(),
389            "hpp".to_string(),
390        ],
391    )
392}
393
394/// pylance 配置(VS Code Python 插件)
395pub fn pylance_config() -> LanguageServerConfig {
396    LanguageServerConfig::new(
397        "pylance",
398        "pylance",
399        vec!["--stdio".to_string()],
400        vec!["py".to_string()],
401    )
402}
403
404#[cfg(test)]
405mod tests {
406    use super::*;
407
408    #[test]
409    fn test_manager_creation() {
410        let manager = LanguageServerManager::new();
411        assert!(manager.get_config("rust").is_some());
412        assert!(manager.get_config("python").is_some());
413        assert!(manager.get_config("typescript").is_some());
414        assert!(manager.get_config("go").is_some());
415        assert!(manager.get_config("cpp").is_some());
416    }
417
418    #[test]
419    fn test_get_language_from_extension() {
420        assert_eq!(
421            LanguageServerManager::get_language_from_extension("rs"),
422            Some("rust")
423        );
424        assert_eq!(
425            LanguageServerManager::get_language_from_extension("py"),
426            Some("python")
427        );
428        assert_eq!(
429            LanguageServerManager::get_language_from_extension("ts"),
430            Some("typescript")
431        );
432        assert_eq!(
433            LanguageServerManager::get_language_from_extension("go"),
434            Some("go")
435        );
436        assert_eq!(
437            LanguageServerManager::get_language_from_extension("cpp"),
438            Some("cpp")
439        );
440        assert_eq!(
441            LanguageServerManager::get_language_from_extension("xyz"),
442            None
443        );
444    }
445
446    #[test]
447    fn test_config_creation() {
448        let config = rust_analyzer_config();
449        assert_eq!(config.name, "rust-analyzer");
450        assert_eq!(config.command, "rust-analyzer");
451        assert_eq!(config.extensions, vec!["rs".to_string()]);
452    }
453}