summaryrefslogtreecommitdiff
path: root/cli/lsp/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/config.rs')
-rw-r--r--cli/lsp/config.rs289
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));
}
}