# Neovim Fuzzy File Finder with Rust Backend: Complete fff.nvim Architecture Guide

> Discover fff.nvim a fast Neovim fuzzy file finder powered by Rust. Explore its architecture and leverage its performance for efficient file navigation. Get the complete guide now.

- Repository: [Dmitriy Kovalenko/fff.nvim](https://github.com/dmtrKovalenko/fff.nvim)
- Tags: architecture
- Published: 2026-04-04

---

**fff.nvim is a high-performance fuzzy file picker for Neovim that delegates indexing, matching, and grep operations to a native Rust engine while providing a rich Lua UI for configuration and interaction.**

fff.nvim (by dmtrKovalenko/fff.nvim) reimagines file discovery in Neovim by combining a lightweight Lua frontend with a compiled Rust core. This architecture delivers sub-millisecond fuzzy matching on massive codebases while maintaining the configurability expected by modern Neovim users. The plugin splits responsibilities across clear architectural layers, from floating-window rendering in Lua to parallel filesystem scanning in Rust.

## Architecture Overview

fff.nvim employs a hybrid architecture that separates presentation from computation. The design ensures that heavy operations—such as indexing thousands of files or computing fuzzy match scores—never block the Neovim UI.

### The Layered Stack

The plugin consists of four distinct layers:

- **Neovim UI (Lua)** – Handles floating windows, keymaps, preview rendering, and user interaction via [`lua/fff/picker_ui.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/picker_ui.lua).
- **Configuration (Lua)** – Merges user settings with defaults in [`lua/fff/conf.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/conf.lua), exposing options through `vim.g.fff`.
- **Bridge (Lua + mlua)** – [`plugin/fff.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/plugin/fff.lua) loads the compiled `fff_nvim` module using the mlua Rust bindings.
- **Core Engine (Rust)** – The [`crates/fff-nvim/src/lib.rs`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/crates/fff-nvim/src/lib.rs) crate performs indexing, fuzzy matching, live grep, and Git integration on background threads.
- **Persistence (LMDB)** – Rust manages frecency data and query history using LMDB databases via `FrecencyTracker` and `QueryTracker`.

## Installation and Binary Management

Unlike pure Lua plugins, fff.nvim requires a compiled native binary. The plugin automates this process during initialization.

When Neovim sources [`plugin/fff.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/plugin/fff.lua), it executes:

```lua
require('fff.download').download_or_build_binary()
require('fff').setup(user_config)

```

This ensures the Rust library is available before the UI attempts to load it. For **lazy.nvim** users, the recommended configuration handles the build step explicitly:

```lua
{
  'dmtrKovalenko/fff.nvim',
  build = function()
    require('fff.download').download_or_build_binary()
  end,
  opts = {
    debug = { enabled = true, show_scores = true },
    keymaps = { close = '<Esc>', select = '<CR>' },
  },
  keys = {
    { 'ff', function() require('fff').find_files() end, desc = 'FFF Find files' },
    { 'fg', function() require('fff').live_grep() end,  desc = 'FFF Live grep' },
  },
}

```

## The Rust Core: Performance and Features

The Rust backend, exposed as the `fff_nvim` Lua module, handles all computationally expensive operations. It is compiled as a shared library using mlua's `#[mlua::lua_module]` attribute in [`crates/fff-nvim/src/lib.rs`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/crates/fff-nvim/src/lib.rs).

### Exported Functions

The Rust side exports a table of functions that the Lua frontend calls via `require('fff_nvim')`:

| Function | Purpose |
|----------|---------|
| `init_db` | Opens LMDB databases for frecency and query history persistence. |
| `init_file_picker` | Instantiates the global `FILE_PICKER` with the project's base path. |
| `fuzzy_search_files` | Runs the fuzzy matcher on indexed files, applying combo-boost, frecency, and Git status scores. |
| `live_grep` | Performs fast content search using the `fff-grep` crate, respecting size limits and search mode. |
| `track_access` / `track_query_completion` | Updates frecency and query history asynchronously on background threads. |
| `health_check` | Returns version, Git detection, DB health, and picker status for `:FFFHealth`. |

### Core Data Structures

Three global statics manage state across Neovim sessions:

- **`FilePicker`** – Maintains the in-memory file index, background scan thread, and Git status cache. Defined in the external `fff` crate, it provides `fuzzy_search()`, `grep()`, and `refresh_git_status()` methods.
- **`FrecencyTracker`** – LMDB-backed scoring based on file open frequency and recency.
- **`QueryTracker`** – Stores successful search patterns for history navigation.

These structures live in `once_cell::sync::Lazy` globals (`FILE_PICKER`, `FRECENCY`, `QUERY_TRACKER`) and are protected by `RwLock` to allow concurrent reads during searches while ensuring thread-safe writes.

## Lua Frontend and Configuration

The Lua layer focuses on user experience and configuration management, deferring all heavy lifting to the Rust core.

### Configuration Handling

[`lua/fff/conf.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/conf.lua) merges user options with sensible defaults, handles deprecated settings, and creates highlight groups. It provides a `get()` accessor used throughout the codebase to retrieve settings like `max_results` or `layout` dimensions.

The configuration is stored globally in `vim.g.fff` after `fff.setup()` is called, making it accessible to both Lua and Rust components.

### UI Implementation

[`lua/fff/picker_ui.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/picker_ui.lua) constructs the floating window interface using `vim.api.nvim_open_win()`. It draws the result list, preview pane, and scrollbar, then wires keymaps defined in the user configuration. When a user types a query, the UI calls `fff_nvim.fuzzy_search_files()` and renders the returned results without blocking the editor.

### Public API Entry Points

[`lua/fff/main.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/main.lua) defines the user-facing functions `find_files()` and `live_grep()`. Each function lazily loads the UI module and forwards requests to the Rust engine:

```lua
local picker_ok, picker_ui = pcall(require, 'fff.picker_ui')
if picker_ok then picker_ui.open(opts) else vim.notify(...) end

```

## Available Commands and API

fff.nvim exposes several Ex commands for direct interaction:

- `:FFFScan` – Forces a rescan of the current directory index.
- `:FFFRefreshGit` – Updates Git status for all indexed files.
- `:FFFClearCache [all|frecency|files]` – Removes cached data from LMDB.
- `:FFFHealth` – Displays diagnostics including Rust version, DB status, and scan progress.
- `:FFFDebug` – Toggles inline score display (also bound to `F2` by default).
- `:FFFOpenLog` – Opens the log file at `~/.local/state/nvim/log/fff.log`.

For programmatic access, call the Lua API directly:

```lua
require('fff').setup({
  base_path = vim.fn.getcwd(),
  max_results = 200,
  layout = { height = 0.9, width = 0.7, preview_position = 'right' },
})

require('fff').find_files({ title = 'My project files' })
require('fff').live_grep({ query = 'TODO' })

```

## Advanced: Accessing the Rust API Directly

Advanced users and plugin developers can bypass the UI layer and call the Rust functions directly via the `fff_nvim` module:

```lua
local fff = require('fff_nvim')

-- Initialize databases and picker
fff.init_db(
  vim.fn.stdpath('cache')..'/fff_nvim',
  vim.fn.stdpath('data')..'/fff_queries',
  false
)
fff.init_file_picker(vim.fn.getcwd())

-- Perform raw fuzzy search
local result = fff.fuzzy_search_files('main', 4, nil, 100, nil, nil, 20)
print(vim.inspect(result))

```

This low-level access is useful for debugging scoring algorithms or integrating fff.nvim's engine into custom picker implementations.

## Summary

- **fff.nvim** combines a Lua frontend ([`lua/fff/picker_ui.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/picker_ui.lua)) with a Rust backend ([`crates/fff-nvim/src/lib.rs`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/crates/fff-nvim/src/lib.rs)) to deliver high-performance fuzzy finding.
- The Rust core handles indexing, fuzzy matching, grep operations, and frecency tracking on background threads, storing data in LMDB.
- [`plugin/fff.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/plugin/fff.lua) automates binary downloading, while [`lua/fff/conf.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/conf.lua) manages configuration merging.
- Public functions like `find_files()` and `live_grep()` in [`lua/fff/main.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/main.lua) provide the primary interface.
- The plugin supports direct Rust API access via `require('fff_nvim')` for advanced use cases.

## Frequently Asked Questions

### How does fff.nvim compare to Telescope for Neovim?

fff.nvim prioritizes raw performance by delegating all search operations to a compiled Rust engine rather than processing results in Lua. According to the source code, the Rust core scans filesystems in parallel and maintains in-memory indices, delivering sub-millisecond query responses even on large repositories. Telescope, while more extensible, executes its matching logic within Neovim's Lua interpreter, which can lag on massive file lists.

### What is frecency tracking in fff.nvim?

**Frecency** is a ranking algorithm that combines *frequency* (how often you open a file) and *recency* (when you last opened it). The `FrecencyTracker` struct in the Rust backend persists this data to an LMDB database via `track_access()` calls. When you search, `fuzzy_search_files()` boosts scores for files you access frequently or recently, surfacing relevant results faster.

### How does the Rust backend communicate with Neovim?

The communication uses the **mlua** crate, which allows exporting Rust functions as a native Lua module. In [`crates/fff-nvim/src/lib.rs`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/crates/fff-nvim/src/lib.rs), the `#[mlua::lua_module]` attribute exposes a table of functions (like `fuzzy_search_files` and `live_grep`) that Lua code calls via `require('fff_nvim')`. The Lua frontend in [`lua/fff/picker_ui.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/picker_ui.lua) invokes these functions when the user types queries, passing strings and receiving structured results without serializing through JSON or RPC.

### Can I use fff.nvim without the floating window UI?

Yes. While [`lua/fff/picker_ui.lua`](https://github.com/dmtrKovalenko/fff.nvim/blob/main/lua/fff/picker_ui.lua) provides the default floating window interface, you can call the Rust backend directly through `require('fff_nvim')` as shown in the advanced usage examples. This allows integration with alternative UI plugins or custom command-line workflows while still leveraging the Rust engine's indexing and fuzzy matching capabilities.