From 8efa14ae1b4dd7f672fba59f114973cb05e05937 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 29 Dec 2025 10:16:32 +0100 Subject: [PATCH] [ty] Limit the returned completions to reduce lag (#22240) --- crates/ty_ide/src/all_symbols.rs | 8 ++++++++ crates/ty_ide/src/completion.rs | 3 +++ crates/ty_ide/src/workspace_symbols.rs | 7 +++++++ crates/ty_server/src/server/api.rs | 8 ++++---- crates/ty_server/src/server/main_loop.rs | 4 ++-- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/ty_ide/src/all_symbols.rs b/crates/ty_ide/src/all_symbols.rs index 2ba09d6374..cc200377a5 100644 --- a/crates/ty_ide/src/all_symbols.rs +++ b/crates/ty_ide/src/all_symbols.rs @@ -21,6 +21,9 @@ pub fn all_symbols<'db>( return Vec::new(); } + let all_symbols_span = tracing::debug_span!("all_symbols"); + let _span = all_symbols_span.enter(); + let typing_extensions = ModuleName::new_static("typing_extensions").unwrap(); let is_typing_extensions_available = importing_from.is_stub(db) || resolve_real_shadowable_module(db, importing_from, &typing_extensions).is_some(); @@ -29,6 +32,7 @@ pub fn all_symbols<'db>( { let modules = all_modules(db); let db = db.dyn_clone(); + let all_symbols_span = &all_symbols_span; let results = &results; let query = &query; @@ -39,6 +43,7 @@ pub fn all_symbols<'db>( let Some(file) = module.file(&*db) else { continue; }; + // By convention, modules starting with an underscore // are generally considered unexported. However, we // should consider first party modules fair game. @@ -59,6 +64,9 @@ pub fn all_symbols<'db>( continue; } s.spawn(move |_| { + let symbols_for_file_span = tracing::debug_span!(parent: all_symbols_span, "symbols_for_file_global_only", ?file); + let _entered = symbols_for_file_span.entered(); + if query.is_match_symbol_name(module.name(&*db)) { results.lock().unwrap().push(AllSymbolInfo { symbol: None, diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index ba595d6e6c..691b4ac830 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -117,6 +117,9 @@ impl<'db> Completions<'db> { self.items.sort_by(|c1, c2| self.context.compare(c1, c2)); self.items .dedup_by(|c1, c2| (&c1.name, c1.module_name) == (&c2.name, c2.module_name)); + // A user should refine its completion request if the searched symbol doesn't appear in the first 1k results. + // Serializing/deserializing 1k completions can be expensive and result in noticeable lag. + self.items.truncate(1000); self.items } diff --git a/crates/ty_ide/src/workspace_symbols.rs b/crates/ty_ide/src/workspace_symbols.rs index d2b01e0c94..60959eb209 100644 --- a/crates/ty_ide/src/workspace_symbols.rs +++ b/crates/ty_ide/src/workspace_symbols.rs @@ -10,6 +10,9 @@ pub fn workspace_symbols(db: &dyn Db, query: &str) -> Vec { return Vec::new(); } + let workspace_symbols_span = tracing::debug_span!("workspace_symbols"); + let _span = workspace_symbols_span.enter(); + let project = db.project(); let query = QueryPattern::fuzzy(query); @@ -20,12 +23,16 @@ pub fn workspace_symbols(db: &dyn Db, query: &str) -> Vec { let files = &files; let results = &results; let query = &query; + let workspace_symbols_span = &workspace_symbols_span; rayon::scope(move |s| { // For each file, extract symbols and add them to results for file in files.iter() { let db = db.dyn_clone(); s.spawn(move |_| { + let symbols_for_file_span = tracing::debug_span!(parent: workspace_symbols_span, "symbols_for_file", ?file); + let _entered = symbols_for_file_span.entered(); + for (_, symbol) in symbols_for_file(&*db, *file).search(query) { // It seems like we could do better here than // locking `results` for every single symbol, diff --git a/crates/ty_server/src/server/api.rs b/crates/ty_server/src/server/api.rs index 3016a3be3a..83fd1c062f 100644 --- a/crates/ty_server/src/server/api.rs +++ b/crates/ty_server/src/server/api.rs @@ -224,7 +224,7 @@ where // Test again if the request was cancelled since it was scheduled on the background task // and, if so, return early if cancellation_token.is_cancelled() { - tracing::trace!( + tracing::debug!( "Ignoring request id={id} method={} because it was cancelled", R::METHOD ); @@ -291,7 +291,7 @@ where // Test again if the request was cancelled since it was scheduled on the background task // and, if so, return early if cancellation_token.is_cancelled() { - tracing::trace!( + tracing::debug!( "Ignoring request id={id} method={} because it was cancelled", R::METHOD ); @@ -325,14 +325,14 @@ fn panic_response( // If the query supports retry, re-queue the request. // The query is still likely to succeed if the user modified any other document. if let Some(request) = request { - tracing::trace!( + tracing::debug!( "request id={} method={} was cancelled by salsa, re-queueing for retry", request.id, request.method ); client.retry(request); } else { - tracing::trace!( + tracing::debug!( "request id={} was cancelled by salsa, sending content modified", id ); diff --git a/crates/ty_server/src/server/main_loop.rs b/crates/ty_server/src/server/main_loop.rs index cbd707f60d..4d74435d62 100644 --- a/crates/ty_server/src/server/main_loop.rs +++ b/crates/ty_server/src/server/main_loop.rs @@ -110,11 +110,11 @@ impl Server { .complete(&response.id) { let duration = start_time.elapsed(); - tracing::trace!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration)); + tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration)); self.connection.sender.send(Message::Response(response))?; } else { - tracing::trace!( + tracing::debug!( "Ignoring response for canceled request id={}", response.id );