perf(repl): don't walk workspace in repl language server (#24037)

This commit is contained in:
Nayeem Rahman 2024-05-29 21:31:09 +01:00 committed by GitHub
parent 3c3076a84c
commit 3d3722507e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 90 additions and 81 deletions

View file

@ -31,6 +31,7 @@ use deno_runtime::fs_util::specifier_to_file_path;
use deno_runtime::permissions::PermissionsContainer; use deno_runtime::permissions::PermissionsContainer;
use import_map::ImportMap; use import_map::ImportMap;
use lsp::Url; use lsp::Url;
use lsp_types::ClientCapabilities;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::collections::HashMap; use std::collections::HashMap;
@ -40,21 +41,6 @@ use tower_lsp::lsp_types as lsp;
pub const SETTINGS_SECTION: &str = "deno"; pub const SETTINGS_SECTION: &str = "deno";
#[derive(Debug, Clone, Default)]
pub struct ClientCapabilities {
pub code_action_disabled_support: bool,
pub line_folding_only: bool,
pub snippet_support: bool,
pub status_notification: bool,
/// The client provides the `experimental.testingApi` capability, which is
/// built around VSCode's testing API. It indicates that the server should
/// send notifications about tests discovered in modules.
pub testing_api: bool,
pub workspace_configuration: bool,
pub workspace_did_change_watched_files: bool,
pub workspace_will_rename_files: bool,
}
fn is_true() -> bool { fn is_true() -> bool {
true true
} }
@ -975,57 +961,73 @@ impl Config {
&self.settings.unscoped.internal_inspect &self.settings.unscoped.internal_inspect
} }
pub fn update_capabilities( pub fn set_client_capabilities(
&mut self, &mut self,
capabilities: &lsp::ClientCapabilities, client_capabilities: ClientCapabilities,
) { ) {
if let Some(experimental) = &capabilities.experimental { self.client_capabilities = client_capabilities;
self.client_capabilities.status_notification = experimental
.get("statusNotification")
.and_then(|it| it.as_bool())
== Some(true);
self.client_capabilities.testing_api =
experimental.get("testingApi").and_then(|it| it.as_bool())
== Some(true);
} }
if let Some(workspace) = &capabilities.workspace { pub fn workspace_capable(&self) -> bool {
self.client_capabilities.workspace_configuration = self.client_capabilities.workspace.is_some()
workspace.configuration.unwrap_or(false);
self.client_capabilities.workspace_did_change_watched_files = workspace
.did_change_watched_files
.and_then(|it| it.dynamic_registration)
.unwrap_or(false);
if let Some(file_operations) = &workspace.file_operations {
if let Some(true) = file_operations.dynamic_registration {
self.client_capabilities.workspace_will_rename_files =
file_operations.will_rename.unwrap_or(false);
}
}
} }
if let Some(text_document) = &capabilities.text_document { pub fn workspace_configuration_capable(&self) -> bool {
self.client_capabilities.line_folding_only = text_document (|| self.client_capabilities.workspace.as_ref()?.configuration)()
.folding_range
.as_ref()
.and_then(|it| it.line_folding_only)
.unwrap_or(false);
self.client_capabilities.code_action_disabled_support = text_document
.code_action
.as_ref()
.and_then(|it| it.disabled_support)
.unwrap_or(false);
self.client_capabilities.snippet_support =
if let Some(completion) = &text_document.completion {
completion
.completion_item
.as_ref()
.and_then(|it| it.snippet_support)
.unwrap_or(false) .unwrap_or(false)
} else {
false
};
} }
pub fn did_change_watched_files_capable(&self) -> bool {
(|| {
let workspace = self.client_capabilities.workspace.as_ref()?;
let did_change_watched_files =
workspace.did_change_watched_files.as_ref()?;
did_change_watched_files.dynamic_registration
})()
.unwrap_or(false)
}
pub fn will_rename_files_capable(&self) -> bool {
(|| {
let workspace = self.client_capabilities.workspace.as_ref()?;
let file_operations = workspace.file_operations.as_ref()?;
file_operations.dynamic_registration.filter(|d| *d)?;
file_operations.will_rename
})()
.unwrap_or(false)
}
pub fn line_folding_only_capable(&self) -> bool {
(|| {
let text_document = self.client_capabilities.text_document.as_ref()?;
text_document.folding_range.as_ref()?.line_folding_only
})()
.unwrap_or(false)
}
pub fn code_action_disabled_capable(&self) -> bool {
(|| {
let text_document = self.client_capabilities.text_document.as_ref()?;
text_document.code_action.as_ref()?.disabled_support
})()
.unwrap_or(false)
}
pub fn snippet_support_capable(&self) -> bool {
(|| {
let text_document = self.client_capabilities.text_document.as_ref()?;
let completion = text_document.completion.as_ref()?;
completion.completion_item.as_ref()?.snippet_support
})()
.unwrap_or(false)
}
pub fn testing_api_capable(&self) -> bool {
(|| {
let experimental = self.client_capabilities.experimental.as_ref()?;
experimental.get("testingApi")?.as_bool()
})()
.unwrap_or(false)
} }
} }

