Refs #451
When both enable_mmap_cache and enable_content_indexing are false,
run_post_scan never executes (guarded by config.warmup || config.content_indexing).
This left bigram_index as None, so get_scan_progress().is_warmup_complete
returned false forever.
Added warmup_complete flag to ScanSignals. Set immediately after post-scan
finishes OR right after walking completes when no post-scan work needed.
get_scan_progress now reads warmup_complete instead of checking bigram_index
presence.
Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com>
Adds optional `hl.winhl` config that controls the picker windows'
`winhighlight`. Accepts either a single string applied to all picker
windows, or a table with optional `prompt`, `list`, `preview`, and
`file_info` keys. Missing keys fall back to the default composed from
`hl.normal`, `hl.border`, and `hl.title`.
Refs #140
Co-authored-by: Dmitriy Kovalenko <dmtr.kovalenko@outlook.com>
* feat(lua): Add programmatic api for lua
* fix(lua): Resolve CI lint errors
- main.lua: drop duplicate `open_file_under_cursor` impl,
superseded by the path-resolving variant
- picker_ui.lua: replace undefined `canonicalize_fff_path`
with `utils.canonicalize_picker_path`
- programmatic_search_spec.lua: cast `hit` to non-nil after
the `assert.is_not_nil` so lua-ls stops flagging the
follow-up field accesses
calculate_layout_dimensions uses math.max(0, ...) for list/preview
heights and widths, which can yield 0 when the terminal is small and
flex wraps preview to top/bottom. nvim_open_win requires positive
integers, so it rejected the call with "Invalid 'height'".
Clamp width/height to >=1 at the final boundary before nvim_open_win
in build_window_configs for list, input, preview, and file_info.
Closes#518
* fix(neovim): Improve borders and default flex layout
* chore: Update docs for - fix(neovim): Improve borders and default flex layout
* fix(ci): Use gustav pat
* fix(lua): Resolve type check warnings
Annotate state shape and inject-field warnings flagged by
lua-language-server.
* feat: add wrap_around option for cursor navigation
When enabled (wrap_around = true), the cursor wraps to the opposite end
when reaching the first or last item in the results list, instead of
stopping at the boundary.
This applies to:
- move_up/move_down in insert mode (C-k/C-j, Up/Down, Tab/S-Tab, etc.)
- j/k navigation in normal mode (list buffer)
- Both top and bottom prompt positions
The option defaults to false to preserve existing behavior.
Pagination still takes priority: wrapping only occurs when there are
no more pages to load in the current direction.
* fix: wrap_around takes priority over pagination
When wrap_around is enabled, cursor wraps within the current page
instead of loading the next/previous page. This gives the expected
cycling behavior where Tab at the top jumps to the bottom and
S-Tab at the bottom jumps to the top.
* fix: wrap only at global boundaries, paginate on intermediate pages
Pagination now takes priority over wrap_around on non-boundary pages.
Wrapping only occurs at the true global edges:
- First item on first page → wraps to last item on last page
- Last item on last page → wraps to first item on first page
On all other page boundaries, normal pagination continues as expected.
* fix: stylua formatting and remove wrap_around from FffKeymapsConfig type
- Collapse multi-line callbacks to single-line (stylua)
- Remove wrap_around from FffKeymapsConfig type annotation (belongs only on FffConfig)
* perf: Improve macos indexing wall time
* wip: try to get rid of arcs
* chore: Update docs for - wip: try to get rid of arcs
* chore: expanad fuzzy test suite
* fix ci
* fix: Parallelize git & indexing
* fix: Segementation fault updating frecency
This is essentially a problem I commited myself into by doing unsafe
dirt, and it actaully blew us in a few releases. This set of changes
making sure that we **guarantee** that the invalid behavior is
impossibe at runtime, while still doing unsafe dirt in compile time.
* chore: Update docs for - fix: Segementation fault updating frecency
* perf: Improve performance for no link time optimmization builds
* chore: Update docs for - perf: Improve performance for no link time optimmization builds
* fix: Prevent LMDB stale locks from deadlocking app
This is the consequences of the migrating to the locked safe version
of LMDB usage which is affected by the other SIGSEGV bug we have fixed.
Now no other sigsegvs can result in deadlock
* chore: Update docs for - fix: Prevent LMDB stale locks from deadlocking app
* feat(mcp): add Windows PowerShell installer script
* fix(mcp/win): force TLS 1.2, drop sha256 verify
Addresses review feedback on PR #444:
- Force TLS 1.2 on PS 5.1 / older Win10 where SecurityProtocol may default to SSL3/TLS1.0 and GitHub rejects.
- Drop sha256 verification entirely. Avoids PS 5.1 vs PS 7 exception-type mismatch (WebException vs HttpResponseException) where ErrorActionPreference=Stop would abort on a missing checksum file instead of warning.
* fix(mcp/win): apply review feedback from @ccastanedaucf
- Add -Version and -InstallDir params (with FFF_MCP_VERSION / FFF_MCP_INSTALL_DIR env-var fallbacks for irm | iex usage).
- Detect arch via registry (HKLM\...\Environment) instead of $env:PROCESSOR_ARCHITECTURE — env vars lie under x86/ARM64 emulation.
- Skip release-list scan when -Version is pinned.
- Prefer curl.exe (ships with Win10 1803+) for downloads, fall back to iwr with $ProgressPreference='SilentlyContinue' to dodge PS 5.1's slow progress bar.
- Append install dir to current-session PATH so the user does not need a new shell.
* feat(mcp/win): add -PathScope param for $PROFILE-style PATH persistence
Per @PurpleMyst feedback: some pwsh users prefer appending to $PROFILE *nix-style
rather than setting the user environment variable globally.
-PathScope User (default) — set HKCU Environment.Path (existing behavior)
-PathScope Profile — append `$env:PATH += ';...'` to $PROFILE.CurrentUserAllHosts
-PathScope None — skip persistence entirely
Env-var fallback: FFF_MCP_PATH_SCOPE (for the irm | iex case).
Current-session $env:PATH still updated regardless of scope.
* fix(windows): treat backslash as path separator in constraint matching
Indexed paths keep the OS-native separator (\\ on Windows) so that pathdiff, strip_prefix and watcher event lookups all stay byte-identical. Queries and constraints always use /, so path_contains_segment and path_ends_with_suffix now fold / and \\ together when walking boundaries and comparing slices. Glob matching gets a localized / rewrite at the call site because globset operates on strings.
Adds windows-latest to the Rust test matrix with a focused regression test covering PathSegment, FilePath and Glob constraints across grep, multi_grep and fuzzy_search. Also gates the unix-only write_relative_cstr / MMAP_THRESHOLD so -D warnings compiles on Windows.
Fixes#381
* test(windows): canonicalize tmp base in tests that rely on path equality
Three pre-existing tests compared picker-internal (canonical) paths against tmp.path()-derived (potentially 8.3 short-name) paths and silently passed on Linux/macOS. Match the picker's dunce canonicalization in each test so watch_dir, overflow and libgit2-workdir assertions hold on Windows too.
score.rs also now detects a forward slash in the query (on top of MAIN_SEPARATOR) so a path-like query enables the path-alignment bonus on Windows.
* fix(windows): canonicalize non-matching event paths in file picker lookups
FilePicker canonicalizes its base via dunce but watcher events, consumer calls and tests may still pass short-name / different-casing paths. to_relative_path, find_file_index and add_new_file now fall back to canonicalize (or parent canonicalize for deleted files) on Windows, keeping native separators in storage while still resolving non-matching prefixes.
* fix(windows): translate '/' to '\\' in fuzzy query parts on Windows
Stored paths keep the native separator, so a user query like 'src/core/file.rs' fails the byte-wise frizbee match against 'src\\core\\file.rs'. Translate forward slashes to backslashes only when the query uses them, inside both score_files and score_dirs, so full-path queries still rank the closest path match first.
* test(windows): compare fuzzy results by filename in fuzzy_and_grep_combined
edit_name/del_name come from repo_files with forward-slash relative paths, but stored paths on Windows use backslashes, so p.contains(edit_name) never matched. Fall back to file_name() which is separator-agnostic.
* chore: Update docs for - test(windows): compare fuzzy results by filename in fuzzy_and_grep_combined
* test(windows): accept backslash separators in path-shape assertions
grep_with_path_constraint, grep_with_negated_path_constraint and the watcher burst test asserted on relative paths with forward-slash prefixes. Stored paths on Windows use '\\', so accept either form.
* refactor(constraints): gate backslash separator handling to Windows
is_path_sep only folds '\\' on Windows and the two backslash-specific tests are #[cfg(windows)]. Removed verbose examples from the rustdoc on path_contains_segment / path_ends_with_suffix now that the behaviour is covered by the unit tests.
multi_grep uses 'patterns' while grep/find_files use 'query'; LLMs
routinely confuse the two and trip on 'missing field `query`' (#311).
Add a serde alias so the wrong-but-intuitive name also works without
changing the published JSON schema.
Sparse checkouts routinely produce paths in the git index that are not
materialized on disk. Those paths aren't in the file picker's index
either, so update_git_statuses() hit the 'couldn't update' branch on
every scan and spammed ERROR logs, which perceivably slowed Neovim
picker usage (#404).
Downgrade to debug — this is expected, not exceptional.
Parallel ffgrep/fffind calls in the same turn all entered ensureFinder()
simultaneously. Each attempted FileFinder.create() on the shared base
path, which acquires exclusive native locks on the frecency and history
DBs — a second concurrent creation could deadlock at the native layer
with no timeout, hanging the whole turn.
Gate ensureFinder() on an in-flight promise so only one FileFinder.create()
runs per base path at a time; concurrent callers await the same promise.
Fixes#403.
Previously 'maxResults: 0' was asymmetric: grep/multi_grep truncated the
output list to zero items while find_files interpreted 0 as 'unlimited'
and returned the full (potentially huge) result set.
Centralize the parsing through normalize_max_results() and treat 0 (plus
negatives and non-finite values) as 'use the default'. Also round
fractional values instead of truncating (0.4 → 1, 10.7 → 11).
Fixes#400 (issue 3).
When a grep pattern like 'foo(' triggers regex compilation and fails,
the core engine already falls back to literal matching and returns the
correct results. Prepending '! regex failed: ...' on top of those
results only confuses LLM callers (issue #425) — they see the error
before the matches and conclude the call failed.
Drop the warning from the formatter output entirely; the core still
populates the field for debugging and other bindings.
Lock in the contract from #423: absolute in-workspace paths (file,
directory, glob) must be rewritten to their repo-relative form before
being emitted as constraints.
Guard against regression of #432 where path='.' or path='app' (without
trailing slash) returned zero results. Current code normalizes both
correctly but tests lock in the contract.
Add tests covering 'git:modified' and 'status:modified' alias to prevent
regression of #308 where constraint-only queries reused raw_query as fuzzy
text and returned zero matches.