use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use super::types::{LspServerConfig, LspServerInfo, LspServerStatus, LspConfig};
pub struct LspManager {
configs: HashMap<String, LspServerConfig>,
statuses: Arc<RwLock<HashMap<String, LspServerStatus>>>,
}
impl LspManager {
pub fn new() -> Self {
Self {
configs: HashMap::new(),
statuses: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn from_config(config: &LspConfig) -> Self {
let configs: HashMap<String, LspServerConfig> = config
.servers
.iter()
.filter(|s| s.enabled)
.map(|s| (s.language.clone(), s.clone()))
.collect();
let statuses = configs
.keys()
.map(|k| (k.clone(), LspServerStatus::NotStarted))
.collect();
Self {
configs,
statuses: Arc::new(RwLock::new(statuses)),
}
}
pub fn add_server(&mut self, config: LspServerConfig) {
let name = config.language.clone();
self.configs.insert(name.clone(), config);
if let Ok(mut statuses) = self.statuses.write() {
statuses.insert(name, LspServerStatus::NotStarted);
}
}
pub fn remove_server(&mut self, name: &str) {
self.configs.remove(name);
if let Ok(mut statuses) = self.statuses.write() {
statuses.remove(name);
}
}
pub fn server_infos(&self) -> Vec<LspServerInfo> {
if let Ok(statuses) = self.statuses.read() {
self.configs
.iter()
.map(|(name, config)| {
let status = statuses.get(name).cloned().unwrap_or(LspServerStatus::NotStarted);
LspServerInfo {
name: config.command.clone(),
language: config.language.clone(),
status,
}
})
.collect()
} else {
self.configs
.iter()
.map(|(_name, config)| {
LspServerInfo {
name: config.command.clone(),
language: config.language.clone(),
status: LspServerStatus::NotStarted,
}
})
.collect()
}
}
pub fn get_status(&self, name: &str) -> LspServerStatus {
self.statuses
.read()
.map(|s| s.get(name).cloned().unwrap_or(LspServerStatus::NotStarted))
.unwrap_or(LspServerStatus::NotStarted)
}
pub fn set_status(&self, name: &str, status: LspServerStatus) {
if let Ok(mut statuses) = self.statuses.write() {
statuses.insert(name.to_string(), status);
}
}
pub fn mark_connected(&self, name: &str) {
self.set_status(name, LspServerStatus::Connected);
}
pub fn mark_error(&self, name: &str, msg: impl Into<String>) {
self.set_status(name, LspServerStatus::Error(msg.into()));
}
pub fn get_config(&self, name: &str) -> Option<&LspServerConfig> {
self.configs.get(name)
}
pub fn server_names(&self) -> Vec<&String> {
self.configs.keys().collect()
}
pub fn connected_count(&self) -> usize {
self.statuses
.read()
.map(|s| s.values().filter(|s| s.is_ok()).count())
.unwrap_or(0)
}
pub fn error_count(&self) -> usize {
self.statuses
.read()
.map(|s| s.values().filter(|s| s.is_error()).count())
.unwrap_or(0)
}
pub fn has_servers(&self) -> bool {
!self.configs.is_empty()
}
pub fn reset_all(&self) {
if let Ok(mut statuses) = self.statuses.write() {
for name in self.configs.keys() {
statuses.insert(name.clone(), LspServerStatus::NotStarted);
}
}
}
}
impl Default for LspManager {
fn default() -> Self {
Self::new()
}
}
pub fn find_lsp_config(start_dir: &std::path::Path) -> Option<std::path::PathBuf> {
let local_config = start_dir.join("lsp.toml");
if local_config.exists() {
return Some(local_config);
}
if let Some(home) = dirs::home_dir() {
let home_config = home.join(".config").join("matrixcode").join("lsp.toml");
if home_config.exists() {
return Some(home_config);
}
}
None
}
pub fn load_lsp_config(start_dir: &std::path::Path) -> LspConfig {
find_lsp_config(start_dir)
.and_then(|p| LspConfig::from_file(&p).ok())
.unwrap_or_default()
}