0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-14 17:47:35 -05:00

perf(lsp): cancellation checks in blocking code (#27997)

This commit is contained in:
Nayeem Rahman 2025-02-10 10:52:31 +00:00 committed by GitHub
parent e94581d272
commit 7d139ddd60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1338 additions and 448 deletions

View file

@ -9,6 +9,7 @@ use std::path::Path;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use deno_ast::SourceTextInfo;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
@ -620,9 +621,13 @@ fn try_reverse_map_package_json_exports(
pub fn fix_ts_import_changes(
changes: &[tsc::FileTextChanges],
language_server: &language_server::Inner,
token: &CancellationToken,
) -> Result<Vec<tsc::FileTextChanges>, AnyError> {
let mut r = Vec::new();
for change in changes {
if token.is_cancelled() {
return Err(anyhow!("request cancelled"));
}
let Ok(referrer) = ModuleSpecifier::parse(&change.file_name) else {
continue;
};

View file

@ -11,6 +11,7 @@ use deno_ast::swc::visit::VisitWith;
use deno_ast::ParsedSource;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::resolve_url;
use deno_core::serde::Deserialize;
@ -21,7 +22,7 @@ use deno_core::ModuleSpecifier;
use lazy_regex::lazy_regex;
use once_cell::sync::Lazy;
use regex::Regex;
use tower_lsp::jsonrpc::Error as LspError;
use tokio_util::sync::CancellationToken;
use tower_lsp::lsp_types as lsp;
use super::analysis::source_range_to_lsp_range;
@ -30,7 +31,6 @@ use super::language_server;
use super::text::LineIndex;
use super::tsc;
use super::tsc::NavigationTree;
use crate::lsp::logging::lsp_warn;
static ABSTRACT_MODIFIER: Lazy<Regex> = lazy_regex!(r"\babstract\b");
@ -253,6 +253,7 @@ async fn resolve_implementation_code_lens(
code_lens: lsp::CodeLens,
data: CodeLensData,
language_server: &language_server::Inner,
token: &CancellationToken,
) -> Result<lsp::CodeLens, AnyError> {
let asset_or_doc = language_server.get_asset_or_document(&data.specifier)?;
let line_index = asset_or_doc.line_index();
@ -262,15 +263,25 @@ async fn resolve_implementation_code_lens(
language_server.snapshot(),
data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?,
token,
)
.await
.map_err(|err| {
lsp_warn!("{err}");
LspError::internal_error()
if token.is_cancelled() {
anyhow!("request cancelled")
} else {
anyhow!(
"Unable to get implementation locations from TypeScript: {:#}",
err
)
}
})?;
if let Some(implementations) = maybe_implementations {
let mut locations = Vec::new();
for implementation in implementations {
if token.is_cancelled() {
break;
}
let implementation_specifier =
resolve_url(&implementation.document_span.file_name)?;
let implementation_location =
@ -326,10 +337,12 @@ async fn resolve_references_code_lens(
code_lens: lsp::CodeLens,
data: CodeLensData,
language_server: &language_server::Inner,
token: &CancellationToken,
) -> Result<lsp::CodeLens, AnyError> {
fn get_locations(
maybe_referenced_symbols: Option<Vec<tsc::ReferencedSymbol>>,
language_server: &language_server::Inner,
token: &CancellationToken,
) -> Result<Vec<lsp::Location>, AnyError> {
let symbols = match maybe_referenced_symbols {
Some(symbols) => symbols,
@ -337,6 +350,9 @@ async fn resolve_references_code_lens(
};
let mut locations = Vec::new();
for reference in symbols.iter().flat_map(|s| &s.references) {
if token.is_cancelled() {
break;
}
if reference.is_definition {
continue;
}
@ -363,13 +379,18 @@ async fn resolve_references_code_lens(
language_server.snapshot(),
data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?,
token,
)
.await
.map_err(|err| {
lsp_warn!("Unable to find references: {err}");
LspError::internal_error()
if token.is_cancelled() {
anyhow!("request cancelled")
} else {
anyhow!("Unable to get references from TypeScript: {:#}", err)
}
})?;
let locations = get_locations(maybe_referenced_symbols, language_server)?;
let locations =
get_locations(maybe_referenced_symbols, language_server, token)?;
let title = if locations.len() == 1 {
"1 reference".to_string()
} else {
@ -402,15 +423,18 @@ async fn resolve_references_code_lens(
pub async fn resolve_code_lens(
code_lens: lsp::CodeLens,
language_server: &language_server::Inner,
token: &CancellationToken,
) -> Result<lsp::CodeLens, AnyError> {
let data: CodeLensData =
serde_json::from_value(code_lens.data.clone().unwrap())?;
match data.source {
CodeLensSource::Implementations => {
resolve_implementation_code_lens(code_lens, data, language_server).await
resolve_implementation_code_lens(code_lens, data, language_server, token)
.await
}
CodeLensSource::References => {
resolve_references_code_lens(code_lens, data, language_server).await
resolve_references_code_lens(code_lens, data, language_server, token)
.await
}
}
}
@ -418,7 +442,9 @@ pub async fn resolve_code_lens(
pub fn collect_test(
specifier: &ModuleSpecifier,
parsed_source: &ParsedSource,
_token: &CancellationToken,
) -> Result<Vec<lsp::CodeLens>, AnyError> {
// TODO(nayeemrmn): Do cancellation checks while collecting tests.
let mut collector =
DenoTestCollector::new(specifier.clone(), parsed_source.clone());
parsed_source.program().visit_with(&mut collector);
@ -431,9 +457,10 @@ pub fn collect_tsc(
code_lens_settings: &CodeLensSettings,
line_index: Arc<LineIndex>,
navigation_tree: &NavigationTree,
token: &CancellationToken,
) -> Result<Vec<lsp::CodeLens>, AnyError> {
let code_lenses = Rc::new(RefCell::new(Vec::new()));
navigation_tree.walk(&|i, mp| {
navigation_tree.walk(token, &|i, mp| {
let mut code_lenses = code_lenses.borrow_mut();
// TSC Implementations Code Lens
@ -541,7 +568,7 @@ pub fn collect_tsc(
_ => (),
}
}
});
})?;
Ok(Rc::try_unwrap(code_lenses).unwrap().into_inner())
}

View file

@ -1087,7 +1087,7 @@ async fn generate_ts_diagnostics(
let (ts_diagnostics_map, ambient_modules_by_scope) =
if !enabled_specifiers.is_empty() {
ts_server
.get_diagnostics(snapshot.clone(), enabled_specifiers, token)
.get_diagnostics(snapshot.clone(), enabled_specifiers, &token)
.await?
} else {
Default::default()

File diff suppressed because it is too large Load diff

View file

@ -82,7 +82,7 @@ pub async fn start() -> Result<(), AnyError> {
// Force end the server 8 seconds after receiving a shutdown request.
tokio::select! {
biased;
_ = Server::new(stdin, stdout, socket).serve(service) => {}
_ = Server::new(stdin, stdout, socket).concurrency_level(32).serve(service) => {}
_ = spawn(async move {
shutdown_flag.wait_raised().await;
tokio::time::sleep(std::time::Duration::from_secs(8)).await;

File diff suppressed because it is too large Load diff

View file

@ -209,6 +209,7 @@ fn unadded_dependency_message_with_import_map() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open(json!({
"textDocument": {
@ -268,6 +269,7 @@ fn unadded_dependency_message() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open(json!({
"textDocument": {
@ -381,6 +383,7 @@ fn lsp_import_map_remote() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open(json!({
"textDocument": {
@ -639,6 +642,7 @@ fn lsp_import_map_config_file_auto_discovered() {
client.wait_until_stderr_line(|line| {
line.contains(" Resolved Deno configuration file:")
});
client.read_diagnostics();
let uri = temp_dir.url().join("a.ts").unwrap();
@ -700,6 +704,7 @@ fn lsp_import_map_config_file_auto_discovered() {
client.wait_until_stderr_line(|line| {
line.contains(" Resolved Deno configuration file:")
});
assert_eq!(json!(client.read_diagnostics().all()), json!([]));
let res = client.write_request(
"textDocument/hover",
json!({
@ -725,7 +730,6 @@ fn lsp_import_map_config_file_auto_discovered() {
}
})
);
assert_eq!(client.read_diagnostics().all().len(), 0);
client.shutdown();
}
@ -761,6 +765,7 @@ fn lsp_import_map_config_file_auto_discovered_symlink() {
"type": 2
}]
}));
client.read_diagnostics();
// this will discover the deno.json in the root
let search_line = format!(
@ -826,6 +831,7 @@ fn lsp_deno_json_imports_comments_cache() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open(json!({
"textDocument": {
@ -917,6 +923,7 @@ fn lsp_format_vendor_path() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
assert!(temp_dir
.path()
.join("vendor/http_localhost_4545/run/002_hello.ts")
@ -1114,6 +1121,7 @@ fn lsp_did_refresh_deno_configuration_tree_notification() {
"type": 1,
}],
}));
client.read_diagnostics();
let res = client
.read_notification_with_method::<Value>(
"deno/didRefreshDenoConfigurationTree",
@ -1263,6 +1271,7 @@ fn lsp_did_change_deno_configuration_notification() {
"type": 2,
}],
}));
client.read_diagnostics();
let res = client
.read_notification_with_method::<Value>("deno/didChangeDenoConfiguration");
assert_eq!(
@ -1284,6 +1293,7 @@ fn lsp_did_change_deno_configuration_notification() {
"type": 3,
}],
}));
client.read_diagnostics();
let res = client
.read_notification_with_method::<Value>("deno/didChangeDenoConfiguration");
assert_eq!(
@ -1305,6 +1315,7 @@ fn lsp_did_change_deno_configuration_notification() {
"type": 2,
}],
}));
client.read_diagnostics();
let res = client
.read_notification_with_method::<Value>("deno/didChangeDenoConfiguration");
assert_eq!(
@ -1326,6 +1337,7 @@ fn lsp_did_change_deno_configuration_notification() {
"type": 3,
}],
}));
client.read_diagnostics();
let res = client
.read_notification_with_method::<Value>("deno/didChangeDenoConfiguration");
assert_eq!(
@ -1409,7 +1421,7 @@ fn lsp_import_attributes() {
},
}));
client.did_open(json!({
client.did_open_raw(json!({
"textDocument": {
"uri": "file:///a/test.json",
"languageId": "json",
@ -2086,7 +2098,6 @@ fn lsp_suggestion_actions_disabled() {
},
},
}));
client.read_diagnostics();
let diagnostics = client.did_open(json!({
"textDocument": {
"uri": temp_dir.url().join("file.ts").unwrap(),
@ -2778,6 +2789,7 @@ fn lsp_hover_dependency() {
"arguments": [[], "file:///a/file.ts"],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -3162,6 +3174,7 @@ fn lsp_hover_typescript_types() {
],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -3211,6 +3224,7 @@ fn lsp_hover_jsr() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -5733,6 +5747,7 @@ fn lsp_jsr_auto_import_completion() {
],
}),
);
client.read_diagnostics();
client.did_open(json!({
"textDocument": {
"uri": temp_dir.url().join("file.ts").unwrap(),
@ -5812,6 +5827,7 @@ fn lsp_jsr_auto_import_completion_import_map() {
],
}),
);
client.read_diagnostics();
client.did_open(json!({
"textDocument": {
"uri": temp_dir.url().join("file.ts").unwrap(),
@ -5951,6 +5967,7 @@ fn lsp_jsr_code_action_missing_declaration() {
"arguments": [[], file.url()],
}),
);
client.read_diagnostics();
client.did_open_file(&file);
let res = client.write_request(
"textDocument/codeAction",
@ -6071,6 +6088,7 @@ fn lsp_jsr_code_action_move_to_new_file() {
"arguments": [[], file.url()],
}),
);
client.read_diagnostics();
client.did_open_file(&file);
let list = client
.write_request_with_res_as::<Option<lsp::CodeActionResponse>>(
@ -6490,6 +6508,7 @@ fn lsp_code_actions_deno_types_for_npm() {
],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/codeAction",
json!({
@ -6657,6 +6676,7 @@ fn lsp_cache_then_definition() {
],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -7606,6 +7626,7 @@ fn lsp_quote_style_from_workspace_settings() {
"type": 1,
}],
}));
client.read_diagnostics();
let res = client.write_request("textDocument/codeAction", code_action_params);
// Expect double quotes in the auto-import.
@ -8090,6 +8111,7 @@ fn lsp_completions_auto_import_node_builtin() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let list = client.get_completion_list(
temp_dir.url().join("file.ts").unwrap(),
(2, 21),
@ -8158,6 +8180,7 @@ fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() {
],
}),
);
client.read_diagnostics();
// try auto-import with path
client.did_open(json!({
@ -8883,6 +8906,7 @@ fn lsp_npm_types_nested_js_dts() {
"arguments": [[], file.url()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open_file(&file);
assert_eq!(
json!(diagnostics.all()),
@ -8982,6 +9006,7 @@ fn lsp_npm_always_caches() {
],
}),
);
client.read_diagnostics();
// now open a new file and chalk should be working
let new_file_uri = temp_dir_path.join("new_file.ts").url_file();
@ -9104,6 +9129,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
],
}),
);
client.read_diagnostics();
// try auto-import with path
client.did_open(json!({
@ -9742,6 +9768,7 @@ fn lsp_completions_npm() {
],
}),
);
client.read_diagnostics();
// check importing a cjs default import
client.write_notification(
@ -9955,6 +9982,7 @@ fn lsp_auto_imports_remote_dts() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
let list = client.get_completion_list(
temp_dir.url().join("file.ts").unwrap(),
(2, 21),
@ -10257,6 +10285,7 @@ fn lsp_completions_node_builtin() {
"arguments": [["npm:@types/node"], "file:///a/file.ts"],
}),
);
client.read_diagnostics();
client.write_notification(
"textDocument/didChange",
@ -10324,6 +10353,7 @@ fn lsp_completions_node_specifier_node_modules_dir() {
"arguments": [[], temp_dir.url().join("file.ts").unwrap()],
}),
);
client.read_diagnostics();
client.write_notification(
"textDocument/didChange",
json!({
@ -10559,6 +10589,7 @@ fn lsp_cache_location() {
"arguments": [[], "file:///a/file.ts"],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -10653,6 +10684,7 @@ fn lsp_tls_cert() {
"arguments": [[], "file:///a/file.ts"],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -10751,6 +10783,7 @@ fn lsp_npmrc() {
"arguments": [[], file.url()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open_file(&file);
assert_eq!(
json!(diagnostics.all()),
@ -11147,6 +11180,7 @@ fn lsp_root_with_global_reference_types() {
"arguments": [[], file2.url()],
}),
);
client.read_diagnostics();
let diagnostics = client.did_open_file(&file);
assert_eq!(json!(diagnostics.all()), json!([]));
}
@ -11456,6 +11490,7 @@ fn lsp_performance() {
"tsc.op.op_load",
"tsc.op.op_script_names",
"tsc.request.$getAssets",
"tsc.request.$getDiagnostics",
"tsc.request.$getSupportedCodeFixes",
"tsc.request.getQuickInfoAtPosition",
]
@ -13187,6 +13222,7 @@ export function B() {
],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/hover",
json!({
@ -13209,10 +13245,6 @@ export function B() {
}
})
);
let diagnostics = client.read_diagnostics();
println!("{:?}", diagnostics);
client.shutdown();
}
@ -14035,6 +14067,7 @@ fn lsp_node_modules_dir() {
"arguments": [["npm:chalk", "npm:@types/node"], file_uri],
}),
);
client.read_diagnostics()
};
cache(&mut client);
@ -14068,11 +14101,9 @@ fn lsp_node_modules_dir() {
"imports": {},
},
"unstable": [],
} }));
} }))
};
refresh_config(&mut client);
let diagnostics = client.read_diagnostics();
let diagnostics = refresh_config(&mut client);
assert_eq!(diagnostics.all().len(), 2, "{:#?}", diagnostics); // not cached
cache(&mut client);
@ -14087,9 +14118,7 @@ fn lsp_node_modules_dir() {
"{ \"nodeModulesDir\": \"auto\" }\n",
);
refresh_config(&mut client);
cache(&mut client);
let diagnostics = client.read_diagnostics();
let diagnostics = cache(&mut client);
assert_eq!(diagnostics.all().len(), 0, "{:#?}", diagnostics);
assert!(lockfile_path.exists());
@ -14166,7 +14195,7 @@ fn lsp_vendor_dir() {
temp_dir.path().join("deno.json"),
"{ \"vendor\": true, \"lock\": false }\n",
);
client.change_configuration(json!({ "deno": {
let diagnostics = client.change_configuration(json!({ "deno": {
"enable": true,
"config": "./deno.json",
"codeLens": {
@ -14185,7 +14214,6 @@ fn lsp_vendor_dir() {
"unstable": [],
} }));
let diagnostics = client.read_diagnostics();
// won't be cached until a manual cache occurs
assert_eq!(
diagnostics
@ -14534,6 +14562,7 @@ fn lsp_deno_json_scopes_vendor_dir() {
"arguments": [[], temp_dir.url().join("project1/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -14584,6 +14613,7 @@ fn lsp_deno_json_scopes_vendor_dir() {
"arguments": [[], temp_dir.url().join("project2/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -14634,6 +14664,7 @@ fn lsp_deno_json_scopes_vendor_dir() {
"arguments": [[], temp_dir.url().join("project2/project3/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -14717,6 +14748,7 @@ fn lsp_deno_json_scopes_node_modules_dir() {
"arguments": [[], temp_dir.url().join("project1/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -14770,6 +14802,7 @@ fn lsp_deno_json_scopes_node_modules_dir() {
"arguments": [[], temp_dir.url().join("project2/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -14820,6 +14853,7 @@ fn lsp_deno_json_scopes_node_modules_dir() {
"arguments": [[], temp_dir.url().join("project2/project3/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -15799,6 +15833,7 @@ fn lsp_deno_json_workspace_vendor_dir() {
"arguments": [[], temp_dir.url().join("project1/project2/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -15870,6 +15905,7 @@ fn lsp_deno_json_workspace_node_modules_dir() {
"arguments": [[], temp_dir.url().join("project1/project2/file.ts").unwrap()],
}),
);
client.read_diagnostics();
let res = client.write_request(
"textDocument/definition",
json!({
@ -17343,6 +17379,7 @@ fn compiler_options_types() {
"type": 2,
}],
}));
client.read_diagnostics();
let diagnostics = client.did_open_file(&source);
eprintln!("{:#?}", diagnostics.all());
@ -17416,6 +17453,7 @@ fn type_reference_import_meta() {
"type": 2,
}],
}));
client.read_diagnostics();
let diagnostics = client.did_open_file(&source);
assert_eq!(diagnostics.all().len(), 0);

View file

@ -918,7 +918,10 @@ impl LspClient {
self.write_notification("textDocument/didOpen", params);
}
pub fn change_configuration(&mut self, config: Value) {
pub fn change_configuration(
&mut self,
config: Value,
) -> CollectedDiagnostics {
self.config = config;
if self.supports_workspace_configuration {
self.write_notification(
@ -932,6 +935,7 @@ impl LspClient {
json!({ "settings": &self.config }),
);
}
self.read_diagnostics()
}
pub fn handle_configuration_request(&mut self) {