From f8b9eda712c03df01c322aeaf6c29722e7473b41 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Tue, 28 May 2024 00:15:26 +0100 Subject: [PATCH 1/4] perf(lsp): lock out requests until init is complete --- cli/lsp/diagnostics.rs | 5 +- cli/lsp/documents.rs | 15 +- cli/lsp/language_server.rs | 366 ++++++++++++++++++++++----------- cli/lsp/resolver.rs | 54 +---- cli/lsp/tsc.rs | 5 +- tests/integration/lsp_tests.rs | 87 +++----- tests/util/server/src/lsp.rs | 1 + 7 files changed, 300 insertions(+), 233 deletions(-) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 5c982b0f3fa8e5..35da778dc81930 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1629,9 +1629,8 @@ mod tests { .unwrap(); config.tree.inject_config_file(config_file).await; } - let resolver = LspResolver::default() - .with_new_config(&config, &cache, None) - .await; + let resolver = + Arc::new(LspResolver::from_config(&config, &cache, None).await); let mut documents = Documents::default(); documents.update_config(&config, &resolver, &cache, &Default::default()); for (specifier, source, version, language_id) in sources { diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index ab053336ffa58b..c0c533c343e410 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1530,9 +1530,8 @@ mod tests { let temp_dir = TempDir::new(); let cache = LspCache::new(Some(temp_dir.uri())); let config = Config::default(); - let resolver = LspResolver::default() - .with_new_config(&config, &cache, None) - .await; + let resolver = + Arc::new(LspResolver::from_config(&config, &cache, None).await); let mut documents = Documents::default(); documents.update_config(&config, &resolver, &cache, &Default::default()); (documents, cache, temp_dir) @@ -1680,9 +1679,8 @@ console.log(b, "hello deno"); ) .await; - let resolver = LspResolver::default() - .with_new_config(&config, &cache, None) - .await; + let resolver = + Arc::new(LspResolver::from_config(&config, &cache, None).await); documents.update_config(&config, &resolver, &cache, &workspace_files); // open the document @@ -1725,9 +1723,8 @@ console.log(b, "hello deno"); ) .await; - let resolver = LspResolver::default() - .with_new_config(&config, &cache, None) - .await; + let resolver = + Arc::new(LspResolver::from_config(&config, &cache, None).await); documents.update_config(&config, &resolver, &cache, &workspace_files); // check the document's dependencies diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 8f37d8b9ca5434..884144e89fbc41 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -115,10 +115,17 @@ impl RootCertStoreProvider for LspRootCertStoreProvider { } #[derive(Debug, Clone)] -pub struct LanguageServer( - pub Arc>, - CancellationToken, -); +pub struct LanguageServer { + pub inner: Arc>, + /// 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_token: CancellationToken, + shutdown_token: CancellationToken, +} /// Snapshot of the state used by TSC. #[derive(Clone, Debug, Default)] @@ -209,11 +216,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_token: CancellationToken) -> Self { + Self { + inner: Arc::new(tokio::sync::RwLock::new(Inner::new(client))), + init_token: Default::default(), + shutdown_token, + } } /// Similar to `deno cache` on the command line, where modules will be cached @@ -224,6 +232,9 @@ impl LanguageServer { referrer: ModuleSpecifier, force_global_cache: bool, ) -> LspResult> { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } async fn create_graph_for_caching( cli_options: CliOptions, roots: Vec, @@ -270,13 +281,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 @@ -296,7 +307,7 @@ impl LanguageServer { if let Err(err) = handle.await.unwrap() { lsp_warn!("Error caching: {:#}", err); self - .0 + .inner .read() .await .client @@ -304,7 +315,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(); @@ -325,9 +336,12 @@ impl LanguageServer { pub async fn latest_diagnostic_batch_index_request( &self, ) -> LspResult> { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } Ok( self - .0 + .inner .read() .await .diagnostics_server @@ -337,18 +351,27 @@ impl LanguageServer { } pub async fn performance_request(&self) -> LspResult> { - Ok(Some(self.0.read().await.get_performance())) + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + Ok(Some(self.inner.read().await.get_performance())) } pub async fn task_definitions(&self) -> LspResult> { - self.0.read().await.task_definitions() + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.task_definitions() } pub async fn test_run_request( &self, params: Option, ) -> LspResult> { - let inner = self.0.read().await; + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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)) => { @@ -368,7 +391,11 @@ impl LanguageServer { &self, params: Option, ) -> LspResult> { - if let Some(testing_server) = &self.0.read().await.maybe_testing_server { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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())), @@ -383,10 +410,13 @@ impl LanguageServer { &self, params: Option, ) -> LspResult> { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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!( @@ -403,11 +433,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 { @@ -431,8 +461,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); } } } @@ -749,10 +781,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 @@ -945,10 +973,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) { @@ -966,10 +998,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" { @@ -1147,7 +1175,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 { @@ -1176,15 +1214,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); } @@ -1252,9 +1281,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. @@ -2810,6 +2838,9 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: ExecuteCommandParams, ) -> LspResult> { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } if params.command == "deno.cache" { #[derive(Default, Deserialize)] #[serde(rename_all = "camelCase")] @@ -2826,7 +2857,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) } @@ -2836,14 +2867,25 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: InitializeParams, ) -> LspResult { - 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_token.cancel(); + if inner .config .client_capabilities .workspace_did_change_watched_files @@ -2865,7 +2907,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()), @@ -2883,17 +2925,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(), @@ -2912,14 +2954,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 { @@ -2932,18 +2976,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 @@ -2970,38 +3002,53 @@ impl tower_lsp::LanguageServer for LanguageServer { } async fn shutdown(&self) -> LspResult<()> { - self.1.cancel(); - self.0.write().await.shutdown() + self.shutdown_token.cancel(); + Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { - self.0.write().await.did_open(params).await; + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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_token.is_cancelled() { + self.init_token.cancelled().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_token.is_cancelled() { + self.init_token.cancelled().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_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.write().await.did_close(params).await } async fn did_change_configuration( &self, params: DidChangeConfigurationParams, ) { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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 = @@ -3033,15 +3080,26 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DidChangeWatchedFilesParams, ) { - self.0.write().await.did_change_watched_files(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self + .inner + .write() + .await + .did_change_watched_files(params) + .await } async fn did_change_workspace_folders( &self, params: DidChangeWorkspaceFoldersParams, ) { + if !self.init_token.is_cancelled() { + self.init_token.cancelled().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); @@ -3068,7 +3126,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; @@ -3083,176 +3141,254 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DocumentSymbolParams, ) -> LspResult> { - self.0.read().await.document_symbol(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.document_symbol(params).await } async fn formatting( &self, params: DocumentFormattingParams, ) -> LspResult>> { - self.0.read().await.formatting(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.formatting(params).await } async fn hover(&self, params: HoverParams) -> LspResult> { - self.0.read().await.hover(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.hover(params).await } async fn inlay_hint( &self, params: InlayHintParams, ) -> LspResult>> { - self.0.read().await.inlay_hint(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.inlay_hint(params).await } async fn code_action( &self, params: CodeActionParams, ) -> LspResult> { - self.0.read().await.code_action(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.code_action(params).await } async fn code_action_resolve( &self, params: CodeAction, ) -> LspResult { - self.0.read().await.code_action_resolve(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.code_action_resolve(params).await } async fn code_lens( &self, params: CodeLensParams, ) -> LspResult>> { - self.0.read().await.code_lens(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.code_lens(params).await } async fn code_lens_resolve(&self, params: CodeLens) -> LspResult { - self.0.read().await.code_lens_resolve(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.code_lens_resolve(params).await } async fn document_highlight( &self, params: DocumentHighlightParams, ) -> LspResult>> { - self.0.read().await.document_highlight(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.document_highlight(params).await } async fn references( &self, params: ReferenceParams, ) -> LspResult>> { - self.0.read().await.references(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.references(params).await } async fn goto_definition( &self, params: GotoDefinitionParams, ) -> LspResult> { - self.0.read().await.goto_definition(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.goto_definition(params).await } async fn goto_type_definition( &self, params: GotoTypeDefinitionParams, ) -> LspResult> { - self.0.read().await.goto_type_definition(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.goto_type_definition(params).await } async fn completion( &self, params: CompletionParams, ) -> LspResult> { - self.0.read().await.completion(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.completion(params).await } async fn completion_resolve( &self, params: CompletionItem, ) -> LspResult { - self.0.read().await.completion_resolve(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.completion_resolve(params).await } async fn goto_implementation( &self, params: GotoImplementationParams, ) -> LspResult> { - self.0.read().await.goto_implementation(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.goto_implementation(params).await } async fn folding_range( &self, params: FoldingRangeParams, ) -> LspResult>> { - self.0.read().await.folding_range(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.folding_range(params).await } async fn incoming_calls( &self, params: CallHierarchyIncomingCallsParams, ) -> LspResult>> { - self.0.read().await.incoming_calls(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.incoming_calls(params).await } async fn outgoing_calls( &self, params: CallHierarchyOutgoingCallsParams, ) -> LspResult>> { - self.0.read().await.outgoing_calls(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.outgoing_calls(params).await } async fn prepare_call_hierarchy( &self, params: CallHierarchyPrepareParams, ) -> LspResult>> { - self.0.read().await.prepare_call_hierarchy(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.prepare_call_hierarchy(params).await } async fn rename( &self, params: RenameParams, ) -> LspResult> { - self.0.read().await.rename(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.rename(params).await } async fn selection_range( &self, params: SelectionRangeParams, ) -> LspResult>> { - self.0.read().await.selection_range(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.selection_range(params).await } async fn semantic_tokens_full( &self, params: SemanticTokensParams, ) -> LspResult> { - self.0.read().await.semantic_tokens_full(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.semantic_tokens_full(params).await } async fn semantic_tokens_range( &self, params: SemanticTokensRangeParams, ) -> LspResult> { - self.0.read().await.semantic_tokens_range(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.semantic_tokens_range(params).await } async fn signature_help( &self, params: SignatureHelpParams, ) -> LspResult> { - self.0.read().await.signature_help(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.signature_help(params).await } async fn will_rename_files( &self, params: RenameFilesParams, ) -> LspResult> { - self.0.read().await.will_rename_files(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.will_rename_files(params).await } async fn symbol( &self, params: WorkspaceSymbolParams, ) -> LspResult>> { - self.0.read().await.symbol(params).await + if !self.init_token.is_cancelled() { + self.init_token.cancelled().await; + } + self.inner.read().await.symbol(params).await } } diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 0567bba868155d..01b8cd0a3de1e2 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -3,7 +3,6 @@ use crate::args::create_default_npmrc; use crate::args::package_json; use crate::args::CacheSetting; -use crate::cache::FastInsecureHasher; use crate::graph_util::CliJsrUrlProvider; use crate::http_util::HttpClient; use crate::jsr::JsrCacheResolver; @@ -60,7 +59,6 @@ pub struct LspResolver { jsr_resolver: Option>, npm_resolver: Option>, node_resolver: Option>, - npm_config_hash: LspNpmConfigHash, redirect_resolver: Option>, graph_imports: Arc>, config: Arc, @@ -73,7 +71,6 @@ impl Default for LspResolver { jsr_resolver: None, npm_resolver: None, node_resolver: None, - npm_config_hash: LspNpmConfigHash(0), redirect_resolver: None, graph_imports: Default::default(), config: Default::default(), @@ -82,26 +79,17 @@ impl Default for LspResolver { } impl LspResolver { - pub async fn with_new_config( - &self, + pub async fn from_config( config: &Config, cache: &LspCache, http_client: Option<&Arc>, - ) -> Arc { - let npm_config_hash = LspNpmConfigHash::new(config, cache); + ) -> Self { let config_data = config.tree.root_data(); let mut npm_resolver = None; let mut node_resolver = None; - if npm_config_hash != self.npm_config_hash { - if let (Some(http_client), Some(config_data)) = (http_client, config_data) - { - npm_resolver = - create_npm_resolver(config_data, cache, http_client).await; - node_resolver = create_node_resolver(npm_resolver.as_ref()); - } - } else { - npm_resolver = self.npm_resolver.clone(); - node_resolver = self.node_resolver.clone(); + if let (Some(http_client), Some(config_data)) = (http_client, config_data) { + npm_resolver = create_npm_resolver(config_data, cache, http_client).await; + node_resolver = create_node_resolver(npm_resolver.as_ref()); } let graph_resolver = create_graph_resolver( config_data, @@ -136,16 +124,15 @@ impl LspResolver { ) }) .unwrap_or_default(); - Arc::new(Self { + Self { graph_resolver, jsr_resolver, npm_resolver, node_resolver, - npm_config_hash, redirect_resolver, graph_imports, config: Arc::new(config.clone()), - }) + } } pub fn snapshot(&self) -> Arc { @@ -162,7 +149,6 @@ impl LspResolver { jsr_resolver: self.jsr_resolver.clone(), npm_resolver, node_resolver, - npm_config_hash: self.npm_config_hash.clone(), redirect_resolver: self.redirect_resolver.clone(), graph_imports: self.graph_imports.clone(), config: self.config.clone(), @@ -177,8 +163,11 @@ impl LspResolver { &self, reqs: &[PackageReq], ) -> Result<(), AnyError> { + dbg!(reqs.iter().map(|r| r.to_string()).collect::>()); if let Some(npm_resolver) = self.npm_resolver.as_ref() { + dbg!(); if let Some(npm_resolver) = npm_resolver.as_managed() { + dbg!(); return npm_resolver.set_package_reqs(reqs).await; } } @@ -336,7 +325,7 @@ async fn create_npm_resolver( let options = if config_data.byonm { CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { fs: Arc::new(deno_fs::RealFs), - root_node_modules_dir: node_modules_dir, + root_node_modules_dir: node_modules_dir.clone(), }) } else { CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { @@ -422,27 +411,6 @@ fn create_graph_resolver( })) } -#[derive(Debug, Clone, PartialEq, Eq)] -struct LspNpmConfigHash(u64); - -impl LspNpmConfigHash { - pub fn new(config: &Config, cache: &LspCache) -> Self { - let config_data = config.tree.root_data(); - let scope = config_data.map(|d| &d.scope); - let node_modules_dir = - config_data.and_then(|d| d.node_modules_dir.as_ref()); - let lockfile = config_data.and_then(|d| d.lockfile.as_ref()); - let mut hasher = FastInsecureHasher::new(); - hasher.write_hashable(scope); - hasher.write_hashable(node_modules_dir); - if let Some(lockfile) = lockfile { - hasher.write_hashable(&*lockfile.lock()); - } - hasher.write_hashable(cache.deno_dir().npm_folder_path()); - Self(hasher.finish()) - } -} - #[derive(Debug, Eq, PartialEq)] struct RedirectEntry { headers: Arc>, diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 9215be0742eb0f..ebd3fc973d12d0 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -5184,9 +5184,8 @@ mod tests { .unwrap(), ) .await; - let resolver = LspResolver::default() - .with_new_config(&config, &cache, None) - .await; + let resolver = + Arc::new(LspResolver::from_config(&config, &cache, None).await); let mut documents = Documents::default(); documents.update_config(&config, &resolver, &cache, &Default::default()); for (specifier, source, version, language_id) in sources { diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 33943d83ea1a33..09882a666df60d 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -933,27 +933,30 @@ fn lsp_workspace_enable_paths_no_workspace_configuration() { fn lsp_did_change_deno_configuration_notification() { let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); + temp_dir.write("deno.json", json!({}).to_string()); + temp_dir.write("package.json", json!({}).to_string()); let mut client = context.new_lsp_command().build(); client.initialize_default(); - temp_dir.write("deno.json", json!({}).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.json").unwrap(), - "type": 1, - }], - })); let res = client .read_notification_with_method::("deno/didChangeDenoConfiguration"); assert_eq!( res, Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("deno.json").unwrap(), - "type": "added", - "configurationType": "denoJson" - }], + "changes": [ + { + "scopeUri": temp_dir.uri(), + "fileUri": temp_dir.uri().join("deno.json").unwrap(), + "type": "added", + "configurationType": "denoJson" + }, + { + "scopeUri": temp_dir.uri(), + "fileUri": temp_dir.uri().join("package.json").unwrap(), + "type": "added", + "configurationType": "packageJson" + }, + ], })) ); @@ -1002,27 +1005,6 @@ fn lsp_did_change_deno_configuration_notification() { })) ); - temp_dir.write("package.json", json!({}).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("package.json").unwrap(), - "type": 1, - }], - })); - let res = client - .read_notification_with_method::("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("package.json").unwrap(), - "type": "added", - "configurationType": "packageJson" - }], - })) - ); - temp_dir.write("package.json", json!({ "type": "module" }).to_string()); client.did_change_watched_files(json!({ "changes": [{ @@ -9691,26 +9673,17 @@ fn lsp_format_exclude_default_config() { #[test] fn lsp_format_json() { let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir_path = context.temp_dir().path(); - // Also test out using a non-json file extension here. - // What should matter is the language identifier. - let lock_file_path = temp_dir_path.join("file.lock"); + let temp_dir = context.temp_dir(); + let json_file = + source_file(temp_dir.path().join("file.json"), "{\"key\":\"value\"}"); let mut client = context.new_lsp_command().build(); client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": lock_file_path.uri_file(), - "languageId": "json", - "version": 1, - "text": "{\"key\":\"value\"}" - } - })); let res = client.write_request( "textDocument/formatting", json!({ "textDocument": { - "uri": lock_file_path.uri_file(), + "uri": json_file.uri(), }, "options": { "tabSize": 2, @@ -9751,7 +9724,7 @@ fn lsp_json_no_diagnostics() { let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); - client.did_open(json!({ + client.did_open_raw(json!({ "textDocument": { "uri": "file:///a/file.json", "languageId": "json", @@ -9798,7 +9771,7 @@ fn lsp_json_import_with_query_string() { ); let mut client = context.new_lsp_command().build(); client.initialize_default(); - client.did_open(json!({ + client.did_open_raw(json!({ "textDocument": { "uri": temp_dir.uri().join("data.json").unwrap(), "languageId": "json", @@ -9821,23 +9794,17 @@ fn lsp_json_import_with_query_string() { #[test] fn lsp_format_markdown() { let context = TestContextBuilder::new().use_temp_cwd().build(); - let markdown_file = context.temp_dir().path().join("file.md"); + let temp_dir = context.temp_dir(); + let markdown_file = + source_file(temp_dir.path().join("file.md"), "# Hello World"); let mut client = context.new_lsp_command().build(); client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": markdown_file.uri_file(), - "languageId": "markdown", - "version": 1, - "text": "# Hello World" - } - })); let res = client.write_request( "textDocument/formatting", json!({ "textDocument": { - "uri": markdown_file.uri_file() + "uri": markdown_file.uri() }, "options": { "tabSize": 2, @@ -9980,7 +9947,7 @@ fn lsp_markdown_no_diagnostics() { let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); - client.did_open(json!({ + client.did_open_raw(json!({ "textDocument": { "uri": "file:///a/file.md", "languageId": "markdown", diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 7c48bae23edcc6..f210356105a456 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -1292,6 +1292,7 @@ impl SourceFile { "js" => "javascript", "ts" | "d.ts" => "typescript", "json" => "json", + "md" => "markdown", other => panic!("unsupported file extension: {other}"), }; Self { From 9f2f247fb26d173ee15db3f0396dec41e2d44452 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Tue, 28 May 2024 00:39:53 +0100 Subject: [PATCH 2/4] remove dbg --- cli/lsp/resolver.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 01b8cd0a3de1e2..5c6708c79b6592 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -163,11 +163,8 @@ impl LspResolver { &self, reqs: &[PackageReq], ) -> Result<(), AnyError> { - dbg!(reqs.iter().map(|r| r.to_string()).collect::>()); if let Some(npm_resolver) = self.npm_resolver.as_ref() { - dbg!(); if let Some(npm_resolver) = npm_resolver.as_managed() { - dbg!(); return npm_resolver.set_package_reqs(reqs).await; } } From 37b2854794bb09d0230b6735e542bc44e30aa0cf Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Tue, 28 May 2024 02:38:05 +0100 Subject: [PATCH 3/4] AsyncFlag --- cli/lsp/language_server.rs | 193 ++++++++++++++++++++----------------- 1 file changed, 105 insertions(+), 88 deletions(-) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 884144e89fbc41..e0691fdf8882f2 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -123,8 +123,8 @@ pub struct LanguageServer { /// 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_token: CancellationToken, - shutdown_token: CancellationToken, + init_flag: AsyncFlag, + shutdown_flag: AsyncFlag, } /// Snapshot of the state used by TSC. @@ -137,6 +137,23 @@ pub struct StateSnapshot { pub resolver: Arc, } +#[derive(Debug, Default, Clone)] +struct AsyncFlag(CancellationToken); + +impl AsyncFlag { + fn raise(&self) { + self.0.cancel(); + } + + fn is_raised(&self) -> bool { + self.0.is_cancelled() + } + + fn wait_raised(&self) -> impl std::future::Future + '_ { + self.0.cancelled() + } +} + type LanguageServerTaskFn = Box; /// Used to queue tasks from inside of the language server lock that must be @@ -219,8 +236,8 @@ impl LanguageServer { pub fn new(client: Client, shutdown_token: CancellationToken) -> Self { Self { inner: Arc::new(tokio::sync::RwLock::new(Inner::new(client))), - init_token: Default::default(), - shutdown_token, + init_flag: Default::default(), + shutdown_flag: AsyncFlag(shutdown_token), } } @@ -232,8 +249,8 @@ impl LanguageServer { referrer: ModuleSpecifier, force_global_cache: bool, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } async fn create_graph_for_caching( cli_options: CliOptions, @@ -336,8 +353,8 @@ impl LanguageServer { pub async fn latest_diagnostic_batch_index_request( &self, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } Ok( self @@ -351,15 +368,15 @@ impl LanguageServer { } pub async fn performance_request(&self) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + 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> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.task_definitions() } @@ -368,8 +385,8 @@ impl LanguageServer { &self, params: Option, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().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 { @@ -391,8 +408,8 @@ impl LanguageServer { &self, params: Option, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } if let Some(testing_server) = &self.inner.read().await.maybe_testing_server { @@ -410,8 +427,8 @@ impl LanguageServer { &self, params: Option, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } match params.map(serde_json::from_value) { Some(Ok(params)) => Ok(Some( @@ -2838,8 +2855,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: ExecuteCommandParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } if params.command == "deno.cache" { #[derive(Default, Deserialize)] @@ -2884,7 +2901,7 @@ impl tower_lsp::LanguageServer for LanguageServer { inner.refresh_resolver().await; inner.refresh_documents_config().await; inner.task_queue.start(self.clone()); - self.init_token.cancel(); + self.init_flag.raise(); if inner .config .client_capabilities @@ -3002,34 +3019,34 @@ impl tower_lsp::LanguageServer for LanguageServer { } async fn shutdown(&self) -> LspResult<()> { - self.shutdown_token.cancel(); + self.shutdown_flag.raise(); Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().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) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().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) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + 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) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.write().await.did_close(params).await } @@ -3038,8 +3055,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DidChangeConfigurationParams, ) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } let mark = { let inner = self.inner.read().await; @@ -3080,8 +3097,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DidChangeWatchedFilesParams, ) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self .inner @@ -3095,8 +3112,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DidChangeWorkspaceFoldersParams, ) { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } let mark = { let mut inner = self.inner.write().await; @@ -3141,8 +3158,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DocumentSymbolParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.document_symbol(params).await } @@ -3151,15 +3168,15 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DocumentFormattingParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().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> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.hover(params).await } @@ -3168,8 +3185,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: InlayHintParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.inlay_hint(params).await } @@ -3178,8 +3195,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CodeActionParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.code_action(params).await } @@ -3188,8 +3205,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CodeAction, ) -> LspResult { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.code_action_resolve(params).await } @@ -3198,15 +3215,15 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CodeLensParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().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 { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.code_lens_resolve(params).await } @@ -3215,8 +3232,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: DocumentHighlightParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.document_highlight(params).await } @@ -3225,8 +3242,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: ReferenceParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.references(params).await } @@ -3235,8 +3252,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: GotoDefinitionParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.goto_definition(params).await } @@ -3245,8 +3262,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: GotoTypeDefinitionParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.goto_type_definition(params).await } @@ -3255,8 +3272,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CompletionParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.completion(params).await } @@ -3265,8 +3282,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CompletionItem, ) -> LspResult { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.completion_resolve(params).await } @@ -3275,8 +3292,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: GotoImplementationParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.goto_implementation(params).await } @@ -3285,8 +3302,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: FoldingRangeParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.folding_range(params).await } @@ -3295,8 +3312,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CallHierarchyIncomingCallsParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.incoming_calls(params).await } @@ -3305,8 +3322,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CallHierarchyOutgoingCallsParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.outgoing_calls(params).await } @@ -3315,8 +3332,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: CallHierarchyPrepareParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.prepare_call_hierarchy(params).await } @@ -3325,8 +3342,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: RenameParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.rename(params).await } @@ -3335,8 +3352,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: SelectionRangeParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.selection_range(params).await } @@ -3345,8 +3362,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: SemanticTokensParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.semantic_tokens_full(params).await } @@ -3355,8 +3372,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: SemanticTokensRangeParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.semantic_tokens_range(params).await } @@ -3365,8 +3382,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: SignatureHelpParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.signature_help(params).await } @@ -3375,8 +3392,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: RenameFilesParams, ) -> LspResult> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.will_rename_files(params).await } @@ -3385,8 +3402,8 @@ impl tower_lsp::LanguageServer for LanguageServer { &self, params: WorkspaceSymbolParams, ) -> LspResult>> { - if !self.init_token.is_cancelled() { - self.init_token.cancelled().await; + if !self.init_flag.is_raised() { + self.init_flag.wait_raised().await; } self.inner.read().await.symbol(params).await } From b0fcb2777d6de0ad7ecd4a079a3eb5619ccc98ce Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 29 May 2024 00:29:00 +0100 Subject: [PATCH 4/4] move AsyncFlag to cli/util/sync.rs --- cli/lsp/language_server.rs | 23 +++-------------------- cli/lsp/mod.rs | 10 +++++----- cli/util/sync.rs | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index f5f1963f792b37..9abefb141b95a1 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); @@ -138,23 +138,6 @@ pub struct StateSnapshot { pub resolver: Arc, } -#[derive(Debug, Default, Clone)] -struct AsyncFlag(CancellationToken); - -impl AsyncFlag { - fn raise(&self) { - self.0.cancel(); - } - - fn is_raised(&self) -> bool { - self.0.is_cancelled() - } - - fn wait_raised(&self) -> impl std::future::Future + '_ { - self.0.cancelled() - } -} - type LanguageServerTaskFn = Box; /// Used to queue tasks from inside of the language server lock that must be @@ -234,11 +217,11 @@ pub struct Inner { } impl LanguageServer { - pub fn new(client: Client, shutdown_token: CancellationToken) -> Self { + 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: AsyncFlag(shutdown_token), + shutdown_flag, } } diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs index 07d829d9192d7d..79aa4d8f074d14 100644 --- a/cli/lsp/mod.rs +++ b/cli/lsp/mod.rs @@ -2,11 +2,11 @@ use deno_core::error::AnyError; use deno_core::unsync::spawn; -use tokio_util::sync::CancellationToken; use tower_lsp::LspService; use tower_lsp::Server; use crate::lsp::language_server::LanguageServer; +use crate::util::sync::AsyncFlag; pub use repl::ReplCompletionItem; pub use repl::ReplLanguageServer; @@ -44,11 +44,11 @@ pub async fn start() -> Result<(), AnyError> { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let token = CancellationToken::new(); + let shutdown_flag = AsyncFlag::default(); let builder = LspService::build(|client| { language_server::LanguageServer::new( client::Client::from_tower(client), - token.clone(), + shutdown_flag.clone(), ) }) .custom_method( @@ -80,7 +80,7 @@ pub async fn start() -> Result<(), AnyError> { let (service, socket) = builder.finish(); - // TODO(nayeemrmn): This cancellation token is a workaround for + // TODO(nayeemrmn): This shutdown flag is a workaround for // https://github.com/denoland/deno/issues/20700. Remove when // https://github.com/ebkalderon/tower-lsp/issues/399 is fixed. // Force end the server 8 seconds after receiving a shutdown request. @@ -88,7 +88,7 @@ pub async fn start() -> Result<(), AnyError> { biased; _ = Server::new(stdin, stdout, socket).serve(service) => {} _ = spawn(async move { - token.cancelled().await; + shutdown_flag.wait_raised().await; tokio::time::sleep(std::time::Duration::from_secs(8)).await; }) => {} } diff --git a/cli/util/sync.rs b/cli/util/sync.rs index dddb5991cefe1e..3986c883e37f8f 100644 --- a/cli/util/sync.rs +++ b/cli/util/sync.rs @@ -8,6 +8,7 @@ use std::sync::Arc; use deno_core::futures::task::AtomicWaker; use deno_core::futures::Future; use deno_core::parking_lot::Mutex; +use tokio_util::sync::CancellationToken; /// Simplifies the use of an atomic boolean as a flag. #[derive(Debug, Default)] @@ -160,6 +161,23 @@ impl<'a> Future for TaskQueuePermitAcquireFuture<'a> { } } +#[derive(Debug, Default, Clone)] +pub struct AsyncFlag(CancellationToken); + +impl AsyncFlag { + pub fn raise(&self) { + self.0.cancel(); + } + + pub fn is_raised(&self) -> bool { + self.0.is_cancelled() + } + + pub fn wait_raised(&self) -> impl std::future::Future + '_ { + self.0.cancelled() + } +} + #[cfg(test)] mod test { use deno_core::futures;