use std::cell::RefCell;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use anyhow::Context;
use anyhow::Result;
use crate::configuration::ResolvedConfigPathWithText;
use crate::configuration::get_default_config_file_in_ancestor_directories;
use crate::configuration::resolve_config_from_path_with_bytes;
use crate::configuration::resolve_global_config_path_and_text;
use crate::environment::Environment;
use crate::plugins;
use crate::resolution::PluginsScope;
use crate::resolution::resolve_plugins_scope;
use crate::utils::AsyncMutex;
use crate::utils::PathSource;
type ScopeCell<TEnvironment> = AsyncMutex<Option<Rc<PluginsScope<TEnvironment>>>>;
pub struct LspPluginsScopeContainer<TEnvironment: Environment> {
environment: TEnvironment,
plugin_resolver: Rc<plugins::PluginResolver<TEnvironment>>,
plugins_scope_by_config: RefCell<HashMap<String, Rc<ScopeCell<TEnvironment>>>>,
config_override: Option<PathBuf>,
}
impl<TEnvironment: Environment> LspPluginsScopeContainer<TEnvironment> {
pub fn new(environment: TEnvironment, plugin_resolver: Rc<plugins::PluginResolver<TEnvironment>>, config_override: Option<PathBuf>) -> Self {
Self {
environment,
plugin_resolver,
plugins_scope_by_config: Default::default(),
config_override,
}
}
pub async fn shutdown(&self) {
self.plugins_scope_by_config.borrow_mut().clear();
self.plugin_resolver.clear_and_shutdown_initialized().await;
}
pub async fn resolve_by_path(&self, dir_path: &Path) -> Result<Option<Rc<PluginsScope<TEnvironment>>>> {
let config_file_bytes = if let Some(path) = &self.config_override {
let path = self.environment.canonicalize(path).context("failed resolving --config path")?;
let content = self.environment.read_file(&path).context("failed resolving --config path")?;
Some(ResolvedConfigPathWithText {
base_path: path.parent().unwrap_or_else(|| path.clone()),
source: PathSource::new_local(path),
is_first_download: false,
content,
is_global_config: false,
})
} else {
match get_default_config_file_in_ancestor_directories(&self.environment, dir_path)? {
Some(config) => Some(config),
None => resolve_global_config_path_and_text(&self.environment)?,
}
};
let Some(config_file_bytes) = config_file_bytes else {
return Ok(None);
};
let cell = {
let mut plugins_scope_by_config = self.plugins_scope_by_config.borrow_mut();
plugins_scope_by_config.entry(config_file_bytes.source.display()).or_default().clone()
};
let mut cell = cell.lock().await;
let config = resolve_config_from_path_with_bytes(&config_file_bytes, &self.environment).await?;
if let Some(existing_scope) = cell.as_ref() {
if existing_scope.config.as_deref() == Some(&config) {
return Ok(Some(existing_scope.clone()));
}
self.plugin_resolver.clear_and_shutdown_initialized().await;
}
let new_scope = Rc::new(resolve_plugins_scope(Rc::new(config), &self.environment, &self.plugin_resolver).await?);
let _ = cell.insert(new_scope.clone());
Ok(Some(new_scope))
}
}