1use std::collections::HashMap;
6use std::path::{Path, PathBuf};
7use std::sync::Arc;
8use tokio::sync::RwLock;
9
10use super::config::{default_lsp_configs, load_lsp_config_file, LSPServerConfig};
11use super::server::{LSPDiagnostic, LSPServer, LSPServerState};
12
13#[derive(Debug, Clone, Default)]
15pub struct InitializeLSPOptions {
16 pub load_config_file: bool,
18 pub use_defaults: bool,
20 pub custom_configs: Vec<LSPServerConfig>,
22}
23
24pub struct LSPServerManager {
26 servers: Arc<RwLock<HashMap<String, LSPServer>>>,
27 server_configs: Arc<RwLock<Vec<LSPServerConfig>>>,
28 workspace_root: PathBuf,
29 extension_to_server: Arc<RwLock<HashMap<String, Vec<String>>>>,
30 diagnostics_cache: Arc<RwLock<HashMap<String, Vec<LSPDiagnostic>>>>,
31}
32
33impl LSPServerManager {
34 pub fn new(workspace_root: impl AsRef<Path>) -> Self {
36 Self {
37 servers: Arc::new(RwLock::new(HashMap::new())),
38 server_configs: Arc::new(RwLock::new(Vec::new())),
39 workspace_root: workspace_root.as_ref().to_path_buf(),
40 extension_to_server: Arc::new(RwLock::new(HashMap::new())),
41 diagnostics_cache: Arc::new(RwLock::new(HashMap::new())),
42 }
43 }
44
45 pub async fn register_server(&self, config: LSPServerConfig) {
47 let mut ext_map = self.extension_to_server.write().await;
49 for ext in &config.file_extensions {
50 let normalized = if ext.starts_with('.') {
51 ext.to_lowercase()
52 } else {
53 format!(".{}", ext.to_lowercase())
54 };
55 ext_map
56 .entry(normalized)
57 .or_default()
58 .push(config.name.clone());
59 }
60
61 self.server_configs.write().await.push(config);
62 }
63
64 pub async fn load_config_from_file(&self) -> Vec<LSPServerConfig> {
66 let configs = load_lsp_config_file(&self.workspace_root);
67 for config in &configs {
68 self.register_server(config.clone()).await;
69 }
70 configs
71 }
72
73 pub async fn initialize(&self, options: InitializeLSPOptions) -> Result<(), String> {
75 if options.load_config_file {
77 let file_configs = self.load_config_from_file().await;
78 if !file_configs.is_empty() {
79 tracing::info!("[LSP] 从配置文件加载了 {} 个服务器", file_configs.len());
80 }
81 }
82
83 for config in options.custom_configs {
85 self.register_server(config).await;
86 }
87
88 if options.use_defaults {
90 let existing: std::collections::HashSet<_> = self
91 .server_configs
92 .read()
93 .await
94 .iter()
95 .map(|c| c.name.clone())
96 .collect();
97 for config in default_lsp_configs() {
98 if !existing.contains(&config.name) {
99 self.register_server(config).await;
100 }
101 }
102 }
103
104 let configs = self.server_configs.read().await.clone();
106 for config in configs {
107 let mut server = LSPServer::new(config.clone());
108 if let Err(e) = server.start(&self.workspace_root).await {
109 tracing::warn!("[LSP] 启动 {} 失败: {}", config.name, e);
110 continue;
111 }
112 self.servers
113 .write()
114 .await
115 .insert(config.name.clone(), server);
116 }
117
118 let count = self.servers.read().await.len();
119 tracing::info!("[LSP] 初始化完成: {} 个服务器启动成功", count);
120 Ok(())
121 }
122
123 pub async fn shutdown(&self) {
125 let mut servers = self.servers.write().await;
126 for (name, server) in servers.iter_mut() {
127 if let Err(e) = server.stop().await {
128 tracing::warn!("[LSP] 停止 {} 失败: {}", name, e);
129 }
130 }
131 servers.clear();
132 }
133
134 pub async fn get_server_for_file(&self, file_path: &Path) -> Option<String> {
136 let ext = file_path.extension()?.to_str()?;
137 let normalized = format!(".{}", ext.to_lowercase());
138
139 let ext_map = self.extension_to_server.read().await;
140 let server_names = ext_map.get(&normalized)?;
141
142 let servers = self.servers.read().await;
143 for name in server_names {
144 if let Some(server) = servers.get(name) {
145 if server.is_healthy().await {
146 return Some(name.clone());
147 }
148 }
149 }
150 None
151 }
152
153 pub async fn get_all_server_status(&self) -> HashMap<String, LSPServerState> {
155 let servers = self.servers.read().await;
156 let mut status = HashMap::new();
157 for (name, server) in servers.iter() {
158 status.insert(name.clone(), server.get_state().await);
159 }
160 status
161 }
162
163 pub async fn get_file_diagnostics(&self, file_path: &Path) -> Vec<LSPDiagnostic> {
165 let uri = format!("file://{}", file_path.display());
166 self.diagnostics_cache
167 .read()
168 .await
169 .get(&uri)
170 .cloned()
171 .unwrap_or_default()
172 }
173
174 pub async fn clear_diagnostics(&self, file_path: Option<&Path>) {
176 if let Some(path) = file_path {
177 let uri = format!("file://{}", path.display());
178 self.diagnostics_cache.write().await.remove(&uri);
179 } else {
180 self.diagnostics_cache.write().await.clear();
181 }
182 }
183}