diff options
author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2024-05-29 01:26:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-29 01:26:43 +0100 |
commit | 14a74600de6f1c206ffe4750f6cb6da59657db8e (patch) | |
tree | ce593041af972fd31f83fa6b7b50458bc9f689dc /cli/lsp/language_server.rs | |
parent | 2024c974b613f94f31559a1b32e2d747c2083e91 (diff) |
perf(lsp): lock out requests until init is complete (#23998)
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 368 |
1 files changed, 252 insertions, 116 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 621f45f96..9abefb141 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -31,7 +31,6 @@ use std::sync::Arc; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; -use tokio_util::sync::CancellationToken; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::lsp_types::request::*; @@ -105,6 +104,7 @@ use crate::tools::upgrade::upgrade_check_enabled; use crate::util::fs::remove_dir_all_if_exists; use crate::util::path::is_importable_ext; use crate::util::path::to_percent_decoded_str; +use crate::util::sync::AsyncFlag; use deno_runtime::fs_util::specifier_to_file_path; struct LspRootCertStoreProvider(RootCertStore); @@ -116,10 +116,17 @@ impl RootCertStoreProvider for LspRootCertStoreProvider { } #[derive(Debug, Clone)] -pub struct LanguageServer( - pub Arc<tokio::sync::RwLock<Inner>>, - CancellationToken, -); +pub struct LanguageServer { + pub inner: Arc<tokio::sync::RwLock<Inner>>, + /// This is used to block out standard request handling until the complete + /// user configuration has been fetched. This is done in the `initialized` + /// handler which normally may occur concurrently with those other requests. + /// TODO(nayeemrmn): This wouldn't be necessary if LSP allowed + /// `workspace/configuration` requests in the `initialize` handler. See: + /// https://github.com/Microsoft/language-server-protocol/issues/567#issuecomment-2085131917 + init_flag: AsyncFlag, + shutdown_flag: AsyncFlag, +} /// Snapshot of the state used by TSC. #[derive(Clone, Debug, Default)] @@ -210,11 +217,12 @@ pub struct Inner { } impl LanguageServer { - pub fn new(client: Client, token: CancellationToken) -> Self { - Self( - Arc::new(tokio::sync::RwLock::new(Inner::new(client))), - token, - ) + pub fn new(client: Client, shutdown_flag: AsyncFlag) -> Self { + Self { + inner: Arc::new(tokio::sync::RwLock::new(Inner::new(client))), + init_flag: Default::default(), + shutdown_flag, + } } /// Similar to `deno cache` on the command line, where modules will be cached @@ -225,6 +233,9 @@ impl LanguageServer { referrer: ModuleSpecifier, force_global_cache: bool, ) -> LspResult<Option<Value>> { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } async fn create_graph_for_caching( cli_options: CliOptions, roots: Vec<ModuleSpecifier>, @@ -272,13 +283,13 @@ impl LanguageServer { // prepare the cache inside the lock let maybe_prepare_cache_result = { - let mut inner = self.0.write().await; // ensure dropped + let mut inner = self.inner.write().await; match inner.prepare_cache(specifiers, referrer, force_global_cache) { Ok(maybe_cache_result) => maybe_cache_result, Err(err) => { lsp_warn!("Error preparing caching: {:#}", err); self - .0 + .inner .read() .await .client @@ -298,7 +309,7 @@ impl LanguageServer { if let Err(err) = handle.await.unwrap() { lsp_warn!("Error caching: {:#}", err); self - .0 + .inner .read() .await .client @@ -306,7 +317,7 @@ impl LanguageServer { } // now get the lock back to update with the new information - let mut inner = self.0.write().await; + let mut inner = self.inner.write().await; inner.resolver.did_cache(); inner.refresh_npm_specifiers().await; inner.diagnostics_server.invalidate_all(); @@ -327,9 +338,12 @@ impl LanguageServer { pub async fn latest_diagnostic_batch_index_request( &self, ) -> LspResult<Option<Value>> { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } Ok( self - .0 + .inner .read() .await .diagnostics_server @@ -339,18 +353,27 @@ impl LanguageServer { } pub async fn performance_request(&self) -> LspResult<Option<Value>> { - Ok(Some(self.0.read().await.get_performance())) + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + Ok(Some(self.inner.read().await.get_performance())) } pub async fn task_definitions(&self) -> LspResult<Vec<TaskDefinition>> { - self.0.read().await.task_definitions() + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.task_definitions() } pub async fn test_run_request( &self, params: Option<Value>, ) -> LspResult<Option<Value>> { - let inner = self.0.read().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + let inner = self.inner.read().await; if let Some(testing_server) = &inner.maybe_testing_server { match params.map(serde_json::from_value) { Some(Ok(params)) => { @@ -370,7 +393,11 @@ impl LanguageServer { &self, params: Option<Value>, ) -> LspResult<Option<Value>> { - if let Some(testing_server) = &self.0.read().await.maybe_testing_server { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + if let Some(testing_server) = &self.inner.read().await.maybe_testing_server + { match params.map(serde_json::from_value) { Some(Ok(params)) => testing_server.run_cancel_request(params), Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), @@ -385,10 +412,13 @@ impl LanguageServer { &self, params: Option<Value>, ) -> LspResult<Option<Value>> { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } match params.map(serde_json::from_value) { Some(Ok(params)) => Ok(Some( serde_json::to_value( - self.0.read().await.virtual_text_document(params)?, + self.inner.read().await.virtual_text_document(params)?, ) .map_err(|err| { error!( @@ -405,11 +435,11 @@ impl LanguageServer { pub async fn refresh_configuration(&self) { let (client, folders, capable) = { - let ls = self.0.read().await; + let inner = self.inner.read().await; ( - ls.client.clone(), - ls.config.workspace_folders.clone(), - ls.config.client_capabilities.workspace_configuration, + inner.client.clone(), + inner.config.workspace_folders.clone(), + inner.config.client_capabilities.workspace_configuration, ) }; if capable { @@ -433,8 +463,10 @@ impl LanguageServer { for (folder_uri, _) in &folders { folder_settings.push((folder_uri.clone(), configs.next().unwrap())); } - let mut ls = self.0.write().await; - ls.config.set_workspace_settings(unscoped, folder_settings); + let mut inner = self.inner.write().await; + inner + .config + .set_workspace_settings(unscoped, folder_settings); } } } @@ -751,10 +783,6 @@ impl Inner { }; self.update_debug_flag(); - self.update_global_cache().await; - self.refresh_workspace_files(); - self.refresh_config_tree().await; - self.update_cache(); if capabilities.code_action_provider.is_some() { let fixable_diagnostics = self @@ -947,10 +975,14 @@ impl Inner { } async fn refresh_resolver(&mut self) { - self.resolver = self - .resolver - .with_new_config(&self.config, &self.cache, Some(&self.http_client)) - .await; + self.resolver = Arc::new( + LspResolver::from_config( + &self.config, + &self.cache, + Some(&self.http_client), + ) + .await, + ); } async fn refresh_documents_config(&mut self) { @@ -968,10 +1000,6 @@ impl Inner { self.project_changed([], true); } - fn shutdown(&self) -> LspResult<()> { - Ok(()) - } - async fn did_open(&mut self, params: DidOpenTextDocumentParams) { let mark = self.performance.mark_with_args("lsp.did_open", ¶ms); if params.text_document.uri.scheme() == "deno" { @@ -1149,7 +1177,17 @@ impl Inner { self.workspace_files_hash = 0; self.refresh_workspace_files(); self.refresh_config_tree().await; + self.update_cache(); self.refresh_resolver().await; + self.refresh_documents_config().await; + self.project_changed( + changes.iter().map(|(s, _)| (s, ChangeKind::Modified)), + false, + ); + self.ts_server.cleanup_semantic_cache(self.snapshot()).await; + self.diagnostics_server.invalidate_all(); + self.send_diagnostics_update(); + self.send_testing_update(); deno_config_changes.extend(changes.iter().filter_map(|(s, e)| { self.config.tree.watched_file_type(s).and_then(|t| { let configuration_type = match t.1 { @@ -1178,15 +1216,6 @@ impl Inner { }, ); } - self.refresh_documents_config().await; - self.diagnostics_server.invalidate_all(); - self.project_changed( - changes.iter().map(|(s, _)| (s, ChangeKind::Modified)), - false, - ); - self.ts_server.cleanup_semantic_cache(self.snapshot()).await; - self.send_diagnostics_update(); - self.send_testing_update(); } self.performance.measure(mark); } @@ -1254,9 +1283,8 @@ impl Inner { } let document = file_referrer.and_then(|r| self.documents.get_or_load(&specifier, &r)); - let document = match document { - Some(doc) if doc.is_open() => doc, - _ => return Ok(None), + let Some(document) = document else { + return Ok(None); }; // Detect vendored paths. Vendor file URLs will normalize to their remote // counterparts, but for formatting we want to favour the file URL. @@ -2812,6 +2840,9 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: ExecuteCommandParams, ) -> LspResult<Option<Value>> { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } if params.command == "deno.cache" { #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase")] @@ -2828,7 +2859,7 @@ impl tower_lsp::LanguageServer for LanguageServer { .cache(specifiers, referrer, options.force_global_cache) .await } else if params.command == "deno.reloadImportRegistries" { - self.0.write().await.reload_import_registries().await + self.inner.write().await.reload_import_registries().await } else { Ok(None) } @@ -2838,14 +2869,25 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: InitializeParams, ) -> LspResult<InitializeResult> { - self.0.write().await.initialize(params).await + self.inner.write().await.initialize(params).await } async fn initialized(&self, _: InitializedParams) { + self.refresh_configuration().await; let mut registrations = Vec::with_capacity(2); let (client, http_client) = { - let mut ls = self.0.write().await; - if ls + let mut inner = self.inner.write().await; + init_log_file(inner.config.log_file()); + inner.update_debug_flag(); + inner.update_global_cache().await; + inner.refresh_workspace_files(); + inner.refresh_config_tree().await; + inner.update_cache(); + inner.refresh_resolver().await; + inner.refresh_documents_config().await; + inner.task_queue.start(self.clone()); + self.init_flag.raise(); + if inner .config .client_capabilities .workspace_did_change_watched_files @@ -2867,7 +2909,7 @@ impl tower_lsp::LanguageServer for LanguageServer { register_options: Some(serde_json::to_value(options).unwrap()), }); } - if ls.config.client_capabilities.workspace_will_rename_files { + if inner.config.client_capabilities.workspace_will_rename_files { let options = FileOperationRegistrationOptions { filters: vec![FileOperationFilter { scheme: Some("file".to_string()), @@ -2885,17 +2927,17 @@ impl tower_lsp::LanguageServer for LanguageServer { }); } - if ls.config.client_capabilities.testing_api { + if inner.config.client_capabilities.testing_api { let test_server = testing::TestServer::new( - ls.client.clone(), - ls.performance.clone(), - ls.config.root_uri().cloned(), + inner.client.clone(), + inner.performance.clone(), + inner.config.root_uri().cloned(), ); - ls.maybe_testing_server = Some(test_server); + inner.maybe_testing_server = Some(test_server); } let mut config_events = vec![]; - for (scope_uri, config_data) in ls.config.tree.data_by_scope().iter() { + for (scope_uri, config_data) in inner.config.tree.data_by_scope().iter() { if let Some(config_file) = &config_data.config_file { config_events.push(lsp_custom::DenoConfigurationChangeEvent { scope_uri: scope_uri.clone(), @@ -2914,14 +2956,16 @@ impl tower_lsp::LanguageServer for LanguageServer { } } if !config_events.is_empty() { - ls.client.send_did_change_deno_configuration_notification( - lsp_custom::DidChangeDenoConfigurationNotificationParams { - changes: config_events, - }, - ); + inner + .client + .send_did_change_deno_configuration_notification( + lsp_custom::DidChangeDenoConfigurationNotificationParams { + changes: config_events, + }, + ); } - (ls.client.clone(), ls.http_client.clone()) + (inner.client.clone(), inner.http_client.clone()) }; for registration in registrations { @@ -2934,18 +2978,6 @@ impl tower_lsp::LanguageServer for LanguageServer { } } - self.refresh_configuration().await; - - { - let mut ls = self.0.write().await; - init_log_file(ls.config.log_file()); - ls.refresh_resolver().await; - ls.refresh_documents_config().await; - ls.diagnostics_server.invalidate_all(); - ls.send_diagnostics_update(); - ls.task_queue.start(self.clone()); - }; - if upgrade_check_enabled() { // spawn to avoid lsp send/sync requirement, but also just // to ensure this initialized method returns quickly @@ -2972,38 +3004,53 @@ impl tower_lsp::LanguageServer for LanguageServer { } async fn shutdown(&self) -> LspResult<()> { - self.1.cancel(); - self.0.write().await.shutdown() + self.shutdown_flag.raise(); + Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { - self.0.write().await.did_open(params).await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.write().await.did_open(params).await; } async fn did_change(&self, params: DidChangeTextDocumentParams) { - self.0.write().await.did_change(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.write().await.did_change(params).await } async fn did_save(&self, params: DidSaveTextDocumentParams) { - self.0.write().await.did_save(params); + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.write().await.did_save(params); } async fn did_close(&self, params: DidCloseTextDocumentParams) { - self.0.write().await.did_close(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.write().await.did_close(params).await } async fn did_change_configuration( &self, params: DidChangeConfigurationParams, ) { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } let mark = { - let inner = self.0.read().await; + let inner = self.inner.read().await; inner .performance .mark_with_args("lsp.did_change_configuration", ¶ms) }; self.refresh_configuration().await; - let mut inner = self.0.write().await; + let mut inner = self.inner.write().await; if !inner.config.client_capabilities.workspace_configuration { let config = params.settings.as_object().map(|settings| { let deno = @@ -3035,15 +3082,26 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DidChangeWatchedFilesParams, ) { - self.0.write().await.did_change_watched_files(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self + .inner + .write() + .await + .did_change_watched_files(params) + .await } async fn did_change_workspace_folders( &self, params: DidChangeWorkspaceFoldersParams, ) { + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } let mark = { - let mut inner = self.0.write().await; + let mut inner = self.inner.write().await; let mark = inner .performance .mark_with_args("lsp.did_change_workspace_folders", ¶ms); @@ -3070,7 +3128,7 @@ impl tower_lsp::LanguageServer for LanguageServer { mark }; self.refresh_configuration().await; - let mut inner = self.0.write().await; + let mut inner = self.inner.write().await; inner.refresh_workspace_files(); inner.refresh_config_tree().await; inner.refresh_resolver().await; @@ -3085,176 +3143,254 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DocumentSymbolParams, ) -> LspResult<Option<DocumentSymbolResponse>> { - self.0.read().await.document_symbol(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.document_symbol(params).await } async fn formatting( &self, params: DocumentFormattingParams, ) -> LspResult<Option<Vec<TextEdit>>> { - self.0.read().await.formatting(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.formatting(params).await } async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { - self.0.read().await.hover(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.hover(params).await } async fn inlay_hint( &self, params: InlayHintParams, ) -> LspResult<Option<Vec<InlayHint>>> { - self.0.read().await.inlay_hint(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.inlay_hint(params).await } async fn code_action( &self, params: CodeActionParams, ) -> LspResult<Option<CodeActionResponse>> { - self.0.read().await.code_action(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.code_action(params).await } async fn code_action_resolve( &self, params: CodeAction, ) -> LspResult<CodeAction> { - self.0.read().await.code_action_resolve(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.code_action_resolve(params).await } async fn code_lens( &self, params: CodeLensParams, ) -> LspResult<Option<Vec<CodeLens>>> { - self.0.read().await.code_lens(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.code_lens(params).await } async fn code_lens_resolve(&self, params: CodeLens) -> LspResult<CodeLens> { - self.0.read().await.code_lens_resolve(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.code_lens_resolve(params).await } async fn document_highlight( &self, params: DocumentHighlightParams, ) -> LspResult<Option<Vec<DocumentHighlight>>> { - self.0.read().await.document_highlight(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.document_highlight(params).await } async fn references( &self, params: ReferenceParams, ) -> LspResult<Option<Vec<Location>>> { - self.0.read().await.references(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.references(params).await } async fn goto_definition( &self, params: GotoDefinitionParams, ) -> LspResult<Option<GotoDefinitionResponse>> { - self.0.read().await.goto_definition(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.goto_definition(params).await } async fn goto_type_definition( &self, params: GotoTypeDefinitionParams, ) -> LspResult<Option<GotoTypeDefinitionResponse>> { - self.0.read().await.goto_type_definition(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.goto_type_definition(params).await } async fn completion( &self, params: CompletionParams, ) -> LspResult<Option<CompletionResponse>> { - self.0.read().await.completion(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.completion(params).await } async fn completion_resolve( &self, params: CompletionItem, ) -> LspResult<CompletionItem> { - self.0.read().await.completion_resolve(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.completion_resolve(params).await } async fn goto_implementation( &self, params: GotoImplementationParams, ) -> LspResult<Option<GotoImplementationResponse>> { - self.0.read().await.goto_implementation(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.goto_implementation(params).await } async fn folding_range( &self, params: FoldingRangeParams, ) -> LspResult<Option<Vec<FoldingRange>>> { - self.0.read().await.folding_range(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.folding_range(params).await } async fn incoming_calls( &self, params: CallHierarchyIncomingCallsParams, ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> { - self.0.read().await.incoming_calls(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.incoming_calls(params).await } async fn outgoing_calls( &self, params: CallHierarchyOutgoingCallsParams, ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> { - self.0.read().await.outgoing_calls(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.outgoing_calls(params).await } async fn prepare_call_hierarchy( &self, params: CallHierarchyPrepareParams, ) -> LspResult<Option<Vec<CallHierarchyItem>>> { - self.0.read().await.prepare_call_hierarchy(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.prepare_call_hierarchy(params).await } async fn rename( &self, params: RenameParams, ) -> LspResult<Option<WorkspaceEdit>> { - self.0.read().await.rename(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.rename(params).await } async fn selection_range( &self, params: SelectionRangeParams, ) -> LspResult<Option<Vec<SelectionRange>>> { - self.0.read().await.selection_range(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.selection_range(params).await } async fn semantic_tokens_full( &self, params: SemanticTokensParams, ) -> LspResult<Option<SemanticTokensResult>> { - self.0.read().await.semantic_tokens_full(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.semantic_tokens_full(params).await } async fn semantic_tokens_range( &self, params: SemanticTokensRangeParams, ) -> LspResult<Option<SemanticTokensRangeResult>> { - self.0.read().await.semantic_tokens_range(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.semantic_tokens_range(params).await } async fn signature_help( &self, params: SignatureHelpParams, ) -> LspResult<Option<SignatureHelp>> { - self.0.read().await.signature_help(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.signature_help(params).await } async fn will_rename_files( &self, params: RenameFilesParams, ) -> LspResult<Option<WorkspaceEdit>> { - self.0.read().await.will_rename_files(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.will_rename_files(params).await } async fn symbol( &self, params: WorkspaceSymbolParams, ) -> LspResult<Option<Vec<SymbolInformation>>> { - self.0.read().await.symbol(params).await + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; + } + self.inner.read().await.symbol(params).await } } |