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 committed by GitHub
parent b59c21d2c2
commit 23adf99e83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3683,6 +3683,10 @@ impl CompletionInfo {
position: u32,
language_server: &language_server::Inner,
) -> 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
.entries
.iter()
@ -3694,6 +3698,7 @@ impl CompletionInfo {
specifier,
position,
language_server,
&mut cache,
)
})
.collect();
@ -3898,6 +3903,7 @@ impl CompletionEntry {
self.insert_text.clone()
}
#[allow(clippy::too_many_arguments)]
pub fn as_completion_item(
&self,
line_index: Arc<LineIndex>,
@ -3906,6 +3912,7 @@ impl CompletionEntry {
specifier: &ModuleSpecifier,
position: u32,
language_server: &language_server::Inner,
resolution_cache: &mut HashMap<(ModuleSpecifier, ModuleSpecifier), String>,
) -> Option<lsp::CompletionItem> {
let mut label = self.name.clone();
let mut label_details: Option<lsp::CompletionItemLabelDetails> = None;
@ -3964,14 +3971,18 @@ impl CompletionEntry {
}
}
}
if let Some(source) = &self.source {
let mut display_source = source.clone();
if let Some(import_data) = &self.auto_import_data {
let import_mapper =
language_server.get_ts_response_import_mapper(specifier);
if let Some(mut new_specifier) = import_mapper
.check_specifier(&import_data.normalized, specifier)
let maybe_cached = resolution_cache
.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(|| {
ModuleSpecifier::parse(&import_data.raw.module_specifier)
@ -3979,6 +3990,10 @@ impl CompletionEntry {
.then(|| import_data.normalized.to_string())
})
{
resolution_cache.insert(
(import_data.normalized.clone(), specifier.clone()),
new_specifier.clone(),
);
if new_specifier.contains("/node_modules/") {
return None;
}