Skip to content

Commit 244bf5c

Browse files
committedJan 30, 2025
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.
1 parent 77f6bd0 commit 244bf5c

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed
 

‎cli/lsp/tsc.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -3683,6 +3683,10 @@ impl CompletionInfo {
36833683
position: u32,
36843684
language_server: &language_server::Inner,
36853685
) -> lsp::CompletionResponse {
3686+
// A cache for costly resolution computations.
3687+
// On a test project, it was found to speed up completion requests
3688+
// by 10-20x and contained ~300 entries for 8000 completion items.
3689+
let mut cache = HashMap::with_capacity(512);
36863690
let items = self
36873691
.entries
36883692
.iter()
@@ -3694,6 +3698,7 @@ impl CompletionInfo {
36943698
specifier,
36953699
position,
36963700
language_server,
3701+
&mut cache,
36973702
)
36983703
})
36993704
.collect();
@@ -3898,6 +3903,7 @@ impl CompletionEntry {
38983903
self.insert_text.clone()
38993904
}
39003905

3906+
#[allow(clippy::too_many_arguments)]
39013907
pub fn as_completion_item(
39023908
&self,
39033909
line_index: Arc<LineIndex>,
@@ -3906,6 +3912,7 @@ impl CompletionEntry {
39063912
specifier: &ModuleSpecifier,
39073913
position: u32,
39083914
language_server: &language_server::Inner,
3915+
resolution_cache: &mut HashMap<(ModuleSpecifier, ModuleSpecifier), String>,
39093916
) -> Option<lsp::CompletionItem> {
39103917
let mut label = self.name.clone();
39113918
let mut label_details: Option<lsp::CompletionItemLabelDetails> = None;
@@ -3964,21 +3971,29 @@ impl CompletionEntry {
39643971
}
39653972
}
39663973
}
3967-
39683974
if let Some(source) = &self.source {
39693975
let mut display_source = source.clone();
39703976
if let Some(import_data) = &self.auto_import_data {
39713977
let import_mapper =
39723978
language_server.get_ts_response_import_mapper(specifier);
3973-
if let Some(mut new_specifier) = import_mapper
3974-
.check_specifier(&import_data.normalized, specifier)
3979+
let maybe_cached = resolution_cache
3980+
.get(&(import_data.normalized.clone(), specifier.clone()))
3981+
.cloned();
3982+
if let Some(mut new_specifier) = maybe_cached
3983+
.or_else(|| {
3984+
import_mapper.check_specifier(&import_data.normalized, specifier)
3985+
})
39753986
.or_else(|| relative_specifier(specifier, &import_data.normalized))
39763987
.or_else(|| {
39773988
ModuleSpecifier::parse(&import_data.raw.module_specifier)
39783989
.is_ok()
39793990
.then(|| import_data.normalized.to_string())
39803991
})
39813992
{
3993+
resolution_cache.insert(
3994+
(import_data.normalized.clone(), specifier.clone()),
3995+
new_specifier.clone(),
3996+
);
39823997
if new_specifier.contains("/node_modules/") {
39833998
return None;
39843999
}

0 commit comments

Comments
 (0)
Please sign in to comment.