From 516e444e3aeb9bc24792408827da872d6935c5bb Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Thu, 11 Jun 2026 17:16:46 -0700 Subject: [PATCH] feat(nvim): Expose more configuration for query parsing Allows to enable file.rs like query prefilter for grep to search in a single file partially resolves https://github.com/dmtrKovalenko/fff/issues/512 --- README.md | 1 + crates/fff-nvim/src/lib.rs | 36 +++++---- crates/fff-nvim/src/user_config.rs | 47 ++++++++++++ crates/fff-query-parser/src/config.rs | 87 +++++++--------------- crates/fff-query-parser/src/constraints.rs | 36 +++++++++ crates/fff-query-parser/src/lib.rs | 2 + crates/fff-query-parser/src/parser.rs | 37 +++++++-- lua/fff/conf.lua | 5 ++ lua/fff/core.lua | 2 + packages/fff-bun/examples/glob-bench.ts | 16 ++-- packages/fff-bun/src/ffi.ts | 54 +++----------- packages/fff-bun/src/finder.ts | 2 +- packages/fff-node/src/finder.ts | 2 +- 13 files changed, 191 insertions(+), 136 deletions(-) create mode 100644 crates/fff-nvim/src/user_config.rs diff --git a/README.md b/README.md index 86f4ef17..2bc10c01 100644 --- a/README.md +++ b/README.md @@ -338,6 +338,7 @@ require('fff').setup({ time_budget_ms = 150, modes = { 'plain', 'regex', 'fuzzy' }, trim_whitespace = false, + enable_filename_constraint = false, -- treat filename-like tokens (e.g. `score.rs`) in a grep query as a file-path filter scoping the search; off = searched as literal text location_format = ':%d:%d', -- printf format for line:col prefix in grep results, e.g. ':%d' for line-only }, debug = { diff --git a/crates/fff-nvim/src/lib.rs b/crates/fff-nvim/src/lib.rs index 5bfc94a1..a9f3cb2d 100644 --- a/crates/fff-nvim/src/lib.rs +++ b/crates/fff-nvim/src/lib.rs @@ -5,8 +5,8 @@ use fff::frecency::FrecencyTracker; use fff::path_utils::expand_tilde; use fff::query_tracker::QueryTracker; use fff::{ - DbHealthChecker, DirSearchConfig, Error, FFFMode, FileSearchConfig, FuzzySearchOptions, - GrepConfig, MixedSearchConfig, PaginationArgs, QueryParser, Score, SearchResult, + DbHealthChecker, DirSearchConfig, Error, FFFMode, FFFQuery, FileSearchConfig, + FuzzySearchOptions, MixedSearchConfig, PaginationArgs, QueryParser, Score, SearchResult, SharedFilePicker, SharedFrecency, SharedQueryTracker, }; use mimalloc::MiMalloc; @@ -15,12 +15,14 @@ use once_cell::sync::Lazy; use path_shortening::PathShortenStrategy; use std::path::{Path, PathBuf}; use std::time::Duration; +use user_config::{NvimGrepConfig, UserConfigOptions, set_global_user_config}; mod error; mod hex_dump; mod log; mod lua_types; mod path_shortening; +mod user_config; #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; @@ -65,6 +67,7 @@ struct PickerInitOpts { follow_symlinks: bool, enable_fs_root_scanning: bool, enable_home_dir_scanning: bool, + enable_filename_constraint: bool, } impl PickerInitOpts { @@ -86,6 +89,9 @@ impl PickerInitOpts { enable_home_dir_scanning: t .get::>("enable_home_dir_scanning")? .unwrap_or(false), + enable_filename_constraint: t + .get::>("enable_filename_constraint")? + .unwrap_or(false), }), other => Err(LuaError::RuntimeError(format!( "init opts must be a table, boolean, or nil — got {}", @@ -107,6 +113,9 @@ pub fn init_file_picker( } let opts = PickerInitOpts::from_lua_value(opts)?; + set_global_user_config(UserConfigOptions { + enable_filename_constraint: opts.enable_filename_constraint, + }); FilePicker::new_with_shared_state( FILE_PICKER.clone(), @@ -144,6 +153,9 @@ pub fn restart_index_in_path( })?; let opts = PickerInitOpts::from_lua_value(opts)?; + set_global_user_config(UserConfigOptions { + enable_filename_constraint: opts.enable_filename_constraint, + }); // Spawn a background thread BEFORE touching the picker lock. The // same-dir short-circuit previously called `FILE_PICKER.read()` on @@ -277,11 +289,9 @@ pub fn fuzzy_search_files( "Fuzzy search parameters" ); - let parser = QueryParser::new(FileSearchConfig); - let parsed = parser.parse(&query); - + let parsed_query = FFFQuery::parse(&query, FileSearchConfig); let results = picker.fuzzy_search( - &parsed, + &parsed_query, query_tracker_guard.as_ref(), FuzzySearchOptions { max_threads, @@ -297,7 +307,7 @@ pub fn fuzzy_search_files( ); if results.items.is_empty() && query.contains(std::path::MAIN_SEPARATOR) { - let pure_query = match &parsed.fuzzy_query { + let pure_query = match &parsed_query.fuzzy_query { fff_query_parser::FuzzyQuery::Text(t) => t.trim(), _ => query.trim(), }; @@ -314,7 +324,7 @@ pub fn fuzzy_search_files( }], total_matched: 1, total_files: results.total_files, - location: parsed.location, + location: parsed_query.location, }; return lua_types::SearchResultLua::new(found, picker).into_lua(lua); @@ -443,7 +453,7 @@ pub fn live_grep( return Err(error::to_lua_error(Error::FilePickerMissing)); }; - let parsed = fff::grep::parse_grep_query(&query); + let parsed_query = FFFQuery::parse(&query, NvimGrepConfig); let mode = match grep_mode.as_deref() { Some("regex") => fff::GrepMode::Regex, Some("fuzzy") => fff::GrepMode::Fuzzy, @@ -465,7 +475,7 @@ pub fn live_grep( abort_signal: None, }; - let result = picker.grep(&parsed, &options); + let result = picker.grep(&parsed_query, &options); lua_types::GrepResultLua::new(result, picker).into_lua(lua) } @@ -773,10 +783,10 @@ pub fn get_historical_grep_query(_: &Lua, offset: usize) -> LuaResult LuaResult { - let parser = QueryParser::new(GrepConfig); + let parser = QueryParser::new(NvimGrepConfig); let parsed = parser.parse(&query); let table = lua.create_table()?; table.set("grep_text", parsed.grep_text())?; diff --git a/crates/fff-nvim/src/user_config.rs b/crates/fff-nvim/src/user_config.rs new file mode 100644 index 00000000..0c8d7e52 --- /dev/null +++ b/crates/fff-nvim/src/user_config.rs @@ -0,0 +1,47 @@ +use fff::{GrepConfig, ParserConfig}; +use std::sync::RwLock; + +#[derive(Debug, Clone, Copy, Default)] +pub struct UserConfigOptions { + pub enable_filename_constraint: bool, +} + +static USER_CONFIG: RwLock = RwLock::new(UserConfigOptions { + enable_filename_constraint: false, +}); + +pub fn set_global_user_config(config: UserConfigOptions) { + if let Ok(mut guard) = USER_CONFIG.write() { + *guard = config; + } +} + +fn get_global_user_config() -> UserConfigOptions { + USER_CONFIG.read().map(|c| *c).unwrap_or_default() +} + +/// Grep query parser config for Neovim configured by user +/// Same as `GrepConfig` but lets the user configure some behavior of query parsing +pub struct NvimGrepConfig; + +impl ParserConfig for NvimGrepConfig { + fn enable_path_segments(&self) -> bool { + true + } + + fn enable_git_status(&self) -> bool { + false + } + + fn enable_location(&self) -> bool { + false + } + + fn enable_filename_constraint(&self) -> bool { + get_global_user_config().enable_filename_constraint + } + + fn is_glob_pattern(&self, token: &str) -> bool { + GrepConfig.is_glob_pattern(token) + } +} diff --git a/crates/fff-query-parser/src/config.rs b/crates/fff-query-parser/src/config.rs index 9cfbff81..9516c96d 100644 --- a/crates/fff-query-parser/src/config.rs +++ b/crates/fff-query-parser/src/config.rs @@ -1,45 +1,10 @@ use crate::constraints::Constraint; use crate::glob_detect::has_wildcards; -/// Check if a token looks like a filename or file path for use as a `FilePath` constraint. -/// -/// A token is a filename/path if ALL of: -/// - Does NOT end with `/` (that's a directory/PathSegment) -/// - Does NOT contain wildcards (`*`, `?`, `{`, `[`) — those are globs -/// - Last component (after final `/`) contains `.` with a valid-looking extension -/// (1–10 alphanumeric chars starting with a letter, e.g. `rs`, `json`, `tsx`) -/// -/// This covers both bare filenames (`score.rs`) and path-prefixed ones (`src/main.rs`). -#[inline] -fn is_filename_constraint_token(token: &str) -> bool { - let bytes = token.as_bytes(); - - // Must NOT end with / (that's a PathSegment) - if bytes.last() == Some(&b'/') { - return false; - } - - // Must NOT contain wildcards (those are globs) - if has_wildcards(token) { - return false; - } - - // Get the filename component (after last /) - let filename = token.rsplit('/').next().unwrap_or(token); - - // Extension must exist and look like a real file extension: - // starts with an ASCII letter (rejects version numbers like "v2.0"), - // followed by alphanumeric chars, max 10 chars total. - match filename.rfind('.') { - Some(dot_pos) => { - let ext = &filename[dot_pos + 1..]; - !ext.is_empty() - && ext.len() <= 10 - && ext.as_bytes()[0].is_ascii_alphabetic() - && ext.bytes().all(|b| b.is_ascii_alphanumeric()) - } - None => false, - } +/// Check if a token looks like a filename/path for use as a `FilePath` constraint. +#[deprecated(note = "use `Constraint::is_filename_constraint_token`")] +pub fn is_filename_constraint_token(token: &str) -> bool { + Constraint::is_filename_constraint_token(token) } /// Parser configuration trait - allows different picker types to customize parsing @@ -97,28 +62,32 @@ pub trait ParserConfig { true } - /// Custom constraint parsers for picker-specific needs + /// If `true`, tokens that look like filenames (`score.rs`, `src/main.rs`) + /// become `FilePath` constraints that scope the search to matching paths. + /// Off by default: a partial filename like `vite.conf` would otherwise + /// filter out `vite.config.ts` and yield zero results. + fn enable_filename_constraint(&self) -> bool { + false + } + + /// Custom constraint parsers for picker-specific needs. + /// + /// Returns `None` by default. Filename-token detection is handled + /// separately by the parser via `enable_filename_constraint`. fn parse_custom<'a>(&self, _input: &'a str) -> Option> { None } } -/// Default configuration for file picker - all features enabled +/// Default configuration for the file picker. +/// +/// Filename-constraint detection is off (trait default), so partial filenames +/// like `vite.conf` don't silently filter out fuzzy matches. The Neovim layer +/// overrides `enable_filename_constraint` to opt in based on user config. #[derive(Debug, Clone, Copy, Default)] pub struct FileSearchConfig; -impl ParserConfig for FileSearchConfig { - /// Detect bare filenames (`score.rs`) and path-prefixed filenames (`src/main.rs`) - /// as `FilePath` constraints so that multi-token queries like `score.rs file_picker` - /// filter by filename first, then fuzzy-match the remaining text against the path. - fn parse_custom<'a>(&self, token: &'a str) -> Option> { - if is_filename_constraint_token(token) { - Some(Constraint::FilePath(token)) - } else { - None - } - } -} +impl ParserConfig for FileSearchConfig {} /// Configuration for full-text search (grep) - file constraints enabled for /// filtering which files to search, git status disabled since it's not useful @@ -217,6 +186,10 @@ impl ParserConfig for AiGrepConfig { false } + fn enable_filename_constraint(&self) -> bool { + true + } + fn is_glob_pattern(&self, token: &str) -> bool { // First check GrepConfig's strict rules (path globs, brace expansion) if GrepConfig.is_glob_pattern(token) { @@ -240,14 +213,6 @@ impl ParserConfig for AiGrepConfig { false } - - fn parse_custom<'a>(&self, token: &'a str) -> Option> { - if is_filename_constraint_token(token) { - Some(Constraint::FilePath(token)) - } else { - None - } - } } impl ParserConfig for DirSearchConfig { diff --git a/crates/fff-query-parser/src/constraints.rs b/crates/fff-query-parser/src/constraints.rs index a1161432..74d2c68a 100644 --- a/crates/fff-query-parser/src/constraints.rs +++ b/crates/fff-query-parser/src/constraints.rs @@ -1,3 +1,5 @@ +use crate::glob_detect::has_wildcards; + /// Constraint types that can be extracted from a query #[derive(Debug, Clone, PartialEq, Eq)] pub enum Constraint<'a> { @@ -35,6 +37,40 @@ pub enum Constraint<'a> { Not(Box>), } +impl Constraint<'_> { + #[inline(always)] + pub fn is_filename_constraint_token(token: &str) -> bool { + let bytes = token.as_bytes(); + + // Must NOT end with / or . + if token.is_empty() || (bytes.last() == Some(&b'/') && bytes.first() != Some(&b'.')) { + return false; + } + + // Must NOT contain wildcards (those are globs) + if has_wildcards(token) { + return false; + } + + // Get the filename component (after last /) + let filename = token.rsplit('/').next().unwrap_or(token); + + // Extension must exist and look like a real file extension: + // starts with an ASCII letter (rejects version numbers like "v2.0"), + // followed by alphanumeric chars, max 10 chars total. + match filename.rfind('.') { + None => false, + Some(dot_idx) => { + let extension = &filename[dot_idx + 1..]; + + !extension.is_empty() + && extension.len() <= 10 // just an sassumption + && extension.bytes().all(|b| b.is_ascii_alphanumeric()) + } + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GitStatusFilter { Modified, diff --git a/crates/fff-query-parser/src/lib.rs b/crates/fff-query-parser/src/lib.rs index 6a009dbd..8dfcd4dd 100644 --- a/crates/fff-query-parser/src/lib.rs +++ b/crates/fff-query-parser/src/lib.rs @@ -45,6 +45,8 @@ pub mod glob_detect; pub mod location; mod parser; +#[allow(deprecated)] +pub use config::is_filename_constraint_token; pub use config::{ AiGrepConfig, DirSearchConfig, FileSearchConfig, GrepConfig, MixedSearchConfig, ParserConfig, }; diff --git a/crates/fff-query-parser/src/parser.rs b/crates/fff-query-parser/src/parser.rs index a698b0b7..c3834e1e 100644 --- a/crates/fff-query-parser/src/parser.rs +++ b/crates/fff-query-parser/src/parser.rs @@ -24,7 +24,7 @@ pub struct FFFQuery<'a> { } impl<'a> FFFQuery<'a> { - /// Parse query and execute, perfectly paired with zero sized configuration presets + /// Parse query and execute, perfectly paired with configuration presets /// /// ``` /// use fff_query_parser::{FFFQuery, FileSearchConfig}; @@ -292,10 +292,17 @@ fn parse_token<'a, C: ParserConfig>(token: &'a str, config: &C) -> Option parse_negation(token, config), b'/' if config.enable_path_segments() => parse_path_segment(token), + // Handle trailing slash syntax: www/ -> PathSegment("www") _ if config.enable_path_segments() && token.ends_with('/') => { - // Handle trailing slash syntax: www/ -> PathSegment("www") parse_path_segment_trailing(token) } + // tokens like `file.rs` *if enabled* + _ if config.enable_filename_constraint() + && !token.ends_with('/') + && Constraint::is_filename_constraint_token(token) => + { + Some(Constraint::FilePath(token)) + } _ => { // Check for glob patterns using config-specific detection if config.enable_glob() && config.is_glob_pattern(token) { @@ -416,6 +423,12 @@ fn parse_token_without_negation<'a, C: ParserConfig>( } } + if config.enable_filename_constraint() + && Constraint::is_filename_constraint_token(token) + { + return Some(Constraint::FilePath(token)); + } + config.parse_custom(token) } } @@ -483,6 +496,16 @@ mod tests { use super::*; use crate::{FileSearchConfig, GrepConfig}; + /// File-picker-like config with filename-constraint detection enabled, + /// mirroring the Neovim layer's opt-in behavior. + struct FilenameConstraintConfig; + + impl ParserConfig for FilenameConstraintConfig { + fn enable_filename_constraint(&self) -> bool { + true + } + } + #[test] fn test_parse_extension() { assert_eq!(parse_extension("*.rs"), Some(Constraint::Extension("rs"))); @@ -1305,7 +1328,7 @@ mod tests { #[test] fn test_file_picker_bare_filename_constraint() { - let parser = QueryParser::new(FileSearchConfig); + let parser = QueryParser::new(FilenameConstraintConfig); let result = parser.parse("score.rs file_picker"); assert_eq!(result.constraints.len(), 1); assert!( @@ -1318,7 +1341,7 @@ mod tests { #[test] fn test_file_picker_path_prefixed_filename_constraint() { - let parser = QueryParser::new(FileSearchConfig); + let parser = QueryParser::new(FilenameConstraintConfig); let result = parser.parse("libswscale/slice.c lum_convert"); assert_eq!(result.constraints.len(), 1); assert!( @@ -1362,7 +1385,7 @@ mod tests { #[test] fn test_file_picker_filename_with_multiple_fuzzy_parts() { - let parser = QueryParser::new(FileSearchConfig); + let parser = QueryParser::new(FilenameConstraintConfig); let result = parser.parse("main.rs src components"); assert_eq!(result.constraints.len(), 1); assert!(matches!( @@ -1389,7 +1412,7 @@ mod tests { #[test] fn test_file_picker_only_one_filepath_constraint() { - let parser = QueryParser::new(FileSearchConfig); + let parser = QueryParser::new(FilenameConstraintConfig); let result = parser.parse("main.rs score.rs"); // Only first filename becomes a constraint; second is text assert_eq!(result.constraints.len(), 1); @@ -1418,7 +1441,7 @@ mod tests { #[test] fn test_file_picker_dotfile_is_filename() { - let parser = QueryParser::new(FileSearchConfig); + let parser = QueryParser::new(FilenameConstraintConfig); let result = parser.parse(".gitignore src"); assert_eq!(result.constraints.len(), 1); assert!( diff --git a/lua/fff/conf.lua b/lua/fff/conf.lua index 6edaa297..650cf3c0 100644 --- a/lua/fff/conf.lua +++ b/lua/fff/conf.lua @@ -390,6 +390,11 @@ local function init() 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) + -- Treat filename-like tokens (e.g. `score.rs`, `src/main.rs`) in a grep query as a + -- file-path filter, scoping the content search to matching files. When off, such + -- tokens are searched as literal text. A token is a filename if it has a valid-looking + -- extension and no wildcards. + enable_filename_constraint = false, -- Format string for the line/column location prefix in grep results. -- Uses vim's printf-style format: %d placeholders for line and column (1-based). -- Default ':%d:%d' renders as ':356:1'. Use ':%d' for line-only ':356'. diff --git a/lua/fff/core.lua b/lua/fff/core.lua index cacdf415..1b3f3a16 100644 --- a/lua/fff/core.lua +++ b/lua/fff/core.lua @@ -115,6 +115,7 @@ M.change_indexing_directory = function(new_path) follow_symlinks = config.follow_symlinks, enable_fs_root_scanning = config.enable_fs_root_scanning, enable_home_dir_scanning = config.enable_home_dir_scanning, + enable_filename_constraint = config.grep and config.grep.enable_filename_constraint, }) if not ok then vim.notify('Failed to change directory: ' .. err, vim.log.levels.ERROR) @@ -150,6 +151,7 @@ M.ensure_initialized = function() follow_symlinks = config.follow_symlinks, enable_fs_root_scanning = config.enable_fs_root_scanning, enable_home_dir_scanning = config.enable_home_dir_scanning, + enable_filename_constraint = config.grep and config.grep.enable_filename_constraint, }) if not ok then vim.notify('Failed to initialize file picker: ' .. tostring(result), vim.log.levels.ERROR) diff --git a/packages/fff-bun/examples/glob-bench.ts b/packages/fff-bun/examples/glob-bench.ts index 82fe9244..c4b73341 100644 --- a/packages/fff-bun/examples/glob-bench.ts +++ b/packages/fff-bun/examples/glob-bench.ts @@ -24,9 +24,8 @@ import { Glob as BunGlob } from "bun"; import { FileFinder } from "../src/index"; // npm glob — optional. Skip silently if not installed. -let npmGlob: - | ((pattern: string, opts: { cwd: string }) => Promise) - | null = null; +let npmGlob: ((pattern: string, opts: { cwd: string }) => Promise) | null = + null; try { const mod: { glob: (pattern: string, opts: { cwd: string }) => Promise; @@ -61,16 +60,13 @@ function summarize(label: string, samples: Sample[]): void { const median = sorted[Math.floor(sorted.length / 2)]!; const worst = sorted[sorted.length - 1]!; const counts = new Set(samples.map((s) => s.count)); - const countStr = - counts.size === 1 ? `${best.count}` : `[${[...counts].join(", ")}]`; + const countStr = counts.size === 1 ? `${best.count}` : `[${[...counts].join(", ")}]`; console.log( `${label.padEnd(16)} best=${best.ms.toFixed(2)}ms median=${median.ms.toFixed(2)}ms worst=${worst.ms.toFixed(2)}ms count=${countStr}`, ); } -async function bench( - fn: () => Promise | T, -): Promise<{ ms: number; result: T }> { +async function bench(fn: () => Promise | T): Promise<{ ms: number; result: T }> { const start = performance.now(); const result = await fn(); return { ms: performance.now() - start, result }; @@ -152,8 +148,6 @@ const counts = { console.log( `\nNote: fff respects gitignore + skips binaries; Bun.Glob and npm glob walk the raw filesystem. Count differences are expected.`, ); -console.log( - `raw counts: fff=${counts.fff} bun=${counts.bun} npm=${counts.npm}`, -); +console.log(`raw counts: fff=${counts.fff} bun=${counts.bun} npm=${counts.npm}`); finder.destroy(); diff --git a/packages/fff-bun/src/ffi.ts b/packages/fff-bun/src/ffi.ts index 75fb584c..904902d9 100644 --- a/packages/fff-bun/src/ffi.ts +++ b/packages/fff-bun/src/ffi.ts @@ -292,9 +292,7 @@ function loadLibrary(): FFFLibrary { if (lib) return lib; const isEmbedded = embeddedLibPath?.includes("$bunfs") ?? false; - const binaryPath = isEmbedded - ? embeddedLibPath - : (findBinary() ?? embeddedLibPath); + const binaryPath = isEmbedded ? embeddedLibPath : (findBinary() ?? embeddedLibPath); if (!binaryPath) { throw new Error(libNotFoundMessage()); } @@ -349,9 +347,7 @@ function snakeToCamel(obj: unknown): unknown { const result: Record = {}; for (const [key, value] of Object.entries(obj as Record)) { - const camelKey = key.replace(/_([a-z])/g, (_, letter) => - letter.toUpperCase(), - ); + const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); result[camelKey] = snakeToCamel(value); } return result; @@ -446,8 +442,7 @@ function parseJsonResult(resultPtr: Pointer | null): Result { const jsonStr = readCString(envelope.handlePtr); library.symbols.fff_free_string(asPtr(envelope.handlePtr)); - if (jsonStr === null || jsonStr === "") - return { ok: true, value: undefined as T }; + if (jsonStr === null || jsonStr === "") return { ok: true, value: undefined as T }; try { return { ok: true, value: snakeToCamel(JSON.parse(jsonStr)) as T }; @@ -748,9 +743,7 @@ function readDirItemStruct(p: number): DirItem { /** * Parse an FffDirSearchResult from a raw FffResult pointer, then free native memory. */ -function parseDirSearchResult( - resultPtr: Pointer | null, -): Result { +function parseDirSearchResult(resultPtr: Pointer | null): Result { if (resultPtr === null) { return err("FFI returned null pointer"); } @@ -853,9 +846,7 @@ function readMixedItemStruct(p: number): MixedItem { /** * Parse an FffMixedSearchResult from a raw FffResult pointer, then free native memory. */ -function parseMixedSearchResult( - resultPtr: Pointer | null, -): Result { +function parseMixedSearchResult(resultPtr: Pointer | null): Result { if (resultPtr === null) { return err("FFI returned null pointer"); } @@ -1036,16 +1027,10 @@ function readGrepMatchStruct(p: number): GrepMatch { match.fuzzyScore = read.u16(pp, GM_FUZZY_SCORE); } if (ctxBeforeCount > 0) { - match.contextBefore = readCStringArray( - read.ptr(pp, GM_CTX_BEFORE), - ctxBeforeCount, - ); + match.contextBefore = readCStringArray(read.ptr(pp, GM_CTX_BEFORE), ctxBeforeCount); } if (ctxAfterCount > 0) { - match.contextAfter = readCStringArray( - read.ptr(pp, GM_CTX_AFTER), - ctxAfterCount, - ); + match.contextAfter = readCStringArray(read.ptr(pp, GM_CTX_AFTER), ctxAfterCount); } if (read.u8(pp, GM_IS_DEFINITION) !== 0) { match.isDefinition = true; @@ -1327,30 +1312,18 @@ export function ffiGetScanProgress(handle: NativeHandle): Result { /** * Wait for scan to complete. */ -export function ffiWaitForScan( - handle: NativeHandle, - timeoutMs: number, -): Result { +export function ffiWaitForScan(handle: NativeHandle, timeoutMs: number): Result { const library = loadLibrary(); - const resultPtr = library.symbols.fff_wait_for_scan( - handle, - BigInt(timeoutMs), - ); + const resultPtr = library.symbols.fff_wait_for_scan(handle, BigInt(timeoutMs)); return parseBoolResult(resultPtr); } /** * Restart index in new path. */ -export function ffiRestartIndex( - handle: NativeHandle, - newPath: string, -): Result { +export function ffiRestartIndex(handle: NativeHandle, newPath: string): Result { const library = loadLibrary(); - const resultPtr = library.symbols.fff_restart_index( - handle, - ptr(encodeString(newPath)), - ); + const resultPtr = library.symbols.fff_restart_index(handle, ptr(encodeString(newPath))); return parseVoidResult(resultPtr); } @@ -1388,10 +1361,7 @@ export function ffiGetHistoricalQuery( offset: number, ): Result { const library = loadLibrary(); - const resultPtr = library.symbols.fff_get_historical_query( - handle, - BigInt(offset), - ); + const resultPtr = library.symbols.fff_get_historical_query(handle, BigInt(offset)); return parseStringResult(resultPtr); } diff --git a/packages/fff-bun/src/finder.ts b/packages/fff-bun/src/finder.ts index 4d8c7adf..66ed2e57 100644 --- a/packages/fff-bun/src/finder.ts +++ b/packages/fff-bun/src/finder.ts @@ -507,7 +507,7 @@ export class FileFinder implements FileFinderApi { if (!guard.ok) return guard; const deadline = Date.now() + timeoutMs; - while(true) { + while (true) { const progress = this.getScanProgress(); if (!progress.ok) return progress; if (!progress.value.isScanning && progress.value.isWarmupComplete) { diff --git a/packages/fff-node/src/finder.ts b/packages/fff-node/src/finder.ts index 02957a33..a7b91626 100644 --- a/packages/fff-node/src/finder.ts +++ b/packages/fff-node/src/finder.ts @@ -517,7 +517,7 @@ export class FileFinder implements FileFinderApi { if (!guard.ok) return guard; const deadline = Date.now() + timeoutMs; - while(true) { + while (true) { const progress = this.getScanProgress(); if (!progress.ok) return progress; if (!progress.value.isScanning && progress.value.isWarmupComplete) {