View file

@ -226,7 +226,7 @@ fn get_maybe_test_module_fut(
maybe_parsed_source: Option<&ParsedSourceResult>, maybe_parsed_source: Option<&ParsedSourceResult>,
config: &Config, config: &Config,
) -> Option<TestModuleFut> { ) -> Option<TestModuleFut> {
if !config.client_capabilities.testing_api { if !config.testing_api_capable() {
return None; return None;
} }
let parsed_source = maybe_parsed_source?.as_ref().ok()?.clone(); let parsed_source = maybe_parsed_source?.as_ref().ok()?.clone();

View file

@ -439,7 +439,7 @@ impl LanguageServer {
( (
inner.client.clone(), inner.client.clone(),
inner.config.workspace_folders.clone(), inner.config.workspace_folders.clone(),
inner.config.client_capabilities.workspace_configuration, inner.config.workspace_configuration_capable(),
) )
}; };
if capable { if capable {
@ -769,7 +769,7 @@ impl Inner {
vec![], vec![],
); );
} }
self.config.update_capabilities(&params.capabilities); self.config.set_client_capabilities(params.capabilities);
} }
self.diagnostics_server.start(); self.diagnostics_server.start();
@ -802,6 +802,10 @@ impl Inner {
} }
fn walk_workspace(config: &Config) -> (BTreeSet<ModuleSpecifier>, bool) { fn walk_workspace(config: &Config) -> (BTreeSet<ModuleSpecifier>, bool) {
if !config.workspace_capable() {
log::debug!("Skipped workspace walk due to client incapability.");
return (Default::default(), false);
}
let mut workspace_files = Default::default(); let mut workspace_files = Default::default();
let entry_limit = 1000; let entry_limit = 1000;
let mut pending = VecDeque::new(); let mut pending = VecDeque::new();
@ -1664,10 +1668,10 @@ impl Inner {
.map(CodeActionOrCommand::CodeAction), .map(CodeActionOrCommand::CodeAction),
); );
let code_action_disabled_support = let code_action_disabled_capable =
self.config.client_capabilities.code_action_disabled_support; self.config.code_action_disabled_capable();
let actions: Vec<CodeActionOrCommand> = all_actions.into_iter().filter(|ca| { let actions: Vec<CodeActionOrCommand> = all_actions.into_iter().filter(|ca| {
code_action_disabled_support code_action_disabled_capable
|| matches!(ca, CodeActionOrCommand::CodeAction(ca) if ca.disabled.is_none()) || matches!(ca, CodeActionOrCommand::CodeAction(ca) if ca.disabled.is_none())
}).collect(); }).collect();
let response = if actions.is_empty() { let response = if actions.is_empty() {
@ -2318,7 +2322,7 @@ impl Inner {
span.to_folding_range( span.to_folding_range(
asset_or_doc.line_index(), asset_or_doc.line_index(),
asset_or_doc.text().as_bytes(), asset_or_doc.text().as_bytes(),
self.config.client_capabilities.line_folding_only, self.config.line_folding_only_capable(),
) )
}) })
.collect::<Vec<FoldingRange>>(), .collect::<Vec<FoldingRange>>(),
@ -2887,11 +2891,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
inner.refresh_documents_config().await; inner.refresh_documents_config().await;
inner.task_queue.start(self.clone()); inner.task_queue.start(self.clone());
self.init_flag.raise(); self.init_flag.raise();
if inner if inner.config.did_change_watched_files_capable() {
.config
.client_capabilities
.workspace_did_change_watched_files
{
// we are going to watch all the JSON files in the workspace, and the // we are going to watch all the JSON files in the workspace, and the
// notification handler will pick up any of the changes of those files we // notification handler will pick up any of the changes of those files we
// are interested in. // are interested in.
@ -2909,7 +2909,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
register_options: Some(serde_json::to_value(options).unwrap()), register_options: Some(serde_json::to_value(options).unwrap()),
}); });
} }
if inner.config.client_capabilities.workspace_will_rename_files { if inner.config.will_rename_files_capable() {
let options = FileOperationRegistrationOptions { let options = FileOperationRegistrationOptions {
filters: vec![FileOperationFilter { filters: vec![FileOperationFilter {
scheme: Some("file".to_string()), scheme: Some("file".to_string()),
@ -2927,7 +2927,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
}); });
} }
if inner.config.client_capabilities.testing_api { if inner.config.testing_api_capable() {
let test_server = testing::TestServer::new( let test_server = testing::TestServer::new(
inner.client.clone(), inner.client.clone(),
inner.performance.clone(), inner.performance.clone(),
@ -3051,7 +3051,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
}; };
self.refresh_configuration().await; self.refresh_configuration().await;
let mut inner = self.inner.write().await; let mut inner = self.inner.write().await;
if !inner.config.client_capabilities.workspace_configuration { if !inner.config.workspace_configuration_capable() {
let config = params.settings.as_object().map(|settings| { let config = params.settings.as_object().map(|settings| {
let deno = let deno =
serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap(); serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
@ -3726,6 +3726,10 @@ mod tests {
temp_dir.uri().join("root2/").unwrap(), temp_dir.uri().join("root2/").unwrap(),
temp_dir.uri().join("root3/").unwrap(), temp_dir.uri().join("root3/").unwrap(),
]); ]);
config.set_client_capabilities(ClientCapabilities {
workspace: Some(Default::default()),
..Default::default()
});
config.set_workspace_settings( config.set_workspace_settings(
Default::default(), Default::default(),
vec![ vec![

View file

@ -4679,7 +4679,7 @@ impl UserPreferences {
// TODO(nayeemrmn): Investigate why we use `Index` here. // TODO(nayeemrmn): Investigate why we use `Index` here.
import_module_specifier_ending: Some(ImportModuleSpecifierEnding::Index), import_module_specifier_ending: Some(ImportModuleSpecifierEnding::Index),
include_completions_with_snippet_text: Some( include_completions_with_snippet_text: Some(
config.client_capabilities.snippet_support, config.snippet_support_capable(),
), ),
provide_refactor_not_applicable_reason: Some(true), provide_refactor_not_applicable_reason: Some(true),
quote_preference: Some(fmt_config.into()), quote_preference: Some(fmt_config.into()),
@ -4717,7 +4717,7 @@ impl UserPreferences {
include_completions_with_class_member_snippets: Some( include_completions_with_class_member_snippets: Some(
language_settings.suggest.enabled language_settings.suggest.enabled
&& language_settings.suggest.class_member_snippets.enabled && language_settings.suggest.class_member_snippets.enabled
&& config.client_capabilities.snippet_support, && config.snippet_support_capable(),
), ),
include_completions_with_insert_text: Some( include_completions_with_insert_text: Some(
language_settings.suggest.enabled, language_settings.suggest.enabled,
@ -4728,7 +4728,7 @@ impl UserPreferences {
.suggest .suggest
.object_literal_method_snippets .object_literal_method_snippets
.enabled .enabled
&& config.client_capabilities.snippet_support, && config.snippet_support_capable(),
), ),
import_module_specifier_preference: Some( import_module_specifier_preference: Some(
language_settings.preferences.import_module_specifier, language_settings.preferences.import_module_specifier,

View file

@ -1084,7 +1084,10 @@ fn closed_file_pre_load_does_not_occur() {
.new_command() .new_command()
.args_vec(["repl", "-A", "--log-level=debug"]) .args_vec(["repl", "-A", "--log-level=debug"])
.with_pty(|console| { .with_pty(|console| {
assert_contains!(console.all_output(), "Skipped document preload.",); assert_contains!(
console.all_output(),
"Skipped workspace walk due to client incapability.",
);
}); });
} }