From 5118657fbd36d5b507b20aa0e7904d0fddbe8f6e Mon Sep 17 00:00:00 2001 From: gustav-fff <286169375+gustav-fff@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:07:12 -0700 Subject: [PATCH 1/5] feat(grep): add CaseMode enum and case_mode option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds explicit `CaseMode { Smart, Sensitive, Insensitive }` and a new `case_mode: Option` field on `GrepSearchOptions`. The legacy `smart_case: bool` is preserved for backward compatibility — when `case_mode` is `None`, the boolean is used (true => Smart, false => Sensitive). New callers that need `Insensitive` set `case_mode` explicitly. Co-authored-by: Marko Vejnovic --- crates/fff-core/src/grep.rs | 54 ++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/crates/fff-core/src/grep.rs b/crates/fff-core/src/grep.rs index 5c73fa5b..370a8bfc 100644 --- a/crates/fff-core/src/grep.rs +++ b/crates/fff-core/src/grep.rs @@ -228,6 +228,14 @@ fn replace_unescaped_newline_escapes(text: &str) -> String { String::from_utf8(result).unwrap_or_else(|_| text.to_string()) } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum CaseMode { + #[default] + Smart, + Sensitive, + Insensitive, +} + /// Controls how the grep pattern is interpreted. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum GrepMode { @@ -335,6 +343,10 @@ pub struct GrepSearchOptions { pub max_file_size: u64, pub max_matches_per_file: usize, pub smart_case: bool, + /// Explicit case mode. When `Some`, overrides `smart_case`. When `None`, + /// `smart_case` is used (true => Smart, false => Sensitive). Non-breaking + /// addition; existing callers can ignore this field. + pub case_mode: Option, /// File-based pagination offset: index into the sorted/filtered file list /// to start searching from. Pass 0 for the first page, then use /// `GrepResult::next_file_offset` for subsequent pages. @@ -364,12 +376,25 @@ pub struct GrepSearchOptions { pub abort_signal: Option>, } +impl GrepSearchOptions { + /// Resolves the effective case mode, preferring explicit `case_mode` over + /// the legacy `smart_case` boolean. + pub fn effective_case_mode(&self) -> CaseMode { + match self.case_mode { + Some(m) => m, + None if self.smart_case => CaseMode::Smart, + None => CaseMode::Sensitive, + } + } +} + impl Default for GrepSearchOptions { fn default() -> Self { Self { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 50, mode: GrepMode::default(), @@ -1045,11 +1070,10 @@ pub(crate) fn multi_grep_search<'a>( }; } - // Smart case: case-insensitive when all patterns are lowercase - let case_insensitive = if options.smart_case { - !patterns.iter().any(|p| p.chars().any(|c| c.is_uppercase())) - } else { - false + let case_insensitive = match options.effective_case_mode() { + CaseMode::Smart => !patterns.iter().any(|p| p.chars().any(|c| c.is_uppercase())), + CaseMode::Insensitive => true, + CaseMode::Sensitive => false, }; let ac = aho_corasick::AhoCorasickBuilder::new() @@ -1117,7 +1141,7 @@ const fn is_utf8_char_boundary(b: u8) -> bool { /// - The input is passed directly to the regex engine without escaping /// - Smart case still applies /// - Returns `None` for invalid regex patterns — the caller falls back to literal mode -fn build_regex(pattern: &str, smart_case: bool) -> Result { +fn build_regex(pattern: &str, case_mode: CaseMode) -> Result { if pattern.is_empty() { return Err("empty pattern".to_string()); } @@ -1128,10 +1152,10 @@ fn build_regex(pattern: &str, smart_case: bool) -> Result !pattern.chars().any(|c| c.is_uppercase()), + CaseMode::Insensitive => true, + CaseMode::Sensitive => false, }; regex::bytes::RegexBuilder::new(®ex_pattern) @@ -1995,10 +2019,10 @@ pub(crate) fn grep_search<'a>( }; } - let case_insensitive = if options.smart_case { - !grep_text.chars().any(|c| c.is_uppercase()) - } else { - false + let case_insensitive = match options.effective_case_mode() { + CaseMode::Smart => !grep_text.chars().any(|c| c.is_uppercase()), + CaseMode::Insensitive => true, + CaseMode::Sensitive => false, }; let mut regex_fallback_error: Option = None; @@ -2089,7 +2113,7 @@ pub(crate) fn grep_search<'a>( overflow_arena, ); } - GrepMode::Regex => build_regex(&grep_text, options.smart_case) + GrepMode::Regex => build_regex(&grep_text, options.effective_case_mode()) .inspect_err(|err| { tracing::warn!("Regex compilation failed for {}. Error {}", grep_text, err); From 2ecd02d54d9d7d95f31ddf68aae7143a42526153 Mon Sep 17 00:00:00 2001 From: gustav-fff <286169375+gustav-fff@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:55:10 -0700 Subject: [PATCH 2/5] feat(case_mode): wire through workspace and expose to neovim Add case_mode: None to all GrepSearchOptions construction sites across fff-c, fff-mcp, fff-nvim, fff-python, and tests so the new field compiles everywhere. Expose case_mode in the neovim lua API: new optional config grep.case_mode ("smart"|"sensitive"|"insensitive") flows through live_grep FFI and content_search opts. smart_case remains the legacy default; case_mode overrides it when set. --- crates/fff-c/src/lib.rs | 2 ++ crates/fff-core/src/grep.rs | 2 ++ crates/fff-core/tests/bigram_overlay_coherence_test.rs | 1 + crates/fff-core/tests/bigram_overlay_integration.rs | 1 + crates/fff-core/tests/fuzz_file_operations.rs | 1 + crates/fff-core/tests/fuzz_git_watcher_stress.rs | 3 +++ crates/fff-core/tests/fuzz_real_repos.rs | 1 + crates/fff-core/tests/grep_integration.rs | 3 +++ crates/fff-core/tests/new_directory_watcher_test.rs | 1 + .../fff-core/tests/path_separator_constraint_test.rs | 1 + crates/fff-core/tests/real_binary_fixtures.rs | 1 + crates/fff-mcp/src/server.rs | 1 + crates/fff-nvim/benches/fuzzy_search_bench.rs | 1 + crates/fff-nvim/benches/grep_bench.rs | 1 + crates/fff-nvim/src/bin/bench_grep_query.rs | 1 + crates/fff-nvim/src/bin/fuzzy_grep_test.rs | 1 + crates/fff-nvim/src/bin/grep_profiler.rs | 2 ++ crates/fff-nvim/src/bin/grep_vs_rg.rs | 2 ++ crates/fff-nvim/src/lib.rs | 10 ++++++++++ crates/fff-python/src/finder.rs | 1 + lua/fff/conf.lua | 4 +++- lua/fff/main.lua | 2 ++ lua/fff/picker_ui/grep_renderer.lua | 3 ++- 23 files changed, 44 insertions(+), 2 deletions(-) diff --git a/crates/fff-c/src/lib.rs b/crates/fff-c/src/lib.rs index 0e0d0c91..cdbcb710 100644 --- a/crates/fff-c/src/lib.rs +++ b/crates/fff-c/src/lib.rs @@ -737,6 +737,7 @@ pub unsafe extern "C" fn fff_live_grep( max_file_size: default_u64(max_file_size, 10 * 1024 * 1024), max_matches_per_file: max_matches_per_file as usize, smart_case, + case_mode: None, file_offset: file_offset as usize, page_limit: default_u32(page_limit, 50) as usize, mode: grep_mode_from_u8(mode), @@ -840,6 +841,7 @@ pub unsafe extern "C" fn fff_multi_grep( max_file_size: default_u64(max_file_size, 10 * 1024 * 1024), max_matches_per_file: max_matches_per_file as usize, smart_case, + case_mode: None, file_offset: file_offset as usize, page_limit: default_u32(page_limit, 50) as usize, mode: fff::GrepMode::PlainText, // ignored by multi_grep_search diff --git a/crates/fff-core/src/grep.rs b/crates/fff-core/src/grep.rs index 370a8bfc..9960e814 100644 --- a/crates/fff-core/src/grep.rs +++ b/crates/fff-core/src/grep.rs @@ -2469,6 +2469,7 @@ mod tests { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 100, mode: super::GrepMode::PlainText, @@ -2653,6 +2654,7 @@ mod tests { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 100, mode: super::GrepMode::PlainText, diff --git a/crates/fff-core/tests/bigram_overlay_coherence_test.rs b/crates/fff-core/tests/bigram_overlay_coherence_test.rs index 6cee64e4..c5429516 100644 --- a/crates/fff-core/tests/bigram_overlay_coherence_test.rs +++ b/crates/fff-core/tests/bigram_overlay_coherence_test.rs @@ -1310,6 +1310,7 @@ fn grep_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, diff --git a/crates/fff-core/tests/bigram_overlay_integration.rs b/crates/fff-core/tests/bigram_overlay_integration.rs index c663acf8..da9d7730 100644 --- a/crates/fff-core/tests/bigram_overlay_integration.rs +++ b/crates/fff-core/tests/bigram_overlay_integration.rs @@ -369,6 +369,7 @@ fn grep_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, diff --git a/crates/fff-core/tests/fuzz_file_operations.rs b/crates/fff-core/tests/fuzz_file_operations.rs index e39a6906..7dc5e537 100644 --- a/crates/fff-core/tests/fuzz_file_operations.rs +++ b/crates/fff-core/tests/fuzz_file_operations.rs @@ -627,6 +627,7 @@ fn grep_plain_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, diff --git a/crates/fff-core/tests/fuzz_git_watcher_stress.rs b/crates/fff-core/tests/fuzz_git_watcher_stress.rs index f7ca1e80..673253bd 100644 --- a/crates/fff-core/tests/fuzz_git_watcher_stress.rs +++ b/crates/fff-core/tests/fuzz_git_watcher_stress.rs @@ -892,6 +892,7 @@ fn grep_plain_matches(shared: &SharedFilePicker, query: &str) -> Vec { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, @@ -929,6 +930,7 @@ fn grep_fuzzy_matches(shared: &SharedFilePicker, query: &str) -> Vec { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::Fuzzy, @@ -961,6 +963,7 @@ fn grep_regex_matches(shared: &SharedFilePicker, query: &str) -> Vec { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::Regex, diff --git a/crates/fff-core/tests/fuzz_real_repos.rs b/crates/fff-core/tests/fuzz_real_repos.rs index f83a0f52..e6aa978d 100644 --- a/crates/fff-core/tests/fuzz_real_repos.rs +++ b/crates/fff-core/tests/fuzz_real_repos.rs @@ -227,6 +227,7 @@ fn grep_opts(mode: GrepMode) -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode, diff --git a/crates/fff-core/tests/grep_integration.rs b/crates/fff-core/tests/grep_integration.rs index 2765de3c..423b2c5c 100644 --- a/crates/fff-core/tests/grep_integration.rs +++ b/crates/fff-core/tests/grep_integration.rs @@ -32,6 +32,7 @@ fn plain_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, @@ -50,6 +51,7 @@ fn regex_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::Regex, @@ -68,6 +70,7 @@ fn fuzzy_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::Fuzzy, diff --git a/crates/fff-core/tests/new_directory_watcher_test.rs b/crates/fff-core/tests/new_directory_watcher_test.rs index 9cb9fc11..3c1878e8 100644 --- a/crates/fff-core/tests/new_directory_watcher_test.rs +++ b/crates/fff-core/tests/new_directory_watcher_test.rs @@ -133,6 +133,7 @@ fn grep_plain_count(picker: &FilePicker, query: &str) -> usize { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, diff --git a/crates/fff-core/tests/path_separator_constraint_test.rs b/crates/fff-core/tests/path_separator_constraint_test.rs index 9519ef83..a26c44d6 100644 --- a/crates/fff-core/tests/path_separator_constraint_test.rs +++ b/crates/fff-core/tests/path_separator_constraint_test.rs @@ -37,6 +37,7 @@ fn plain_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, diff --git a/crates/fff-core/tests/real_binary_fixtures.rs b/crates/fff-core/tests/real_binary_fixtures.rs index d7f383b6..f4de9af8 100644 --- a/crates/fff-core/tests/real_binary_fixtures.rs +++ b/crates/fff-core/tests/real_binary_fixtures.rs @@ -31,6 +31,7 @@ fn plain_opts() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, diff --git a/crates/fff-mcp/src/server.rs b/crates/fff-mcp/src/server.rs index 0fc6102a..22a87e4c 100644 --- a/crates/fff-mcp/src/server.rs +++ b/crates/fff-mcp/src/server.rs @@ -67,6 +67,7 @@ fn make_grep_options( max_file_size: 10 * 1024 * 1024, max_matches_per_file: matches_per_file, smart_case: true, + case_mode: None, file_offset, page_limit: 50, mode, diff --git a/crates/fff-nvim/benches/fuzzy_search_bench.rs b/crates/fff-nvim/benches/fuzzy_search_bench.rs index 8a1fcd57..12450aea 100644 --- a/crates/fff-nvim/benches/fuzzy_search_bench.rs +++ b/crates/fff-nvim/benches/fuzzy_search_bench.rs @@ -628,6 +628,7 @@ fn bench_grep_search(c: &mut Criterion) { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 0, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 100, mode: GrepMode::PlainText, diff --git a/crates/fff-nvim/benches/grep_bench.rs b/crates/fff-nvim/benches/grep_bench.rs index 64939946..529b3bcd 100644 --- a/crates/fff-nvim/benches/grep_bench.rs +++ b/crates/fff-nvim/benches/grep_bench.rs @@ -111,6 +111,7 @@ fn plain_options() -> GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 50, mode: GrepMode::PlainText, diff --git a/crates/fff-nvim/src/bin/bench_grep_query.rs b/crates/fff-nvim/src/bin/bench_grep_query.rs index 063d0816..b9c70609 100644 --- a/crates/fff-nvim/src/bin/bench_grep_query.rs +++ b/crates/fff-nvim/src/bin/bench_grep_query.rs @@ -23,6 +23,7 @@ fn run_grep(picker: &FilePicker, query: &str, iters: usize) { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: usize::MAX, mode: GrepMode::PlainText, diff --git a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs index e3bceac8..5b1b1a9b 100644 --- a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs +++ b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs @@ -28,6 +28,7 @@ fn run_fuzzy_query(picker: &FilePicker, query: &str, label: &str) { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 100, mode: GrepMode::Fuzzy, diff --git a/crates/fff-nvim/src/bin/grep_profiler.rs b/crates/fff-nvim/src/bin/grep_profiler.rs index 4c0386b2..186999ae 100644 --- a/crates/fff-nvim/src/bin/grep_profiler.rs +++ b/crates/fff-nvim/src/bin/grep_profiler.rs @@ -89,6 +89,7 @@ impl<'a> GrepBench<'a> { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 50, mode, @@ -410,6 +411,7 @@ fn main() { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset, page_limit: 50, mode: Default::default(), diff --git a/crates/fff-nvim/src/bin/grep_vs_rg.rs b/crates/fff-nvim/src/bin/grep_vs_rg.rs index f202fc1f..eb278d16 100644 --- a/crates/fff-nvim/src/bin/grep_vs_rg.rs +++ b/crates/fff-nvim/src/bin/grep_vs_rg.rs @@ -163,6 +163,7 @@ fn run_fff_full(picker: &FilePicker, query: &str) -> (usize, Duration) { max_file_size: 10 * 1024 * 1024, max_matches_per_file: usize::MAX, smart_case: true, + case_mode: None, file_offset: 0, page_limit: usize::MAX, mode: Default::default(), @@ -186,6 +187,7 @@ fn run_fff_page(picker: &FilePicker, query: &str) -> (usize, Duration) { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, smart_case: true, + case_mode: None, file_offset: 0, page_limit: 50, mode: Default::default(), diff --git a/crates/fff-nvim/src/lib.rs b/crates/fff-nvim/src/lib.rs index a9f3cb2d..25dccdde 100644 --- a/crates/fff-nvim/src/lib.rs +++ b/crates/fff-nvim/src/lib.rs @@ -436,6 +436,7 @@ pub fn live_grep( grep_mode, time_budget_ms, trim_whitespace, + case_mode, ): ( String, Option, @@ -446,6 +447,7 @@ pub fn live_grep( Option, Option, Option, + Option, ), ) -> LuaResult { let file_picker_guard = FILE_PICKER.read().into_lua_result()?; @@ -460,10 +462,18 @@ pub fn live_grep( _ => fff::GrepMode::PlainText, // "plain" or nil or unknown }; + let case_mode = match case_mode.as_deref() { + Some("smart") => Some(fff::CaseMode::Smart), + Some("sensitive") => Some(fff::CaseMode::Sensitive), + Some("insensitive") => Some(fff::CaseMode::Insensitive), + _ => None, + }; + let options = fff::GrepSearchOptions { max_file_size: max_file_size.unwrap_or(10 * 1024 * 1024), max_matches_per_file: max_matches_per_file.unwrap_or(200), smart_case: smart_case.unwrap_or(true), + case_mode, file_offset: file_offset.unwrap_or(0), page_limit: page_size.unwrap_or(50), mode, diff --git a/crates/fff-python/src/finder.rs b/crates/fff-python/src/finder.rs index e61232fc..d12d575f 100644 --- a/crates/fff-python/src/finder.rs +++ b/crates/fff-python/src/finder.rs @@ -91,6 +91,7 @@ fn grep_options( max_file_size: defaulted_u64(max_file_size, defaults.max_file_size), max_matches_per_file: max_matches_per_file as usize, smart_case, + case_mode: None, file_offset: cursor_offset, page_limit: defaulted_usize(page_limit, defaults.page_limit), mode, diff --git a/lua/fff/conf.lua b/lua/fff/conf.lua index dcb53e01..d5c3d968 100644 --- a/lua/fff/conf.lua +++ b/lua/fff/conf.lua @@ -56,6 +56,7 @@ local M = {} --- @field max_file_size number --- @field max_matches_per_file number --- @field smart_case boolean +--- @field case_mode? "smart"|"sensitive"|"insensitive" --- @field time_budget_ms number --- @field modes string[] --- @field trim_whitespace boolean @@ -414,7 +415,8 @@ local function init() grep = { max_file_size = 10 * 1024 * 1024, -- Skip files larger than 10MB max_matches_per_file = 100, -- Maximum matches per file (set 0 to unlimited) - smart_case = true, -- Case-insensitive unless query has uppercase + smart_case = true, -- Case-insensitive unless query has uppercase (legacy; prefer case_mode) + case_mode = nil, -- Optional: "smart" | "sensitive" | "insensitive". Overrides smart_case when set. time_budget_ms = 150, -- Max search time in ms per call (prevents UI freeze, 0 = no limit) modes = { 'plain', 'regex', 'fuzzy' }, -- Available grep modes and their cycling order trim_whitespace = false, -- Strip leading whitespace from matched lines (useful for cleaner display) diff --git a/lua/fff/main.lua b/lua/fff/main.lua index 1418a2d7..59eee551 100644 --- a/lua/fff/main.lua +++ b/lua/fff/main.lua @@ -302,6 +302,7 @@ end --- @field max_file_size? number Skip files larger than N bytes (default: config.grep.max_file_size). --- @field max_matches_per_file? number Cap matches per file, 0 = unlimited (default: config.grep.max_matches_per_file). --- @field smart_case? boolean Case-insensitive when query is all lowercase (default: config.grep.smart_case). +--- @field case_mode? "smart"|"sensitive"|"insensitive" Explicit case mode (default: config.grep.case_mode). Overrides smart_case. --- @field page_size? number Max matches returned (default: 50). --- @field file_offset? number File-based pagination offset (default: 0). --- @field time_budget_ms? number Max wall-clock time, 0 = unlimited (default: config.grep.time_budget_ms). @@ -348,6 +349,7 @@ function M.content_search(query, opts) max_file_size = opts.max_file_size or grep_cfg.max_file_size, max_matches_per_file = opts.max_matches_per_file or grep_cfg.max_matches_per_file, smart_case = opts.smart_case == nil and grep_cfg.smart_case or opts.smart_case, + case_mode = opts.case_mode == nil and grep_cfg.case_mode or opts.case_mode, time_budget_ms = opts.time_budget_ms or grep_cfg.time_budget_ms, trim_whitespace = opts.trim_whitespace == nil and grep_cfg.trim_whitespace or opts.trim_whitespace, } diff --git a/lua/fff/picker_ui/grep_renderer.lua b/lua/fff/picker_ui/grep_renderer.lua index ef022afc..db20acc7 100644 --- a/lua/fff/picker_ui/grep_renderer.lua +++ b/lua/fff/picker_ui/grep_renderer.lua @@ -40,7 +40,8 @@ function M.search(query, file_offset, page_size, config, grep_mode) conf.smart_case, grep_mode or 'plain', conf.time_budget_ms, - conf.trim_whitespace + conf.trim_whitespace, + conf.case_mode ) return last_result end From 9e499906baf4a20589b2139290324bb388939288 Mon Sep 17 00:00:00 2001 From: gustav-fff <286169375+gustav-fff@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:35:36 -0700 Subject: [PATCH 3/5] refactor(grep): deprecate smart_case, make effective_case_mode private, shrink doc --- crates/fff-core/src/grep.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/fff-core/src/grep.rs b/crates/fff-core/src/grep.rs index 9960e814..3f4628aa 100644 --- a/crates/fff-core/src/grep.rs +++ b/crates/fff-core/src/grep.rs @@ -342,10 +342,9 @@ pub use crate::constants::MAX_FFFILE_SIZE; pub struct GrepSearchOptions { pub max_file_size: u64, pub max_matches_per_file: usize, + #[deprecated(note = "use `case_mode` instead")] pub smart_case: bool, - /// Explicit case mode. When `Some`, overrides `smart_case`. When `None`, - /// `smart_case` is used (true => Smart, false => Sensitive). Non-breaking - /// addition; existing callers can ignore this field. + /// When `Some`, overrides `smart_case`. pub case_mode: Option, /// File-based pagination offset: index into the sorted/filtered file list /// to start searching from. Pass 0 for the first page, then use @@ -377,9 +376,8 @@ pub struct GrepSearchOptions { } impl GrepSearchOptions { - /// Resolves the effective case mode, preferring explicit `case_mode` over - /// the legacy `smart_case` boolean. - pub fn effective_case_mode(&self) -> CaseMode { + fn effective_case_mode(&self) -> CaseMode { + #[allow(deprecated)] match self.case_mode { Some(m) => m, None if self.smart_case => CaseMode::Smart, From 19c501873399ebde041125d384035b4d020a32bb Mon Sep 17 00:00:00 2001 From: gustav-fff <286169375+gustav-fff@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:33:43 -0700 Subject: [PATCH 4/5] fix(grep): allow(deprecated) on legacy smart_case call sites Tests, benches, FFI bindings, and the MCP server still construct GrepSearchOptions with the legacy smart_case field. Suppress the deprecation lint per file/site so RUSTFLAGS=-D warnings stays clean. --- crates/fff-c/src/lib.rs | 1 + crates/fff-core/src/grep.rs | 3 +++ crates/fff-core/tests/bigram_overlay_coherence_test.rs | 3 ++- crates/fff-core/tests/bigram_overlay_integration.rs | 3 ++- crates/fff-core/tests/fuzz_file_operations.rs | 3 ++- crates/fff-core/tests/fuzz_git_watcher_stress.rs | 3 ++- crates/fff-core/tests/fuzz_real_repos.rs | 3 ++- crates/fff-core/tests/grep_integration.rs | 3 ++- crates/fff-core/tests/new_directory_watcher_test.rs | 3 ++- crates/fff-core/tests/path_separator_constraint_test.rs | 3 ++- crates/fff-core/tests/real_binary_fixtures.rs | 3 ++- crates/fff-mcp/src/server.rs | 1 + crates/fff-nvim/benches/fuzzy_search_bench.rs | 3 ++- crates/fff-nvim/benches/grep_bench.rs | 3 ++- crates/fff-nvim/src/bin/bench_grep_query.rs | 3 ++- crates/fff-nvim/src/bin/fuzzy_grep_test.rs | 3 ++- crates/fff-nvim/src/bin/grep_profiler.rs | 3 ++- crates/fff-nvim/src/bin/grep_vs_rg.rs | 3 ++- crates/fff-nvim/src/lib.rs | 1 + crates/fff-python/src/finder.rs | 1 + 20 files changed, 37 insertions(+), 15 deletions(-) diff --git a/crates/fff-c/src/lib.rs b/crates/fff-c/src/lib.rs index cdbcb710..9df732f6 100644 --- a/crates/fff-c/src/lib.rs +++ b/crates/fff-c/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! C FFI bindings for fff-core //! //! This crate provides C-compatible FFI exports that can be used from any language diff --git a/crates/fff-core/src/grep.rs b/crates/fff-core/src/grep.rs index 3f4628aa..29ea85da 100644 --- a/crates/fff-core/src/grep.rs +++ b/crates/fff-core/src/grep.rs @@ -388,6 +388,7 @@ impl GrepSearchOptions { impl Default for GrepSearchOptions { fn default() -> Self { + #[allow(deprecated)] Self { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 200, @@ -2463,6 +2464,7 @@ mod tests { let files = picker.get_files(); let arena = picker.arena_base_ptr(); + #[allow(deprecated)] let options = super::GrepSearchOptions { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, @@ -2648,6 +2650,7 @@ mod tests { // Run a grep for "unicorn": six files match // (a, b, c in base + f, g, h in overflow). let query = super::parse_grep_query("unicorn"); + #[allow(deprecated)] let options = super::GrepSearchOptions { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, diff --git a/crates/fff-core/tests/bigram_overlay_coherence_test.rs b/crates/fff-core/tests/bigram_overlay_coherence_test.rs index c5429516..a93701d8 100644 --- a/crates/fff-core/tests/bigram_overlay_coherence_test.rs +++ b/crates/fff-core/tests/bigram_overlay_coherence_test.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Stress tests for the bigram overlay layer. //! //! These tests create a real git repository with many files, build the bigram @@ -1654,4 +1655,4 @@ fn bigram_overlay_coherence_fuzzy_grep_finds_overflow_files() { } stop_picker(&shared_picker); -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/bigram_overlay_integration.rs b/crates/fff-core/tests/bigram_overlay_integration.rs index da9d7730..8ee12b7f 100644 --- a/crates/fff-core/tests/bigram_overlay_integration.rs +++ b/crates/fff-core/tests/bigram_overlay_integration.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Integration test: verify that modifying a file after the bigram index is built //! still makes the new content findable via grep (through the overlay layer). @@ -408,4 +409,4 @@ fn wait_for_bigram(shared_picker: &SharedFilePicker) { "Timed out waiting for bigram build" ); } -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/fuzz_file_operations.rs b/crates/fff-core/tests/fuzz_file_operations.rs index 7dc5e537..b320d71c 100644 --- a/crates/fff-core/tests/fuzz_file_operations.rs +++ b/crates/fff-core/tests/fuzz_file_operations.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Randomized file-system mutation stress test. //! //! Seeds a directory with ~40 files across diverse content domains, builds the @@ -858,4 +859,4 @@ fn drop_during_post_scan_does_not_crash() { The test is not exercising the race. ({caught_active}/10)" ); eprintln!("Caught post-scan active in {caught_active}/10 rounds"); -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/fuzz_git_watcher_stress.rs b/crates/fff-core/tests/fuzz_git_watcher_stress.rs index 673253bd..0184142e 100644 --- a/crates/fff-core/tests/fuzz_git_watcher_stress.rs +++ b/crates/fff-core/tests/fuzz_git_watcher_stress.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Vibe coded stress test: randomized file + git operations driven against the *real* //! `BackgroundWatcher`, asserting the git-status invariant after every mutation. //! @@ -1610,4 +1611,4 @@ fn expect_file_status( } std::thread::sleep(CONVERGE_POLL); } -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/fuzz_real_repos.rs b/crates/fff-core/tests/fuzz_real_repos.rs index e6aa978d..9a6fd803 100644 --- a/crates/fff-core/tests/fuzz_real_repos.rs +++ b/crates/fff-core/tests/fuzz_real_repos.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Proptest-driven fuzz test against real GitHub repos with a live watcher. //! //! Clones real repository, runs the simulated close to real user sereies of file system ewvents and @@ -738,4 +739,4 @@ proptest! { fn fuzz_real_repos_proptest(ops in ops_strategy()) { run_scenario(&ops); } -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/grep_integration.rs b/crates/fff-core/tests/grep_integration.rs index 423b2c5c..fc2835b8 100644 --- a/crates/fff-core/tests/grep_integration.rs +++ b/crates/fff-core/tests/grep_integration.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use std::fs; use std::path::Path; use tempfile::TempDir; @@ -1837,4 +1838,4 @@ fn plain_text_smart_case_finds_uppercase_content_with_lowercase_query() { 1, "lowercase query should case-insensitively match 'VFIO-KVM'" ); -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/new_directory_watcher_test.rs b/crates/fff-core/tests/new_directory_watcher_test.rs index 3c1878e8..0522cd3d 100644 --- a/crates/fff-core/tests/new_directory_watcher_test.rs +++ b/crates/fff-core/tests/new_directory_watcher_test.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Integration test: verifying that the background watcher dynamically detects //! newly created directories and picks up files written inside them. //! @@ -537,4 +538,4 @@ fn gitignored_new_directory_excluded() { let grep_count = grep_plain_count(picker, "IGNORED_BUILD_TOKEN"); assert_eq!(grep_count, 0, "Gitignored content should NOT be grepable"); } -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/path_separator_constraint_test.rs b/crates/fff-core/tests/path_separator_constraint_test.rs index a26c44d6..a07aaf4a 100644 --- a/crates/fff-core/tests/path_separator_constraint_test.rs +++ b/crates/fff-core/tests/path_separator_constraint_test.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Regression test for https://github.com/dmtrKovalenko/fff/issues/381 //! //! Directory (`PathSegment`) and file-path (`FilePath`) constraints must @@ -246,4 +247,4 @@ fn fuzzy_search_with_glob_constraint_matches_on_windows_paths() { results.iter().any(|p| p.contains("handler.lua")), "glob `**/src/**/*.lua` must match services/handler.lua, got {results:?}" ); -} +} \ No newline at end of file diff --git a/crates/fff-core/tests/real_binary_fixtures.rs b/crates/fff-core/tests/real_binary_fixtures.rs index f4de9af8..231102a2 100644 --- a/crates/fff-core/tests/real_binary_fixtures.rs +++ b/crates/fff-core/tests/real_binary_fixtures.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] //! Real-world binary fixture regression. //! //! Reproduces the exact bug chain we hit with `codex_view` (4.5 MB ELF, no @@ -151,4 +152,4 @@ fn contains_subslice(haystack: &[u8], needle: &[u8]) -> bool { haystack .windows(needle.len()) .any(|window| window == needle) -} +} \ No newline at end of file diff --git a/crates/fff-mcp/src/server.rs b/crates/fff-mcp/src/server.rs index 22a87e4c..5e3d11fd 100644 --- a/crates/fff-mcp/src/server.rs +++ b/crates/fff-mcp/src/server.rs @@ -42,6 +42,7 @@ fn cleanup_fuzzy_query(s: &str) -> String { out } +#[allow(deprecated)] fn make_grep_options( output_mode: OutputMode, mode: GrepMode, diff --git a/crates/fff-nvim/benches/fuzzy_search_bench.rs b/crates/fff-nvim/benches/fuzzy_search_bench.rs index 12450aea..c1589ba7 100644 --- a/crates/fff-nvim/benches/fuzzy_search_bench.rs +++ b/crates/fff-nvim/benches/fuzzy_search_bench.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use fff::file_picker::{FFFMode, FilePicker}; use fff::types::PaginationArgs; @@ -673,4 +674,4 @@ criterion_group!( bench_grep_search, ); -criterion_main!(benches); +criterion_main!(benches); \ No newline at end of file diff --git a/crates/fff-nvim/benches/grep_bench.rs b/crates/fff-nvim/benches/grep_bench.rs index 529b3bcd..8bc3ae66 100644 --- a/crates/fff-nvim/benches/grep_bench.rs +++ b/crates/fff-nvim/benches/grep_bench.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use fff::file_picker::{FFFMode, FilePicker}; use fff::{ @@ -234,4 +235,4 @@ criterion_group!( bench_plain_no_index, ); -criterion_main!(benches); +criterion_main!(benches); \ No newline at end of file diff --git a/crates/fff-nvim/src/bin/bench_grep_query.rs b/crates/fff-nvim/src/bin/bench_grep_query.rs index b9c70609..b38e28e5 100644 --- a/crates/fff-nvim/src/bin/bench_grep_query.rs +++ b/crates/fff-nvim/src/bin/bench_grep_query.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] /// Single-query grep benchmark with bigram index profiling. /// /// Usage: @@ -136,4 +137,4 @@ fn main() { query, iters ); run_grep(&picker, query, iters); -} +} \ No newline at end of file diff --git a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs index 5b1b1a9b..31902db7 100644 --- a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs +++ b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] /// Fuzzy grep quality test against ~/dev/lightsource /// /// Runs queries through the fuzzy grep pipeline and prints results @@ -181,4 +182,4 @@ fn main() { } eprintln!("=== Done ==="); -} +} \ No newline at end of file diff --git a/crates/fff-nvim/src/bin/grep_profiler.rs b/crates/fff-nvim/src/bin/grep_profiler.rs index 186999ae..47b7dd67 100644 --- a/crates/fff-nvim/src/bin/grep_profiler.rs +++ b/crates/fff-nvim/src/bin/grep_profiler.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] /// Live grep benchmark profiler for fff.nvim /// /// Benchmarks the full grep pipeline against a large repository (Linux kernel). @@ -452,4 +453,4 @@ fn main() { eprintln!("\nDone. For perf profiling:"); eprintln!(" perf record -g --call-graph dwarf -F 999 ./target/release/grep_profiler"); eprintln!(" perf report --no-children"); -} +} \ No newline at end of file diff --git a/crates/fff-nvim/src/bin/grep_vs_rg.rs b/crates/fff-nvim/src/bin/grep_vs_rg.rs index eb278d16..a2027b90 100644 --- a/crates/fff-nvim/src/bin/grep_vs_rg.rs +++ b/crates/fff-nvim/src/bin/grep_vs_rg.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use fff::file_picker::FilePicker; /// FFF vs ripgrep comparison benchmark /// @@ -442,4 +443,4 @@ fn main() { eprintln!(" on EVERY invocation (= every keystroke in telescope/fzf-lua)."); eprintln!(" fff pays this cost once at startup, then searches from warm cached mmaps."); eprintln!(); -} +} \ No newline at end of file diff --git a/crates/fff-nvim/src/lib.rs b/crates/fff-nvim/src/lib.rs index 25dccdde..4efe5087 100644 --- a/crates/fff-nvim/src/lib.rs +++ b/crates/fff-nvim/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use crate::path_shortening::shorten_path_with_cache; use error::IntoLuaResult; use fff::file_picker::FilePicker; diff --git a/crates/fff-python/src/finder.rs b/crates/fff-python/src/finder.rs index d12d575f..5bcef303 100644 --- a/crates/fff-python/src/finder.rs +++ b/crates/fff-python/src/finder.rs @@ -1,3 +1,4 @@ +#![allow(deprecated)] use std::path::{Path, PathBuf}; use std::time::Duration; From 2ce2a51d5a1f7406a027adad76b570de336d69ca Mon Sep 17 00:00:00 2001 From: gustav-fff <286169375+gustav-fff@users.noreply.github.com> Date: Thu, 18 Jun 2026 11:16:55 -0700 Subject: [PATCH 5/5] refactor(grep): switch internal call sites to case_mode Convert all in-tree GrepSearchOptions constructions to set case_mode: Some(...) instead of the deprecated smart_case bool field. FFI boundaries (fff-c, fff-nvim, fff-python) still accept smart_case bool from external callers but translate to CaseMode before constructing the options struct. Removes the blanket #[allow(deprecated)] attrs added in 19c5018; only Default for GrepSearchOptions retains it since it must initialize the deprecated field by name. --- crates/fff-c/src/lib.rs | 17 ++++++++++++----- crates/fff-core/src/grep.rs | 10 ++++------ .../tests/bigram_overlay_coherence_test.rs | 5 ++--- .../tests/bigram_overlay_integration.rs | 5 ++--- crates/fff-core/tests/fuzz_file_operations.rs | 5 ++--- .../fff-core/tests/fuzz_git_watcher_stress.rs | 13 ++++++------- crates/fff-core/tests/fuzz_real_repos.rs | 5 ++--- crates/fff-core/tests/grep_integration.rs | 13 ++++++------- .../tests/new_directory_watcher_test.rs | 5 ++--- .../tests/path_separator_constraint_test.rs | 5 ++--- crates/fff-core/tests/real_binary_fixtures.rs | 5 ++--- crates/fff-mcp/src/server.rs | 7 +++---- crates/fff-nvim/benches/fuzzy_search_bench.rs | 5 ++--- crates/fff-nvim/benches/grep_bench.rs | 4 +--- crates/fff-nvim/src/bin/bench_grep_query.rs | 5 ++--- crates/fff-nvim/src/bin/fuzzy_grep_test.rs | 5 ++--- crates/fff-nvim/src/bin/grep_profiler.rs | 9 ++++----- crates/fff-nvim/src/bin/grep_vs_rg.rs | 9 ++++----- crates/fff-nvim/src/lib.rs | 9 ++++++--- crates/fff-python/src/finder.rs | 13 ++++++++----- 20 files changed, 74 insertions(+), 80 deletions(-) diff --git a/crates/fff-c/src/lib.rs b/crates/fff-c/src/lib.rs index 9df732f6..d6c5cc3e 100644 --- a/crates/fff-c/src/lib.rs +++ b/crates/fff-c/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! C FFI bindings for fff-core //! //! This crate provides C-compatible FFI exports that can be used from any language @@ -737,8 +736,11 @@ pub unsafe extern "C" fn fff_live_grep( let options = fff::GrepSearchOptions { max_file_size: default_u64(max_file_size, 10 * 1024 * 1024), max_matches_per_file: max_matches_per_file as usize, - smart_case, - case_mode: None, + case_mode: Some(if smart_case { + fff::CaseMode::Smart + } else { + fff::CaseMode::Sensitive + }), file_offset: file_offset as usize, page_limit: default_u32(page_limit, 50) as usize, mode: grep_mode_from_u8(mode), @@ -748,6 +750,7 @@ pub unsafe extern "C" fn fff_live_grep( classify_definitions, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let result = picker.grep(&parsed, &options); @@ -841,8 +844,11 @@ pub unsafe extern "C" fn fff_multi_grep( let options = fff::GrepSearchOptions { max_file_size: default_u64(max_file_size, 10 * 1024 * 1024), max_matches_per_file: max_matches_per_file as usize, - smart_case, - case_mode: None, + case_mode: Some(if smart_case { + fff::CaseMode::Smart + } else { + fff::CaseMode::Sensitive + }), file_offset: file_offset as usize, page_limit: default_u32(page_limit, 50) as usize, mode: fff::GrepMode::PlainText, // ignored by multi_grep_search @@ -852,6 +858,7 @@ pub unsafe extern "C" fn fff_multi_grep( classify_definitions, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let result = picker.multi_grep(&patterns, constraint_refs, &options); diff --git a/crates/fff-core/src/grep.rs b/crates/fff-core/src/grep.rs index 29ea85da..2c857fe7 100644 --- a/crates/fff-core/src/grep.rs +++ b/crates/fff-core/src/grep.rs @@ -2464,12 +2464,10 @@ mod tests { let files = picker.get_files(); let arena = picker.arena_base_ptr(); - #[allow(deprecated)] let options = super::GrepSearchOptions { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, - smart_case: true, - case_mode: None, + case_mode: Some(super::CaseMode::Smart), file_offset: 0, page_limit: 100, mode: super::GrepMode::PlainText, @@ -2479,6 +2477,7 @@ mod tests { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let no_cancel = AtomicBool::new(false); @@ -2650,12 +2649,10 @@ mod tests { // Run a grep for "unicorn": six files match // (a, b, c in base + f, g, h in overflow). let query = super::parse_grep_query("unicorn"); - #[allow(deprecated)] let options = super::GrepSearchOptions { max_file_size: MAX_FFFILE_SIZE, max_matches_per_file: 0, - smart_case: true, - case_mode: None, + case_mode: Some(super::CaseMode::Smart), file_offset: 0, page_limit: 100, mode: super::GrepMode::PlainText, @@ -2665,6 +2662,7 @@ mod tests { classify_definitions: false, trim_whitespace: false, abort_signal: Some(std::sync::Arc::new(AtomicBool::new(false))), + ..Default::default() }; let result = picker.grep(&query, &options); diff --git a/crates/fff-core/tests/bigram_overlay_coherence_test.rs b/crates/fff-core/tests/bigram_overlay_coherence_test.rs index a93701d8..7369168d 100644 --- a/crates/fff-core/tests/bigram_overlay_coherence_test.rs +++ b/crates/fff-core/tests/bigram_overlay_coherence_test.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Stress tests for the bigram overlay layer. //! //! These tests create a real git repository with many files, build the bigram @@ -1310,8 +1309,7 @@ fn grep_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, @@ -1321,6 +1319,7 @@ fn grep_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/bigram_overlay_integration.rs b/crates/fff-core/tests/bigram_overlay_integration.rs index 8ee12b7f..8d5cbedb 100644 --- a/crates/fff-core/tests/bigram_overlay_integration.rs +++ b/crates/fff-core/tests/bigram_overlay_integration.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Integration test: verify that modifying a file after the bigram index is built //! still makes the new content findable via grep (through the overlay layer). @@ -369,8 +368,7 @@ fn grep_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, @@ -380,6 +378,7 @@ fn grep_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/fuzz_file_operations.rs b/crates/fff-core/tests/fuzz_file_operations.rs index b320d71c..24374d8e 100644 --- a/crates/fff-core/tests/fuzz_file_operations.rs +++ b/crates/fff-core/tests/fuzz_file_operations.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Randomized file-system mutation stress test. //! //! Seeds a directory with ~40 files across diverse content domains, builds the @@ -627,8 +626,7 @@ fn grep_plain_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, @@ -638,6 +636,7 @@ fn grep_plain_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/fuzz_git_watcher_stress.rs b/crates/fff-core/tests/fuzz_git_watcher_stress.rs index 0184142e..74ed7306 100644 --- a/crates/fff-core/tests/fuzz_git_watcher_stress.rs +++ b/crates/fff-core/tests/fuzz_git_watcher_stress.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Vibe coded stress test: randomized file + git operations driven against the *real* //! `BackgroundWatcher`, asserting the git-status invariant after every mutation. //! @@ -892,8 +891,7 @@ fn grep_plain_matches(shared: &SharedFilePicker, query: &str) -> Vec { let opts = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, @@ -903,6 +901,7 @@ fn grep_plain_matches(shared: &SharedFilePicker, query: &str) -> Vec { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let result = picker.grep(&parsed, &opts); // `GrepResult::files` is the already-deduplicated list of files that @@ -930,8 +929,7 @@ fn grep_fuzzy_matches(shared: &SharedFilePicker, query: &str) -> Vec { let opts = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::Fuzzy, @@ -941,6 +939,7 @@ fn grep_fuzzy_matches(shared: &SharedFilePicker, query: &str) -> Vec { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let result = picker.grep(&parsed, &opts); result @@ -963,8 +962,7 @@ fn grep_regex_matches(shared: &SharedFilePicker, query: &str) -> Vec { let opts = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::Regex, @@ -974,6 +972,7 @@ fn grep_regex_matches(shared: &SharedFilePicker, query: &str) -> Vec { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let result = picker.grep(&parsed, &opts); result diff --git a/crates/fff-core/tests/fuzz_real_repos.rs b/crates/fff-core/tests/fuzz_real_repos.rs index 9a6fd803..81db683b 100644 --- a/crates/fff-core/tests/fuzz_real_repos.rs +++ b/crates/fff-core/tests/fuzz_real_repos.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Proptest-driven fuzz test against real GitHub repos with a live watcher. //! //! Clones real repository, runs the simulated close to real user sereies of file system ewvents and @@ -227,8 +226,7 @@ fn grep_opts(mode: GrepMode) -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode, @@ -238,6 +236,7 @@ fn grep_opts(mode: GrepMode) -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/grep_integration.rs b/crates/fff-core/tests/grep_integration.rs index fc2835b8..71e0a5f8 100644 --- a/crates/fff-core/tests/grep_integration.rs +++ b/crates/fff-core/tests/grep_integration.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use std::fs; use std::path::Path; use tempfile::TempDir; @@ -32,8 +31,7 @@ fn plain_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, @@ -43,6 +41,7 @@ fn plain_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } @@ -51,8 +50,7 @@ fn regex_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::Regex, @@ -62,6 +60,7 @@ fn regex_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } @@ -70,8 +69,7 @@ fn fuzzy_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::Fuzzy, @@ -81,6 +79,7 @@ fn fuzzy_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/new_directory_watcher_test.rs b/crates/fff-core/tests/new_directory_watcher_test.rs index 0522cd3d..095a510f 100644 --- a/crates/fff-core/tests/new_directory_watcher_test.rs +++ b/crates/fff-core/tests/new_directory_watcher_test.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Integration test: verifying that the background watcher dynamically detects //! newly created directories and picks up files written inside them. //! @@ -133,8 +132,7 @@ fn grep_plain_count(picker: &FilePicker, query: &str) -> usize { let opts = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 500, mode: GrepMode::PlainText, @@ -144,6 +142,7 @@ fn grep_plain_count(picker: &FilePicker, query: &str) -> usize { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; picker.grep(&parsed, &opts).matches.len() } diff --git a/crates/fff-core/tests/path_separator_constraint_test.rs b/crates/fff-core/tests/path_separator_constraint_test.rs index a07aaf4a..7b7110c9 100644 --- a/crates/fff-core/tests/path_separator_constraint_test.rs +++ b/crates/fff-core/tests/path_separator_constraint_test.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Regression test for https://github.com/dmtrKovalenko/fff/issues/381 //! //! Directory (`PathSegment`) and file-path (`FilePath`) constraints must @@ -37,8 +36,7 @@ fn plain_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, @@ -48,6 +46,7 @@ fn plain_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-core/tests/real_binary_fixtures.rs b/crates/fff-core/tests/real_binary_fixtures.rs index 231102a2..4b6c8261 100644 --- a/crates/fff-core/tests/real_binary_fixtures.rs +++ b/crates/fff-core/tests/real_binary_fixtures.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] //! Real-world binary fixture regression. //! //! Reproduces the exact bug chain we hit with `codex_view` (4.5 MB ELF, no @@ -31,8 +30,7 @@ fn plain_opts() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff_search::grep::CaseMode::Smart), file_offset: 0, page_limit: 200, mode: GrepMode::PlainText, @@ -42,6 +40,7 @@ fn plain_opts() -> GrepSearchOptions { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() } } diff --git a/crates/fff-mcp/src/server.rs b/crates/fff-mcp/src/server.rs index 5e3d11fd..4e9ad7ec 100644 --- a/crates/fff-mcp/src/server.rs +++ b/crates/fff-mcp/src/server.rs @@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex}; use crate::cursor::CursorStore; use crate::output::{GrepFormatter, OutputMode, file_suffix}; -use fff::grep::{GrepMode, GrepSearchOptions, has_regex_metacharacters}; +use fff::grep::{CaseMode, GrepMode, GrepSearchOptions, has_regex_metacharacters}; use fff::types::{FileItem, PaginationArgs}; use fff::{FuzzySearchOptions, QueryParser, SharedFilePicker, SharedFrecency}; use fff_query_parser::AiGrepConfig; @@ -42,7 +42,6 @@ fn cleanup_fuzzy_query(s: &str) -> String { out } -#[allow(deprecated)] fn make_grep_options( output_mode: OutputMode, mode: GrepMode, @@ -67,8 +66,7 @@ fn make_grep_options( GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: matches_per_file, - smart_case: true, - case_mode: None, + case_mode: Some(CaseMode::Smart), file_offset, page_limit: 50, mode, @@ -78,6 +76,7 @@ fn make_grep_options( classify_definitions: true, trim_whitespace: true, abort_signal: None, + ..Default::default() }, auto_expand, ) diff --git a/crates/fff-nvim/benches/fuzzy_search_bench.rs b/crates/fff-nvim/benches/fuzzy_search_bench.rs index c1589ba7..fe12bdc0 100644 --- a/crates/fff-nvim/benches/fuzzy_search_bench.rs +++ b/crates/fff-nvim/benches/fuzzy_search_bench.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use fff::file_picker::{FFFMode, FilePicker}; use fff::types::PaginationArgs; @@ -628,8 +627,7 @@ fn bench_grep_search(c: &mut Criterion) { let options = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 0, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: 100, mode: GrepMode::PlainText, @@ -639,6 +637,7 @@ fn bench_grep_search(c: &mut Criterion) { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let test_queries = vec![ diff --git a/crates/fff-nvim/benches/grep_bench.rs b/crates/fff-nvim/benches/grep_bench.rs index 8bc3ae66..225e8a75 100644 --- a/crates/fff-nvim/benches/grep_bench.rs +++ b/crates/fff-nvim/benches/grep_bench.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use fff::file_picker::{FFFMode, FilePicker}; use fff::{ @@ -111,8 +110,7 @@ fn plain_options() -> GrepSearchOptions { GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: 50, mode: GrepMode::PlainText, diff --git a/crates/fff-nvim/src/bin/bench_grep_query.rs b/crates/fff-nvim/src/bin/bench_grep_query.rs index b38e28e5..19cf13ff 100644 --- a/crates/fff-nvim/src/bin/bench_grep_query.rs +++ b/crates/fff-nvim/src/bin/bench_grep_query.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] /// Single-query grep benchmark with bigram index profiling. /// /// Usage: @@ -23,8 +22,7 @@ fn run_grep(picker: &FilePicker, query: &str, iters: usize) { let options = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: usize::MAX, mode: GrepMode::PlainText, @@ -34,6 +32,7 @@ fn run_grep(picker: &FilePicker, query: &str, iters: usize) { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let parsed = parse_grep_query(query); diff --git a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs index 31902db7..7fd2cf31 100644 --- a/crates/fff-nvim/src/bin/fuzzy_grep_test.rs +++ b/crates/fff-nvim/src/bin/fuzzy_grep_test.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] /// Fuzzy grep quality test against ~/dev/lightsource /// /// Runs queries through the fuzzy grep pipeline and prints results @@ -28,8 +27,7 @@ fn run_fuzzy_query(picker: &FilePicker, query: &str, label: &str) { let options = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: 100, mode: GrepMode::Fuzzy, @@ -39,6 +37,7 @@ fn run_fuzzy_query(picker: &FilePicker, query: &str, label: &str) { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let parsed = parse_grep_query(query); diff --git a/crates/fff-nvim/src/bin/grep_profiler.rs b/crates/fff-nvim/src/bin/grep_profiler.rs index 47b7dd67..487e4a9e 100644 --- a/crates/fff-nvim/src/bin/grep_profiler.rs +++ b/crates/fff-nvim/src/bin/grep_profiler.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] /// Live grep benchmark profiler for fff.nvim /// /// Benchmarks the full grep pipeline against a large repository (Linux kernel). @@ -89,8 +88,7 @@ impl<'a> GrepBench<'a> { options: GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: 50, mode, @@ -100,6 +98,7 @@ impl<'a> GrepBench<'a> { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }, } } @@ -411,8 +410,7 @@ fn main() { let opts = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset, page_limit: 50, mode: Default::default(), @@ -422,6 +420,7 @@ fn main() { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let start = Instant::now(); let result = picker.grep(&parsed, &opts); diff --git a/crates/fff-nvim/src/bin/grep_vs_rg.rs b/crates/fff-nvim/src/bin/grep_vs_rg.rs index a2027b90..9e05a5fe 100644 --- a/crates/fff-nvim/src/bin/grep_vs_rg.rs +++ b/crates/fff-nvim/src/bin/grep_vs_rg.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use fff::file_picker::FilePicker; /// FFF vs ripgrep comparison benchmark /// @@ -163,8 +162,7 @@ fn run_fff_full(picker: &FilePicker, query: &str) -> (usize, Duration) { let options = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: usize::MAX, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: usize::MAX, mode: Default::default(), @@ -174,6 +172,7 @@ fn run_fff_full(picker: &FilePicker, query: &str) -> (usize, Duration) { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let start = Instant::now(); let result = picker.grep(&parsed, &options); @@ -187,8 +186,7 @@ fn run_fff_page(picker: &FilePicker, query: &str) -> (usize, Duration) { let options = GrepSearchOptions { max_file_size: 10 * 1024 * 1024, max_matches_per_file: 200, - smart_case: true, - case_mode: None, + case_mode: Some(fff::grep::CaseMode::Smart), file_offset: 0, page_limit: 50, mode: Default::default(), @@ -198,6 +196,7 @@ fn run_fff_page(picker: &FilePicker, query: &str) -> (usize, Duration) { classify_definitions: false, trim_whitespace: false, abort_signal: None, + ..Default::default() }; let start = Instant::now(); let result = picker.grep(&parsed, &options); diff --git a/crates/fff-nvim/src/lib.rs b/crates/fff-nvim/src/lib.rs index 4efe5087..52a71251 100644 --- a/crates/fff-nvim/src/lib.rs +++ b/crates/fff-nvim/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use crate::path_shortening::shorten_path_with_cache; use error::IntoLuaResult; use fff::file_picker::FilePicker; @@ -467,13 +466,16 @@ pub fn live_grep( Some("smart") => Some(fff::CaseMode::Smart), Some("sensitive") => Some(fff::CaseMode::Sensitive), Some("insensitive") => Some(fff::CaseMode::Insensitive), - _ => None, + _ => Some(if smart_case.unwrap_or(true) { + fff::CaseMode::Smart + } else { + fff::CaseMode::Sensitive + }), }; let options = fff::GrepSearchOptions { max_file_size: max_file_size.unwrap_or(10 * 1024 * 1024), max_matches_per_file: max_matches_per_file.unwrap_or(200), - smart_case: smart_case.unwrap_or(true), case_mode, file_offset: file_offset.unwrap_or(0), page_limit: page_size.unwrap_or(50), @@ -484,6 +486,7 @@ pub fn live_grep( classify_definitions: false, trim_whitespace: trim_whitespace.unwrap_or(false), abort_signal: None, + ..Default::default() }; let result = picker.grep(&parsed_query, &options); diff --git a/crates/fff-python/src/finder.rs b/crates/fff-python/src/finder.rs index 5bcef303..78106e65 100644 --- a/crates/fff-python/src/finder.rs +++ b/crates/fff-python/src/finder.rs @@ -1,4 +1,3 @@ -#![allow(deprecated)] use std::path::{Path, PathBuf}; use std::time::Duration; @@ -6,8 +5,8 @@ use fff::file_picker::FilePicker; use fff::frecency::FrecencyTracker; use fff::query_tracker::QueryTracker; use fff::{ - FFFMode, FilePickerOptions, FuzzySearchOptions, GrepSearchOptions, PaginationArgs, QueryParser, - SharedFilePicker, SharedFrecency, SharedQueryTracker, + CaseMode, FFFMode, FilePickerOptions, FuzzySearchOptions, GrepSearchOptions, PaginationArgs, + QueryParser, SharedFilePicker, SharedFrecency, SharedQueryTracker, }; use pyo3::prelude::*; use pyo3::types::PyDict; @@ -91,8 +90,11 @@ fn grep_options( GrepSearchOptions { max_file_size: defaulted_u64(max_file_size, defaults.max_file_size), max_matches_per_file: max_matches_per_file as usize, - smart_case, - case_mode: None, + case_mode: Some(if smart_case { + CaseMode::Smart + } else { + CaseMode::Sensitive + }), file_offset: cursor_offset, page_limit: defaulted_usize(page_limit, defaults.page_limit), mode, @@ -102,6 +104,7 @@ fn grep_options( classify_definitions, trim_whitespace: false, abort_signal: None, + ..Default::default() } }