Skip to content

Commit 477a8a2

Browse files
mvanhornclaude
andauthored
fix(grep): align preview highlight stripping with Rust query parser (#343)
* fix(grep): align preview highlight stripping with Rust query parser The Lua heuristic in highlight_grep_matches used a simple prefix check (^[*!/] or ^.) to strip constraints. This diverged from the Rust GrepConfig parser in several ways: - Multi-word queries like 'foo bar *.rs' only highlighted 'foo' - Constraint prefixes like type:rust were not stripped - Tokens starting with '.' were incorrectly treated as constraints - Escaped constraint tokens (e.g. \*.config) were not handled Replace the heuristic with _is_grep_constraint() that matches the Rust parser's actual GrepConfig rules: extensions (*.rs), path segments (/src/), exclusions (!test), type filters (type:rust), and path-oriented globs. Use all text parts joined with space for highlighting, matching grep_text() on the Rust side. Fixes #331 * refactor(grep): expose parsed query to lua via Rust function Replace the Lua-side constraint detection (_is_grep_constraint) with a new parse_grep_query() function that delegates to the Rust GrepConfig parser. This keeps the Rust parser as the single source of truth for query parsing, avoiding drift when new token types are added. The new function is exposed to Lua as fff.parse_grep_query(query) and returns a table with the grep_text field (the search text with all constraints stripped). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9a6ee3d commit 477a8a2

3 files changed

Lines changed: 24 additions & 12 deletions

File tree

crates/fff-nvim/src/lib.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use fff::frecency::FrecencyTracker;
55
use fff::path_utils::expand_tilde;
66
use fff::query_tracker::QueryTracker;
77
use fff::{
8-
DbHealthChecker, Error, FFFMode, FileSearchConfig, FuzzySearchOptions, PaginationArgs,
9-
QueryParser, Score, SearchResult, SharedFrecency, SharedPicker, SharedQueryTracker,
8+
DbHealthChecker, Error, FFFMode, FileSearchConfig, FuzzySearchOptions, GrepConfig,
9+
PaginationArgs, QueryParser, Score, SearchResult, SharedFrecency, SharedPicker,
10+
SharedQueryTracker,
1011
};
1112
use mimalloc::MiMalloc;
1213
use mlua::prelude::*;
@@ -582,6 +583,18 @@ pub fn get_historical_grep_query(_: &Lua, offset: usize) -> LuaResult<Option<Str
582583
.into_lua_result()
583584
}
584585

586+
/// Parse a grep query string and return its text portion (with constraints stripped).
587+
///
588+
/// Uses the Rust `GrepConfig` parser as the single source of truth, so Lua
589+
/// code never needs to re-implement constraint detection.
590+
pub fn parse_grep_query(lua: &Lua, query: String) -> LuaResult<LuaTable> {
591+
let parser = QueryParser::new(GrepConfig);
592+
let parsed = parser.parse(&query);
593+
let table = lua.create_table()?;
594+
table.set("grep_text", parsed.grep_text())?;
595+
Ok(table)
596+
}
597+
585598
pub fn wait_for_initial_scan(_: &Lua, timeout_ms: Option<u64>) -> LuaResult<bool> {
586599
// Extract the scan signal Arc WITHOUT holding the read lock, so the
587600
// scan thread can acquire the write lock to store its results.
@@ -822,6 +835,7 @@ fn create_exports(lua: &Lua) -> LuaResult<LuaTable> {
822835
exports.set("health_check", lua.create_function(health_check)?)?;
823836
exports.set("shorten_path", lua.create_function(shorten_path)?)?;
824837
exports.set("hex_dump", lua.create_function(hex_dump::hex_dump)?)?;
838+
exports.set("parse_grep_query", lua.create_function(parse_grep_query)?)?;
825839

826840
Ok(exports)
827841
}

lua/fff/fuzzy.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ M.get_git_root = rust_module.get_git_root
4646

4747
-- Grep functions
4848
M.live_grep = rust_module.live_grep
49+
M.parse_grep_query = rust_module.parse_grep_query
4950

5051
-- Utility functions
5152
M.health_check = rust_module.health_check

lua/fff/location_utils.lua

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,13 @@ function M.highlight_grep_matches(bufnr, location, namespace)
178178

179179
local query = location.grep_query
180180

181-
-- Extract the actual search text from the grep query (strip file constraints like *.rs /src/)
182-
-- The query parser uses space-separated tokens; the first non-constraint token is the pattern.
183-
-- Simple heuristic: strip tokens that look like constraints (start with *, /, or !)
184-
local search_text = query
185-
local parts = vim.split(query, '%s+')
186-
local text_parts = {}
187-
for _, part in ipairs(parts) do
188-
if part ~= '' and not part:match('^[%*!/]') and not part:match('^%.') then table.insert(text_parts, part) end
189-
end
190-
if #text_parts > 0 then search_text = text_parts[1] end
181+
-- Use the Rust GrepConfig parser as the single source of truth for
182+
-- stripping constraint tokens. This avoids duplicating constraint
183+
-- detection in Lua, which would break whenever a new token type is added.
184+
local fuzzy = require('fff.fuzzy')
185+
local parsed = fuzzy.parse_grep_query(query)
186+
local search_text = parsed.grep_text
187+
if search_text == '' then search_text = query end
191188

192189
if not search_text or search_text == '' then return nil end
193190

0 commit comments

Comments
 (0)