From dfafb948e2023504b6fdd60d2a58291ee2de715b Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Thu, 13 Jun 2024 20:57:14 +0100 Subject: [PATCH] perf(lsp): store settings in Arc (#24191) --- cli/lsp/config.rs | 82 ++++++++++++++++++++++++-------------- cli/lsp/diagnostics.rs | 23 ++++++----- cli/lsp/language_server.rs | 6 +-- 3 files changed, 68 insertions(+), 43 deletions(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index bfadfe5ea2..ae9ec6db84 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -722,8 +722,9 @@ impl WorkspaceSettings { #[derive(Debug, Default, Clone)] pub struct Settings { - pub unscoped: WorkspaceSettings, - pub by_workspace_folder: BTreeMap>, + pub unscoped: Arc, + pub by_workspace_folder: + BTreeMap>>, pub first_folder: Option, } @@ -815,9 +816,9 @@ impl Settings { #[derive(Clone, Debug, Default)] pub struct Config { - pub client_capabilities: ClientCapabilities, - pub settings: Settings, - pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>, + pub client_capabilities: Arc, + pub settings: Arc, + pub workspace_folders: Arc>, pub tree: ConfigTree, } @@ -845,10 +846,15 @@ impl Config { &mut self, folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>, ) { - self.settings.by_workspace_folder = - folders.iter().map(|(s, _)| (s.clone(), None)).collect(); - self.settings.first_folder = folders.first().map(|(s, _)| s.clone()); - self.workspace_folders = folders; + self.settings = Arc::new(Settings { + unscoped: self.settings.unscoped.clone(), + by_workspace_folder: folders + .iter() + .map(|(s, _)| (s.clone(), None)) + .collect(), + first_folder: folders.first().map(|(s, _)| s.clone()), + }); + self.workspace_folders = Arc::new(folders); } pub fn set_workspace_settings( @@ -856,14 +862,17 @@ impl Config { unscoped: WorkspaceSettings, folder_settings: Vec<(ModuleSpecifier, WorkspaceSettings)>, ) { - self.settings.unscoped = unscoped; - for (folder_uri, settings) in folder_settings.into_iter() { - if let Some(settings_) = - self.settings.by_workspace_folder.get_mut(&folder_uri) - { - *settings_ = Some(settings); - } - } + let mut by_folder = folder_settings.into_iter().collect::>(); + self.settings = Arc::new(Settings { + unscoped: Arc::new(unscoped), + by_workspace_folder: self + .settings + .by_workspace_folder + .keys() + .map(|s| (s.clone(), by_folder.remove(s).map(Arc::new))) + .collect(), + first_folder: self.settings.first_folder.clone(), + }); } pub fn workspace_settings(&self) -> &WorkspaceSettings { @@ -966,7 +975,7 @@ impl Config { &mut self, client_capabilities: ClientCapabilities, ) { - self.client_capabilities = client_capabilities; + self.client_capabilities = Arc::new(client_capabilities); } pub fn workspace_capable(&self) -> bool { @@ -1906,10 +1915,15 @@ mod tests { fn test_config_specifier_disabled_path() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); - config.settings.unscoped.enable = Some(true); - config.settings.unscoped.enable_paths = - Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]); - config.settings.unscoped.disable_paths = vec!["mod2.ts".to_string()]; + config.set_workspace_settings( + WorkspaceSettings { + enable: Some(true), + enable_paths: Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]), + disable_paths: vec!["mod2.ts".to_string()], + ..Default::default() + }, + vec![], + ); assert!(config.specifier_enabled(&root_uri.join("mod1.ts").unwrap())); assert!(!config.specifier_enabled(&root_uri.join("mod2.ts").unwrap())); @@ -2107,7 +2121,6 @@ mod tests { async fn config_enable_via_config_file_detection() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); - config.settings.unscoped.enable = None; assert!(!config.specifier_enabled(&root_uri)); config @@ -2129,7 +2142,13 @@ mod tests { fn config_specifier_enabled_matches_by_path_component() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); - config.settings.unscoped.enable_paths = Some(vec!["mo".to_string()]); + config.set_workspace_settings( + WorkspaceSettings { + enable_paths: Some(vec!["mo".to_string()]), + ..Default::default() + }, + vec![], + ); assert!(!config.specifier_enabled(&root_uri.join("mod.ts").unwrap())); } @@ -2137,11 +2156,13 @@ mod tests { async fn config_specifier_enabled_for_test() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); - config.settings.unscoped.enable = Some(true); - - config.settings.unscoped.enable_paths = - Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]); - config.settings.unscoped.disable_paths = vec!["mod2.ts".to_string()]; + let mut settings = WorkspaceSettings { + enable: Some(true), + enable_paths: Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]), + disable_paths: vec!["mod2.ts".to_string()], + ..Default::default() + }; + config.set_workspace_settings(settings.clone(), vec![]); assert!( config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap()) ); @@ -2151,7 +2172,8 @@ mod tests { assert!( !config.specifier_enabled_for_test(&root_uri.join("mod3.ts").unwrap()) ); - config.settings.unscoped.enable_paths = None; + settings.enable_paths = None; + config.set_workspace_settings(settings, vec![]); config .tree diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 6504c38fed..e76d2c5b0e 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1611,21 +1611,21 @@ mod tests { fn mock_config() -> Config { let root_uri = resolve_url("file:///").unwrap(); Config { - settings: Settings { - unscoped: WorkspaceSettings { + settings: Arc::new(Settings { + unscoped: Arc::new(WorkspaceSettings { enable: Some(true), lint: true, ..Default::default() - }, + }), ..Default::default() - }, - workspace_folders: vec![( + }), + workspace_folders: Arc::new(vec![( root_uri.clone(), lsp::WorkspaceFolder { uri: root_uri, name: "".to_string(), }, - )], + )]), ..Default::default() } } @@ -1719,10 +1719,13 @@ let c: number = "a"; // now test disabled specifier { let mut disabled_config = mock_config(); - disabled_config.settings.unscoped = WorkspaceSettings { - enable: Some(false), - ..Default::default() - }; + disabled_config.set_workspace_settings( + WorkspaceSettings { + enable: Some(false), + ..Default::default() + }, + vec![], + ); let diagnostics = generate_lint_diagnostics( &snapshot, diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 7e4cf55ab1..b8194109de 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -446,7 +446,7 @@ impl LanguageServer { if capable { let mut scopes = Vec::with_capacity(folders.len() + 1); scopes.push(None); - for (_, folder) in &folders { + for (_, folder) in folders.as_ref() { scopes.push(Some(folder.uri.clone())); } let configs = client @@ -461,7 +461,7 @@ impl LanguageServer { let mut configs = configs.into_iter(); let unscoped = configs.next().unwrap(); let mut folder_settings = Vec::with_capacity(folders.len()); - for (folder_uri, _) in &folders { + for (folder_uri, _) in folders.as_ref() { folder_settings.push((folder_uri.clone(), configs.next().unwrap())); } let mut inner = self.inner.write().await; @@ -3146,7 +3146,7 @@ impl tower_lsp::LanguageServer for LanguageServer { ) }) .collect::>(); - for (specifier, folder) in &inner.config.workspace_folders { + for (specifier, folder) in inner.config.workspace_folders.as_ref() { if !params.event.removed.is_empty() && params.event.removed.iter().any(|f| f.uri == folder.uri) {