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

perf(lsp): cache completion item resolution during request (#27831)

This commit adds a simple HashMap cache to completion requests.

On a test project the time to compute completions went from 1200ms
to 75ms and the cache contained ~300 entries when 8000 completion
items were produced by the TSC.

This is a short-lived cache and is discarded once the completion
request is finished. In a follow up commits we could make it persist
between requests.
This commit is contained in:
Bartek Iwańczuk 2025-01-26 22:40:53 +01:00
parent 77f6bd0db3
commit 244bf5c2be
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750

View file

@ -3683,6 +3683,10 @@ impl CompletionInfo {
position: u32, position: u32,
language_server: &language_server::Inner, language_server: &language_server::Inner,
) -> lsp::CompletionResponse { ) -> lsp::CompletionResponse {
// A cache for costly resolution computations.
// On a test project, it was found to speed up completion requests
// by 10-20x and contained ~300 entries for 8000 completion items.
let mut cache = HashMap::with_capacity(512);
let items = self let items = self
.entries .entries
.iter() .iter()
@ -3694,6 +3698,7 @@ impl CompletionInfo {
specifier, specifier,
position, position,
language_server, language_server,
&mut cache,
) )
}) })
.collect(); .collect();
@ -3898,6 +3903,7 @@ impl CompletionEntry {
self.insert_text.clone() self.insert_text.clone()
} }
#[allow(clippy::too_many_arguments)]
pub fn as_completion_item( pub fn as_completion_item(
&self, &self,
line_index: Arc<LineIndex>, line_index: Arc<LineIndex>,
@ -3906,6 +3912,7 @@ impl CompletionEntry {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
position: u32, position: u32,
language_server: &language_server::Inner, language_server: &language_server::Inner,
resolution_cache: &mut HashMap<(ModuleSpecifier, ModuleSpecifier), String>,
) -> Option<lsp::CompletionItem> { ) -> Option<lsp::CompletionItem> {
let mut label = self.name.clone(); let mut label = self.name.clone();
let mut label_details: Option<lsp::CompletionItemLabelDetails> = None; let mut label_details: Option<lsp::CompletionItemLabelDetails> = None;
@ -3964,14 +3971,18 @@ impl CompletionEntry {
} }
} }
} }
if let Some(source) = &self.source { if let Some(source) = &self.source {
let mut display_source = source.clone(); let mut display_source = source.clone();
if let Some(import_data) = &self.auto_import_data { if let Some(import_data) = &self.auto_import_data {
let import_mapper = let import_mapper =
language_server.get_ts_response_import_mapper(specifier); language_server.get_ts_response_import_mapper(specifier);
if let Some(mut new_specifier) = import_mapper let maybe_cached = resolution_cache
.check_specifier(&import_data.normalized, specifier) .get(&(import_data.normalized.clone(), specifier.clone()))
.cloned();
if let Some(mut new_specifier) = maybe_cached
.or_else(|| {
import_mapper.check_specifier(&import_data.normalized, specifier)
})
.or_else(|| relative_specifier(specifier, &import_data.normalized)) .or_else(|| relative_specifier(specifier, &import_data.normalized))
.or_else(|| { .or_else(|| {
ModuleSpecifier::parse(&import_data.raw.module_specifier) ModuleSpecifier::parse(&import_data.raw.module_specifier)
@ -3979,6 +3990,10 @@ impl CompletionEntry {
.then(|| import_data.normalized.to_string()) .then(|| import_data.normalized.to_string())
}) })
{ {
resolution_cache.insert(
(import_data.normalized.clone(), specifier.clone()),
new_specifier.clone(),
);
if new_specifier.contains("/node_modules/") { if new_specifier.contains("/node_modules/") {
return None; return None;
} }