diff options
Diffstat (limited to 'cli/lsp/config.rs')
-rw-r--r-- | cli/lsp/config.rs | 289 |
1 files changed, 239 insertions, 50 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 441b2cc99..3e1b9fe85 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,14 +1,22 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use crate::tokio_util::create_basic_runtime; + +use deno_core::error::anyhow; +use deno_core::error::AnyError; use deno_core::serde::Deserialize; use deno_core::serde_json; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; -use lspower::jsonrpc::Error as LSPError; -use lspower::jsonrpc::Result as LSPResult; +use log::error; use lspower::lsp; +use std::collections::BTreeMap; use std::collections::HashMap; +use std::sync::Arc; +use std::sync::RwLock; +use std::thread; +use tokio::sync::mpsc; pub const SETTINGS_SECTION: &str = "deno"; @@ -139,25 +147,201 @@ impl WorkspaceSettings { } } +#[derive(Debug, Clone, Default)] +pub struct ConfigSnapshot { + pub client_capabilities: ClientCapabilities, + pub root_uri: Option<Url>, + pub settings: Settings, +} + +impl ConfigSnapshot { + pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool { + if let Some(settings) = self.settings.specifiers.get(specifier) { + settings.1.enable + } else { + self.settings.workspace.enable + } + } +} + +enum ConfigRequest { + Specifier(ModuleSpecifier, ModuleSpecifier), + Workspace, +} + #[derive(Debug, Default, Clone)] +pub struct Settings { + pub specifiers: + BTreeMap<ModuleSpecifier, (ModuleSpecifier, SpecifierSettings)>, + pub workspace: WorkspaceSettings, +} + +#[derive(Debug)] pub struct Config { pub client_capabilities: ClientCapabilities, pub root_uri: Option<Url>, - pub specifier_settings: HashMap<ModuleSpecifier, SpecifierSettings>, - pub workspace_settings: WorkspaceSettings, + settings: Arc<RwLock<Settings>>, + tx: mpsc::Sender<ConfigRequest>, } impl Config { - #[allow(unused)] - pub fn contains(&self, specifier: &ModuleSpecifier) -> bool { - self.specifier_settings.contains_key(specifier) + pub fn new(client: lspower::Client) -> Self { + let (tx, mut rx) = mpsc::channel::<ConfigRequest>(100); + let settings = Arc::new(RwLock::new(Settings::default())); + let settings_ref = settings.clone(); + + let _join_handle = thread::spawn(move || { + let runtime = create_basic_runtime(); + + runtime.block_on(async { + loop { + match rx.recv().await { + None => break, + Some(ConfigRequest::Workspace) => { + let mut items = vec![lsp::ConfigurationItem { + scope_uri: None, + section: Some(SETTINGS_SECTION.to_string()), + }]; + let (specifier_uri_map, mut specifier_items): ( + Vec<(ModuleSpecifier, ModuleSpecifier)>, + Vec<lsp::ConfigurationItem>, + ) = { + let settings = settings_ref.read().unwrap(); + ( + settings + .specifiers + .iter() + .map(|(s, (u, _))| (s.clone(), u.clone())) + .collect(), + settings + .specifiers + .iter() + .map(|(_, (uri, _))| lsp::ConfigurationItem { + scope_uri: Some(uri.clone()), + section: Some(SETTINGS_SECTION.to_string()), + }) + .collect(), + ) + }; + items.append(&mut specifier_items); + if let Ok(configs) = client.configuration(items).await { + let mut settings = settings_ref.write().unwrap(); + for (i, value) in configs.into_iter().enumerate() { + match i { + 0 => { + match serde_json::from_value::<WorkspaceSettings>(value) { + Ok(workspace_settings) => { + settings.workspace = workspace_settings; + } + Err(err) => { + error!( + "Error converting workspace settings: {}", + err + ); + } + } + } + _ => { + match serde_json::from_value::<SpecifierSettings>(value) { + Ok(specifier_settings) => { + let (specifier, uri) = + specifier_uri_map[i - 1].clone(); + settings + .specifiers + .insert(specifier, (uri, specifier_settings)); + } + Err(err) => { + error!( + "Error converting specifier settings: {}", + err + ); + } + } + } + } + } + } + } + Some(ConfigRequest::Specifier(specifier, uri)) => { + if settings_ref + .read() + .unwrap() + .specifiers + .contains_key(&specifier) + { + continue; + } + if let Ok(value) = client + .configuration(vec![lsp::ConfigurationItem { + scope_uri: Some(uri.clone()), + section: Some(SETTINGS_SECTION.to_string()), + }]) + .await + { + match serde_json::from_value::<SpecifierSettings>( + value[0].clone(), + ) { + Ok(specifier_settings) => { + settings_ref + .write() + .unwrap() + .specifiers + .insert(specifier, (uri, specifier_settings)); + } + Err(err) => { + error!("Error converting specifier settings: {}", err); + } + } + } else { + error!( + "Error retrieving settings for specifier: {}", + specifier + ); + } + } + } + } + }) + }); + + Self { + client_capabilities: ClientCapabilities::default(), + root_uri: None, + settings, + tx, + } + } + + pub fn get_workspace_settings(&self) -> WorkspaceSettings { + self.settings.read().unwrap().workspace.clone() + } + + /// Set the workspace settings directly, which occurs during initialization + /// and when the client does not support workspace configuration requests + pub fn set_workspace_settings(&self, value: Value) -> Result<(), AnyError> { + let workspace_settings = serde_json::from_value(value)?; + self.settings.write().unwrap().workspace = workspace_settings; + Ok(()) + } + + pub fn snapshot(&self) -> Result<ConfigSnapshot, AnyError> { + Ok(ConfigSnapshot { + client_capabilities: self.client_capabilities.clone(), + root_uri: self.root_uri.clone(), + settings: self + .settings + .try_read() + .map_err(|_| anyhow!("Error reading settings."))? + .clone(), + }) } pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool { - if let Some(settings) = self.specifier_settings.get(specifier) { - settings.enable + let settings = self.settings.read().unwrap(); + if let Some(specifier_settings) = settings.specifiers.get(specifier) { + specifier_settings.1.enable } else { - self.workspace_settings.enable + settings.workspace.enable } } @@ -192,23 +376,24 @@ impl Config { } } - pub fn update_specifier( - &mut self, - specifier: ModuleSpecifier, - value: Value, - ) -> LSPResult<()> { - let settings: SpecifierSettings = serde_json::from_value(value) - .map_err(|err| LSPError::invalid_params(err.to_string()))?; - self.specifier_settings.insert(specifier, settings); - Ok(()) + pub async fn update_specifier_settings( + &self, + specifier: &ModuleSpecifier, + uri: &ModuleSpecifier, + ) -> Result<(), AnyError> { + self + .tx + .send(ConfigRequest::Specifier(specifier.clone(), uri.clone())) + .await + .map_err(|_| anyhow!("Error sending config update task.")) } - pub fn update_workspace(&mut self, value: Value) -> LSPResult<()> { - let settings: WorkspaceSettings = serde_json::from_value(value) - .map_err(|err| LSPError::invalid_params(err.to_string()))?; - self.workspace_settings = settings; - self.specifier_settings = HashMap::new(); - Ok(()) + pub async fn update_workspace_settings(&self) -> Result<(), AnyError> { + self + .tx + .send(ConfigRequest::Workspace) + .await + .map_err(|_| anyhow!("Error sending config update task.")) } } @@ -218,41 +403,45 @@ mod tests { use deno_core::resolve_url; use deno_core::serde_json::json; - #[test] - fn test_config_contains() { - let mut config = Config::default(); - let specifier = resolve_url("https://deno.land/x/a.ts").unwrap(); - assert!(!config.contains(&specifier)); - config - .update_specifier( - specifier.clone(), - json!({ - "enable": true - }), - ) - .expect("could not update specifier"); - assert!(config.contains(&specifier)); + #[derive(Debug, Default)] + struct MockLanguageServer; + + #[lspower::async_trait] + impl lspower::LanguageServer for MockLanguageServer { + async fn initialize( + &self, + _params: lspower::lsp::InitializeParams, + ) -> lspower::jsonrpc::Result<lsp::InitializeResult> { + Ok(lspower::lsp::InitializeResult { + capabilities: lspower::lsp::ServerCapabilities::default(), + server_info: None, + }) + } + + async fn shutdown(&self) -> lspower::jsonrpc::Result<()> { + Ok(()) + } + } + + fn setup() -> Config { + let mut maybe_client: Option<lspower::Client> = None; + let (_service, _) = lspower::LspService::new(|client| { + maybe_client = Some(client); + MockLanguageServer::default() + }); + Config::new(maybe_client.unwrap()) } #[test] fn test_config_specifier_enabled() { - let mut config = Config::default(); + let config = setup(); let specifier = resolve_url("file:///a.ts").unwrap(); assert!(!config.specifier_enabled(&specifier)); config - .update_workspace(json!({ + .set_workspace_settings(json!({ "enable": true })) .expect("could not update"); assert!(config.specifier_enabled(&specifier)); - config - .update_specifier( - specifier.clone(), - json!({ - "enable": false - }), - ) - .expect("could not update"); - assert!(!config.specifier_enabled(&specifier)); } } |