From 382bb2af15e7076f8dcbbe6cc4a6b135709ef7b9 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 01:32:06 +0100 Subject: autocomplete: key results by extension filter and reject the placeholder --- src/commands/autocomplete.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src/commands/autocomplete.rs') diff --git a/src/commands/autocomplete.rs b/src/commands/autocomplete.rs index d05a48b..4cefd67 100644 --- a/src/commands/autocomplete.rs +++ b/src/commands/autocomplete.rs @@ -20,7 +20,7 @@ const AC_SEARCH_TIMEOUT: Duration = Duration::from_secs(2); /// Value used for the mock "searching" placeholder — recognisable by the /// command handler as a no-op. -const MOCK_SEARCHING: &str = "\u{231B}..."; +pub(crate) const MOCK_SEARCHING: &str = "\u{231B}..."; fn truncate_choice(s: &str) -> String { if s.chars().count() <= MAX_CHOICE_LEN { @@ -98,6 +98,12 @@ type SearchCache = Arc< tokio::sync::Mutex, std::time::Instant)>>, >; +fn search_cache_key(partial: &str, extensions: &[&str]) -> String { + // The extension filter changes the result set, so it must be part of the + // key; otherwise `/music` and `/upload` poison each other's cache. + format!("{partial}\u{1f}{}", extensions.join(",")) +} + fn spawn_search( cache: SearchCache, client: copyparty::CopypartyClient, @@ -114,10 +120,10 @@ fn spawn_search( Ok(hits) => { debug!(%query, count = hits.len(), "background search completed"); // Always fill the shared search cache - cache - .lock() - .await - .insert(query, (hits.clone(), std::time::Instant::now())); + cache.lock().await.insert( + search_cache_key(&query, &exts), + (hits.clone(), std::time::Instant::now()), + ); // Send to foreground (ignores error if receiver was dropped) let _ = tx.send(hits); } @@ -143,8 +149,14 @@ async fn timed_search( partial: &str, extensions: &[&str], ) -> Vec { + // The placeholder is a UI hint, not a real query. + if partial == MOCK_SEARCHING { + return vec![]; + } + // 1. Shared search cache hit? - if let Some(hits) = ctx.data().ac_search_cache_lookup(partial).await + let cache_key = search_cache_key(partial, extensions); + if let Some(hits) = ctx.data().ac_search_cache_lookup(&cache_key).await && !hits.is_empty() { debug!(%partial, count = hits.len(), "autocomplete cache hit"); let pairs = build_choices(&hits); -- cgit v1.2.3