From 52e45defac1f5d8e62f543fb4d5715d66a414405 Mon Sep 17 00:00:00 2001 From: David Kay Date: Sat, 21 Mar 2026 19:21:30 -0500 Subject: [PATCH 1/5] fix(search): add search.smart_case config option When smart_case is true (the default), the existing behavior is preserved: all-lowercase queries match case-insensitively via LIKE, while queries containing uppercase characters match case-sensitively via GLOB. When smart_case is false, all queries match case-insensitively regardless of casing. This addresses the long-standing request for case-insensitive fuzzy search without needing to switch to skim or daemon-fuzzy mode. Closes #625 --- crates/atuin-client/config.toml | 6 ++ crates/atuin-client/src/database.rs | 94 ++++++++++++++++++- crates/atuin-client/src/settings.rs | 14 +++ crates/atuin/src/command/client/search.rs | 1 + .../src/command/client/search/engines.rs | 4 +- .../command/client/search/engines/daemon.rs | 3 + .../src/command/client/search/engines/db.rs | 3 +- 7 files changed, 120 insertions(+), 5 deletions(-) diff --git a/crates/atuin-client/config.toml b/crates/atuin-client/config.toml index 6e67a4e1eb9..a81a365233e 100644 --- a/crates/atuin-client/config.toml +++ b/crates/atuin-client/config.toml @@ -308,6 +308,12 @@ records = true ## Default filter mode can be overridden with the filter_mode setting. # filters = [ "global", "host", "session", "session-preload", "workspace", "directory" ] +## Use smart-case matching for search queries. +## When true (default), all-lowercase queries match case-insensitively, while queries +## containing uppercase characters match case-sensitively. Similar to fzf's --smart-case. +## Set to false to always match case-insensitively, regardless of query casing. +# smart_case = true + [tmux] ## Enable using atuin with tmux popup (requires tmux >= 3.2) ## When enabled and running inside tmux, Atuin will use a popup window for interactive search. diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs index 7c63368d016..17ae4b2e2d7 100644 --- a/crates/atuin-client/src/database.rs +++ b/crates/atuin-client/src/database.rs @@ -132,6 +132,7 @@ pub trait Database: Send + Sync + 'static { context: &Context, query: &str, filter_options: OptFilters, + smart_case: bool, ) -> Result>; async fn query_history(&self, query: &str) -> Result>; @@ -456,6 +457,7 @@ impl Database for Sqlite { context: &Context, query: &str, filter_options: OptFilters, + smart_case: bool, ) -> Result> { let mut sql = SqlBuilder::select_from("history"); @@ -510,8 +512,9 @@ impl Database for Sqlite { _ => { let mut is_or = false; for token in QueryTokenizer::new(query) { - // TODO smart case mode could be made configurable like in fzf - let (is_glob, glob) = if token.has_uppercase() { + // Use GLOB (case-sensitive) when smart_case is enabled and the + // token contains uppercase chars, otherwise LIKE (case-insensitive). + let (is_glob, glob) = if smart_case && token.has_uppercase() { (true, "*") } else { (false, "%") @@ -959,6 +962,7 @@ mod test { OptFilters { ..Default::default() }, + true, // smart_case: use default behavior in tests ) .await?; @@ -1237,6 +1241,91 @@ mod test { .unwrap(); } + #[tokio::test(flavor = "multi_thread")] + async fn test_search_smart_case_disabled() { + let mut db = Sqlite::new("sqlite::memory:", test_local_timeout()) + .await + .unwrap(); + new_history_item(&mut db, "ls /home/ellie").await.unwrap(); + new_history_item(&mut db, "cd /home/Ellie").await.unwrap(); + new_history_item(&mut db, "/home/ellie/.bin/rustup") + .await + .unwrap(); + + let context = Context { + hostname: "test:host".to_string(), + session: "beepboopiamasession".to_string(), + cwd: "/home/ellie".to_string(), + host_id: "test-host".to_string(), + git_root: None, + }; + + // With smart_case=true (default), "Ellie" should only match the one with uppercase E + let results = db + .search( + SearchMode::Fuzzy, + FilterMode::Global, + &context, + "Ellie", + OptFilters::default(), + true, + ) + .await + .unwrap(); + assert_eq!( + results.len(), + 1, + "smart_case=true: uppercase query should match case-sensitively" + ); + + // With smart_case=false, "Ellie" should match all entries containing ellie/Ellie + let results = db + .search( + SearchMode::Fuzzy, + FilterMode::Global, + &context, + "Ellie", + OptFilters::default(), + false, + ) + .await + .unwrap(); + assert_eq!( + results.len(), + 3, + "smart_case=false: uppercase query should still match case-insensitively" + ); + + // Lowercase query should always match case-insensitively regardless of smart_case + let results_smart = db + .search( + SearchMode::Fuzzy, + FilterMode::Global, + &context, + "ellie", + OptFilters::default(), + true, + ) + .await + .unwrap(); + let results_no_smart = db + .search( + SearchMode::Fuzzy, + FilterMode::Global, + &context, + "ellie", + OptFilters::default(), + false, + ) + .await + .unwrap(); + assert_eq!( + results_smart.len(), + results_no_smart.len(), + "lowercase queries should behave identically regardless of smart_case" + ); + } + #[tokio::test(flavor = "multi_thread")] async fn test_paged_basic() { let mut db = Sqlite::new("sqlite::memory:", test_local_timeout()) @@ -1385,6 +1474,7 @@ mod test { OptFilters { ..Default::default() }, + true, ) .await .unwrap(); diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index 745bd2ff577..5a22788608f 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -17,6 +17,11 @@ use serde_with::DeserializeFromStr; use time::{OffsetDateTime, UtcOffset, format_description::FormatItem, macros::format_description}; pub const HISTORY_PAGE_SIZE: i64 = 100; + +/// Serde default helper that returns `true`. +fn default_true() -> bool { + true +} static EXAMPLE_CONFIG: &str = include_str!("../config.toml"); static DATA_DIR: OnceLock = OnceLock::new(); @@ -526,6 +531,14 @@ pub struct Search { /// The overall frecency score multiplier for the search index (default: 1.0). /// Applied after combining recency and frequency scores. pub frecency_score_multiplier: f64, + + /// Use smart-case matching for search queries (default: true). + /// When true, queries with all lowercase characters match case-insensitively, + /// while queries containing uppercase characters match case-sensitively. + /// When false, all queries match case-insensitively regardless of casing. + /// Similar to fzf's --smart-case / --no-smart-case options. + #[serde(default = "default_true")] + pub smart_case: bool, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -781,6 +794,7 @@ impl Default for Search { recency_score_multiplier: 1.0, frequency_score_multiplier: 1.0, frecency_score_multiplier: 1.0, + smart_case: true, } } } diff --git a/crates/atuin/src/command/client/search.rs b/crates/atuin/src/command/client/search.rs index 7c72e13dacb..8f6e534b2ff 100644 --- a/crates/atuin/src/command/client/search.rs +++ b/crates/atuin/src/command/client/search.rs @@ -332,6 +332,7 @@ async fn run_non_interactive( &context, query.join(" ").as_str(), opt_filter, + settings.search.smart_case, ) .await?; diff --git a/crates/atuin/src/command/client/search/engines.rs b/crates/atuin/src/command/client/search/engines.rs index 8cbee0c3d07..d98cb339fc2 100644 --- a/crates/atuin/src/command/client/search/engines.rs +++ b/crates/atuin/src/command/client/search/engines.rs @@ -22,9 +22,9 @@ pub fn engine(search_mode: SearchMode, settings: &Settings) -> Box { // Fall back to fuzzy mode if daemon feature is not enabled - Box::new(db::Search(SearchMode::Fuzzy)) as Box<_> + Box::new(db::Search(SearchMode::Fuzzy, settings.search.smart_case)) as Box<_> } - mode => Box::new(db::Search(mode)) as Box<_>, + mode => Box::new(db::Search(mode, settings.search.smart_case)) as Box<_>, } } diff --git a/crates/atuin/src/command/client/search/engines/daemon.rs b/crates/atuin/src/command/client/search/engines/daemon.rs index c5de39abbb0..5219d1c7e3d 100644 --- a/crates/atuin/src/command/client/search/engines/daemon.rs +++ b/crates/atuin/src/command/client/search/engines/daemon.rs @@ -18,6 +18,7 @@ use super::{SearchEngine, SearchState}; pub struct Search { client: Option, query_id: u64, + smart_case: bool, #[cfg(unix)] socket_path: String, #[cfg(not(unix))] @@ -29,6 +30,7 @@ impl Search { Search { client: None, query_id: 0, + smart_case: settings.search.smart_case, #[cfg(unix)] socket_path: settings.daemon.socket_path.clone(), #[cfg(not(unix))] @@ -77,6 +79,7 @@ impl Search { limit: Some(200), ..Default::default() }, + self.smart_case, ) .await .map_or(Vec::new(), |r| r.into_iter().collect()); diff --git a/crates/atuin/src/command/client/search/engines/db.rs b/crates/atuin/src/command/client/search/engines/db.rs index 476462f5484..39088c94561 100644 --- a/crates/atuin/src/command/client/search/engines/db.rs +++ b/crates/atuin/src/command/client/search/engines/db.rs @@ -13,7 +13,7 @@ use norm::fzf::{FzfParser, FzfV2}; use std::ops::Range; use tracing::{Level, instrument}; -pub struct Search(pub SearchMode); +pub struct Search(pub SearchMode, pub bool); #[async_trait] impl SearchEngine for Search { @@ -33,6 +33,7 @@ impl SearchEngine for Search { limit: Some(200), ..Default::default() }, + self.1, ) .await // ignore errors as it may be caused by incomplete regex From 63818593fa2e419cd656350322378799eeaa7ddd Mon Sep 17 00:00:00 2001 From: David Kay Date: Sat, 21 Mar 2026 19:27:29 -0500 Subject: [PATCH 2/5] fix(search): respect smart_case in highlight functions The highlight functions had their own hardcoded smart-case logic via has_uppercase() checks, independent of the new search.smart_case config. This caused a mismatch where the SQL query would find results case-insensitively but highlights would fail to match. - Pass smart_case through to get_highlight_indices_fulltext - Set FzfV2 case sensitivity to Insensitive when smart_case is false - Lowercase search terms when matching against lowered command text to prevent mismatches (e.g., "Git" vs "git clone") --- .../command/client/search/engines/daemon.rs | 6 ++- .../src/command/client/search/engines/db.rs | 38 ++++++++++++++++--- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/crates/atuin/src/command/client/search/engines/daemon.rs b/crates/atuin/src/command/client/search/engines/daemon.rs index 5219d1c7e3d..a8407a6d7d0 100644 --- a/crates/atuin/src/command/client/search/engines/daemon.rs +++ b/crates/atuin/src/command/client/search/engines/daemon.rs @@ -193,7 +193,11 @@ impl SearchEngine for Search { fn get_highlight_indices(&self, command: &str, search_input: &str) -> Vec { // Use fulltext highlighting for regex queries if Self::contains_regex_pattern(search_input) { - return super::db::get_highlight_indices_fulltext(command, search_input); + return super::db::get_highlight_indices_fulltext( + command, + search_input, + self.smart_case, + ); } let mut matcher = Matcher::new(Config::DEFAULT); diff --git a/crates/atuin/src/command/client/search/engines/db.rs b/crates/atuin/src/command/client/search/engines/db.rs index 39088c94561..50e08211072 100644 --- a/crates/atuin/src/command/client/search/engines/db.rs +++ b/crates/atuin/src/command/client/search/engines/db.rs @@ -8,6 +8,7 @@ use atuin_client::{ settings::SearchMode, }; use eyre::Result; +use norm::CaseSensitivity; use norm::Metric; use norm::fzf::{FzfParser, FzfV2}; use std::ops::Range; @@ -46,9 +47,12 @@ impl SearchEngine for Search { if self.0 == SearchMode::Prefix { return vec![]; } else if self.0 == SearchMode::FullText { - return get_highlight_indices_fulltext(command, search_input); + return get_highlight_indices_fulltext(command, search_input, self.1); } let mut fzf = FzfV2::new(); + if !self.1 { + fzf.set_case_sensitivity(CaseSensitivity::Insensitive); + } let mut parser = FzfParser::new(); let query = parser.parse(search_input); let mut ranges: Vec> = Vec::new(); @@ -60,12 +64,19 @@ impl SearchEngine for Search { } #[instrument(skip_all, level = Level::TRACE, name = "db_highlight_fulltext")] -pub fn get_highlight_indices_fulltext(command: &str, search_input: &str) -> Vec { +pub fn get_highlight_indices_fulltext( + command: &str, + search_input: &str, + smart_case: bool, +) -> Vec { let mut ranges = vec![]; let lower_command = command.to_ascii_lowercase(); for token in QueryTokenizer::new(search_input) { - let matchee = if token.has_uppercase() { + // Match case-sensitively only when smart_case is enabled and the token + // contains uppercase characters, mirroring the SQL query logic. + let case_sensitive = smart_case && token.has_uppercase(); + let matchee = if case_sensitive { command } else { &lower_command @@ -85,18 +96,33 @@ pub fn get_highlight_indices_fulltext(command: &str, search_input: &str) -> Vec< } } QueryToken::MatchStart(term, _) => { - if matchee.starts_with(term) { + let term = if case_sensitive { + term.to_string() + } else { + term.to_ascii_lowercase() + }; + if matchee.starts_with(&term) { ranges.push(0..term.len()); } } QueryToken::MatchEnd(term, _) => { - if matchee.ends_with(term) { + let term = if case_sensitive { + term.to_string() + } else { + term.to_ascii_lowercase() + }; + if matchee.ends_with(&term) { let l = matchee.len(); ranges.push((l - term.len())..l); } } QueryToken::Match(term, _) | QueryToken::MatchFull(term, _) => { - for (idx, m) in matchee.match_indices(term) { + let term = if case_sensitive { + term.to_string() + } else { + term.to_ascii_lowercase() + }; + for (idx, m) in matchee.match_indices(&*term) { ranges.push(idx..(idx + m.len())); } } From 4a6fffae460aaac554a8ba88de444c3e0bd32b6c Mon Sep 17 00:00:00 2001 From: David Kay Date: Sat, 21 Mar 2026 19:46:38 -0500 Subject: [PATCH 3/5] fix(search): thread smart_case through daemon search and highlight paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The daemon gRPC service and Nucleo index were hardcoding CaseMatching::Smart, silently ignoring search.smart_case = false. Now the setting propagates through SearchComponent → SearchGrpcService → SearchIndex::search and through the daemon highlight path. --- crates/atuin-daemon/src/components/search.rs | 12 +++++++++++- crates/atuin-daemon/src/search/index.rs | 11 +++++++++-- .../src/command/client/search/engines/daemon.rs | 7 ++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/atuin-daemon/src/components/search.rs b/crates/atuin-daemon/src/components/search.rs index 9fc87faecf7..33a82eecfcd 100644 --- a/crates/atuin-daemon/src/components/search.rs +++ b/crates/atuin-daemon/src/components/search.rs @@ -39,6 +39,8 @@ pub struct SearchComponent { handle: tokio::sync::RwLock>, loader_handle: Option>, frecency_handle: Option>, + /// Shared smart_case setting — updated on SettingsReloaded, read by the gRPC service. + smart_case: Arc>, } impl SearchComponent { @@ -49,6 +51,8 @@ impl SearchComponent { handle: tokio::sync::RwLock::new(None), loader_handle: None, frecency_handle: None, + // Default true (smart-case on) — overwritten when settings load. + smart_case: Arc::new(tokio::sync::RwLock::new(true)), } } @@ -56,6 +60,7 @@ impl SearchComponent { pub fn grpc_service(&self) -> SearchServer { SearchServer::new(SearchGrpcService { index: self.index.clone(), + smart_case: self.smart_case.clone(), }) } @@ -121,6 +126,7 @@ impl Component for SearchComponent { let index = self.index.clone(); let db = handle.history_db().clone(); let handle_for_loader = handle.clone(); + let smart_case_for_loader = self.smart_case.clone(); self.loader_handle = Some(tokio::spawn(async move { info!( @@ -144,6 +150,7 @@ impl Component for SearchComponent { ); // Build initial frecency map with current settings let settings = handle_for_loader.settings().await; + *smart_case_for_loader.write().await = settings.search.smart_case; index.read().await.rebuild_frecency(&settings.search).await; info!("Initial frecency map built"); break; @@ -244,6 +251,7 @@ impl Component for SearchComponent { let handle_guard = self.handle.read().await; if let Some(handle) = handle_guard.as_ref() { let settings = handle.settings().await; + *self.smart_case.write().await = settings.search.smart_case; self.index .read() .await @@ -275,6 +283,7 @@ impl Component for SearchComponent { /// The gRPC service implementation. pub struct SearchGrpcService { index: Arc>, + smart_case: Arc>, } #[tonic::async_trait] @@ -288,6 +297,7 @@ impl SearchSvc for SearchGrpcService { ) -> Result, Status> { let mut in_stream = request.into_inner(); let index = self.index.clone(); + let smart_case = self.smart_case.clone(); // Create output channel let (tx, rx) = tokio::sync::mpsc::channel::>(128); @@ -332,7 +342,7 @@ impl SearchSvc for SearchGrpcService { .in_scope(|| async { let index = index.read().await; index - .search(&query, index_filter, &query_context, RESULTS_LIMIT) + .search(&query, index_filter, &query_context, RESULTS_LIMIT, *smart_case.read().await) .await }) .await; diff --git a/crates/atuin-daemon/src/search/index.rs b/crates/atuin-daemon/src/search/index.rs index 3328c5b555b..b4bca3e1f80 100644 --- a/crates/atuin-daemon/src/search/index.rs +++ b/crates/atuin-daemon/src/search/index.rs @@ -337,6 +337,7 @@ impl SearchIndex { filter_mode: IndexFilterMode, _context: &QueryContext, limit: u32, + smart_case: bool, ) -> Vec { let mut nucleo = self.nucleo.write().await; @@ -352,10 +353,15 @@ impl SearchIndex { nucleo.set_scorer(scorer); // Update pattern + let case_matching = if smart_case { + pattern::CaseMatching::Smart + } else { + pattern::CaseMatching::Ignore + }; nucleo.pattern.reparse( 0, query, - pattern::CaseMatching::Smart, + case_matching, pattern::Normalization::Smart, false, ); @@ -661,7 +667,7 @@ mod tests { // Search for "git" - should match 2 commands let results = index - .search("git", IndexFilterMode::Global, &QueryContext::default(), 10) + .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, true) .await; assert_eq!(results.len(), 2); @@ -672,6 +678,7 @@ mod tests { IndexFilterMode::Directory(with_trailing_slash("/home/user/project")), &QueryContext::default(), 10, + true, ) .await; assert_eq!(results.len(), 2); // git status and git commit diff --git a/crates/atuin/src/command/client/search/engines/daemon.rs b/crates/atuin/src/command/client/search/engines/daemon.rs index a8407a6d7d0..2529e5c3ab7 100644 --- a/crates/atuin/src/command/client/search/engines/daemon.rs +++ b/crates/atuin/src/command/client/search/engines/daemon.rs @@ -201,7 +201,12 @@ impl SearchEngine for Search { } let mut matcher = Matcher::new(Config::DEFAULT); - let pattern = Pattern::parse(search_input, CaseMatching::Smart, Normalization::Smart); + let case_matching = if self.smart_case { + CaseMatching::Smart + } else { + CaseMatching::Ignore + }; + let pattern = Pattern::parse(search_input, case_matching, Normalization::Smart); let mut indices: Vec = Vec::new(); let mut haystack_buf = Vec::new(); From 20fa42523aa145cb3f9ff304fe4a2c4459a4928c Mon Sep 17 00:00:00 2001 From: David Kay Date: Sat, 21 Mar 2026 19:49:08 -0500 Subject: [PATCH 4/5] fix(search): apply smart_case on daemon startup, add index test SearchComponent::new() hardcoded smart_case=true. If the user had smart_case=false in config, searches before the history loader finished (potentially minutes on large histories) would use the wrong case mode. Now settings are read at the top of start() before spawning the loader. Also adds a dedicated test exercising smart_case=true vs false through the daemon search index (Nucleo CaseMatching::Smart vs Ignore). --- crates/atuin-daemon/src/components/search.rs | 5 ++ crates/atuin-daemon/src/search/index.rs | 49 ++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/crates/atuin-daemon/src/components/search.rs b/crates/atuin-daemon/src/components/search.rs index 33a82eecfcd..271b163ffb3 100644 --- a/crates/atuin-daemon/src/components/search.rs +++ b/crates/atuin-daemon/src/components/search.rs @@ -122,6 +122,11 @@ impl Component for SearchComponent { async fn start(&mut self, handle: DaemonHandle) -> Result<()> { *self.handle.write().await = Some(handle.clone()); + // Apply user's smart_case setting immediately so searches before + // the history loader finishes use the correct case mode. + let settings = handle.settings().await; + *self.smart_case.write().await = settings.search.smart_case; + // Spawn background task to load history into index let index = self.index.clone(); let db = handle.history_db().clone(); diff --git a/crates/atuin-daemon/src/search/index.rs b/crates/atuin-daemon/src/search/index.rs index b4bca3e1f80..df1ec329a4f 100644 --- a/crates/atuin-daemon/src/search/index.rs +++ b/crates/atuin-daemon/src/search/index.rs @@ -683,4 +683,53 @@ mod tests { .await; assert_eq!(results.len(), 2); // git status and git commit } + + #[tokio::test] + async fn search_index_smart_case() { + let index = SearchIndex::new(); + + let h1 = make_history( + "Git status", + "/home/user/project", + datetime!(2024-01-01 10:00 UTC), + ); + let h2 = make_history( + "git commit -m 'test'", + "/home/user/project", + datetime!(2024-01-01 10:05 UTC), + ); + let h3 = make_history( + "GIT PUSH", + "/home/user/project", + datetime!(2024-01-01 10:10 UTC), + ); + + index.add_history(&h1); + index.add_history(&h2); + index.add_history(&h3); + + // smart_case=true: uppercase query "Git" should be case-sensitive, + // matching only "Git status" (not "git commit" or "GIT PUSH") + let results = index + .search("Git", IndexFilterMode::Global, &QueryContext::default(), 10, true) + .await; + assert_eq!(results.len(), 1); + + // smart_case=false: same uppercase query "Git" should match + // case-insensitively, finding all three commands + let results = index + .search("Git", IndexFilterMode::Global, &QueryContext::default(), 10, false) + .await; + assert_eq!(results.len(), 3); + + // lowercase query matches all regardless of smart_case setting + let results_smart = index + .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, true) + .await; + let results_ignore = index + .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, false) + .await; + assert_eq!(results_smart.len(), 3); + assert_eq!(results_ignore.len(), 3); + } } From ead705f8b0bab16ce903368b0a31dd5aa5e53e1b Mon Sep 17 00:00:00 2001 From: David Kay Date: Sat, 21 Mar 2026 20:18:40 -0500 Subject: [PATCH 5/5] style: rustfmt line wrapping for search calls --- crates/atuin-daemon/src/components/search.rs | 8 +++- crates/atuin-daemon/src/search/index.rs | 40 +++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/atuin-daemon/src/components/search.rs b/crates/atuin-daemon/src/components/search.rs index 271b163ffb3..de9b32bcb1c 100644 --- a/crates/atuin-daemon/src/components/search.rs +++ b/crates/atuin-daemon/src/components/search.rs @@ -347,7 +347,13 @@ impl SearchSvc for SearchGrpcService { .in_scope(|| async { let index = index.read().await; index - .search(&query, index_filter, &query_context, RESULTS_LIMIT, *smart_case.read().await) + .search( + &query, + index_filter, + &query_context, + RESULTS_LIMIT, + *smart_case.read().await, + ) .await }) .await; diff --git a/crates/atuin-daemon/src/search/index.rs b/crates/atuin-daemon/src/search/index.rs index df1ec329a4f..3eb1898c41b 100644 --- a/crates/atuin-daemon/src/search/index.rs +++ b/crates/atuin-daemon/src/search/index.rs @@ -667,7 +667,13 @@ mod tests { // Search for "git" - should match 2 commands let results = index - .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, true) + .search( + "git", + IndexFilterMode::Global, + &QueryContext::default(), + 10, + true, + ) .await; assert_eq!(results.len(), 2); @@ -711,23 +717,47 @@ mod tests { // smart_case=true: uppercase query "Git" should be case-sensitive, // matching only "Git status" (not "git commit" or "GIT PUSH") let results = index - .search("Git", IndexFilterMode::Global, &QueryContext::default(), 10, true) + .search( + "Git", + IndexFilterMode::Global, + &QueryContext::default(), + 10, + true, + ) .await; assert_eq!(results.len(), 1); // smart_case=false: same uppercase query "Git" should match // case-insensitively, finding all three commands let results = index - .search("Git", IndexFilterMode::Global, &QueryContext::default(), 10, false) + .search( + "Git", + IndexFilterMode::Global, + &QueryContext::default(), + 10, + false, + ) .await; assert_eq!(results.len(), 3); // lowercase query matches all regardless of smart_case setting let results_smart = index - .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, true) + .search( + "git", + IndexFilterMode::Global, + &QueryContext::default(), + 10, + true, + ) .await; let results_ignore = index - .search("git", IndexFilterMode::Global, &QueryContext::default(), 10, false) + .search( + "git", + IndexFilterMode::Global, + &QueryContext::default(), + 10, + false, + ) .await; assert_eq!(results_smart.len(), 3); assert_eq!(results_ignore.len(), 3);