How to Contribute to fff.nvim: The Complete Guide for Neovim Plugin Developers
Contributing to fff.nvim requires setting up a dual Rust and Lua development environment, building the core library with make build, and following the standardized workflow of formatting, linting, and testing via make commands before submitting a pull request.
fff.nvim is a high-performance fuzzy finder for Neovim that combines a Rust-based indexing engine with a lightweight Lua user interface. Whether you want to extend the fuzzy search algorithms, add new Lua API functions, or improve the floating window UI, this guide covers the exact file paths, build commands, and code patterns used in the repository.
Repository Architecture and Key Files
fff.nvim follows a hybrid architecture where performance-critical operations reside in Rust and the Neovim integration lives in Lua. Understanding this separation is essential before contributing.
Core Components
- Rust Core (
crates/): Handles file indexing, frecency tracking, query parsing, and search algorithms. The FFI bridge resides incrates/fff-nvim/src/lib.rs, which exposes functions likeinit_file_pickerandfuzzy_search_filesto Lua viamlua. - Lua Frontend (
lua/fff/): Provides the public API throughrequire('fff'). Key files includelua/fff/main.lua(containingfind_filesandlive_grep),lua/fff/conf.lua(configuration defaults), andlua/fff/picker_ui.lua(UI rendering and window management). - Plugin Entry (
plugin/fff.lua): The Vimscript shim that loads the Lua module when Neovim starts. - Documentation (
doc/fff.nvim.txtandREADME.md): Help files and usage examples that must be updated with any API changes.
Critical File Reference
When contributing to fff.nvim, you will most likely edit these specific files:
crates/fff-nvim/src/lib.rs– Add new Rust-exposed functions herecrates/fff-grep/src/searcher/core.rs– Modify theGrepSearchOptionsstruct or grep engine logiclua/fff/main.lua– Extend the public Lua APIlua/fff/conf.lua– Add new configuration optionslua/fff/picker_ui.lua– Adjust UI layouts, keymaps, or preview behaviortests/fff_core_spec.lua– Add integration tests for new features
Development Environment Setup
Before writing code, you must install the Rust toolchain and Lua formatting tools to match the CI environment.
Prerequisites Installation
# Install Rust (required for the core library)
curl https://sh.rustup.rs -sSf | sh -s -- -y
# Install Lua linting and formatting tools
sudo apt-get install luarocks
luarocks install luacheck
# Install stylua for Lua formatting
curl -L https://github.com/folke/stylua/releases/download/v0.20.0/stylua-linux-x86_64 -o ~/.local/bin/stylua
chmod +x ~/.local/bin/stylua
Building the Project
Clone your fork and build the Rust core with the required zlob feature:
git clone https://github.com/<your-username>/fff.nvim.git
cd fff.nvim
make build
This generates target/release/libfff_c.* and prepares the binary for the Lua side. If you are modifying the Node.js or Bun packages, also run make prepare-node or make prepare-bun to copy the compiled library into packages/*/bin.
The Contribution Workflow
Follow this exact sequence to ensure your changes pass CI and maintain code quality.
-
Install test dependencies: The repository includes a
test-setuptarget that clonesplenary.nvim(required for Lua tests).make test-setup -
Run the full test suite: Verify your environment is working before making changes.
make test -
Make your changes: Edit the appropriate files from the key file reference section above. For Rust changes, ensure you follow the existing patterns in
crates/fff-nvim/src/lib.rsfor exposing functions to Lua. -
Format and lint: The repository enforces strict formatting standards.
make format # Runs stylua and cargo fmt make lint # Runs luacheck and cargo clippy -
Update documentation: Modify
doc/fff.nvim.txtandREADME.mdto reflect any API changes or new configuration options. -
Version bump (if needed): If you added public API features, synchronize the version across packages:
make set-npm-version PKG=packages/fff-bun VERSION=0.6.0 -
Submit your PR: Push your branch and open a pull request. The CI pipelines (
rust.yml,lua.yml) will automatically run the samemakecommands you ran locally.
Practical Contribution Examples
Adding a New Configuration Option to the Rust Grep Engine
To add a new before_context option for grep results, you must modify both the Rust struct and the Lua wrapper.
Rust side (crates/fff-grep/src/searcher/core.rs):
pub struct GrepSearchOptions {
pub max_file_size: usize,
pub max_matches_per_file: usize,
pub smart_case: bool,
pub file_offset: usize,
pub page_limit: usize,
pub mode: GrepMode,
pub time_budget_ms: u64,
// New field:
pub before_context: usize,
}
Lua side (lua/fff/main.lua):
local options = fff.GrepSearchOptions {
max_file_size = max_file_size,
max_matches_per_file = max_matches_per_file,
smart_case = smart_case,
file_offset = file_offset,
page_limit = page_size,
mode = mode,
time_budget_ms = time_budget_ms,
before_context = opts.before_context or 0, -- new parameter
}
Creating a Custom Picker Keybinding
You can expose new functionality by wrapping the existing API in your contribution tests or documentation examples:
-- Example: Find Neovim config files only
vim.keymap.set('n', '<leader>fc', function()
require('fff').find_files({
title = 'Neovim Config Files',
cwd = vim.fn.stdpath('config'),
query = 'lua/**/*.lua',
})
end, { desc = 'FFF – Find Neovim config files' })
This works because find_files forwards the options table to picker_ui.open (defined in lua/fff/main.lua), where cwd overrides the base path and query acts as an initial filter.
Programmatic Search Integration
Plugins can leverage the Rust search engine directly without opening the UI:
local function open_first_match(word)
local results = require('fff').search(word, 5)
if #results > 0 then
vim.api.nvim_command('edit ' .. vim.fn.fnameescape(results[1].path))
else
vim.notify('No matches for "' .. word .. '"', vim.log.levels.WARN)
end
end
-- Create a command that uses the fuzzy finder programmatically
vim.api.nvim_create_user_command('FffOpenWord', function(opts)
open_first_match(opts.args)
end, { nargs = 1 })
The M.search function in lua/fff/main.lua maps directly to the Rust fuzzy_search_files implementation, ensuring identical scoring and frecency boosting.
Common Issues and Solutions
Contributors frequently encounter these specific issues when working with the fff.nvim codebase:
- Missing LMDB directory: The frecency database defaults to
vim.fn.stdpath('cache') .. '/fff_nvim'. If running individual tests manually, create this directory first or the Rust core will fail to initialize. zlobfeature errors: Always build withmake buildrather than rawcargo build, as the Makefile includes--features zlobrequired for globbing functionality.- Formatting failures: CI strictly enforces
styluaformatting. Runmake formatbefore committing to avoid build failures. - Binary path errors in JS packages: After building, run
make prepare-nodeormake prepare-bunto copy the compiled.soor.dllfiles into the appropriatepackages/subdirectories before running JavaScript tests.
Summary
- fff.nvim consists of a Rust core (
crates/) for indexing and search, and a Lua frontend (lua/fff/) for the Neovim interface. - Use
make buildto compile the project andmake testto validate changes across Rust, Lua, and JavaScript targets. - Key files for contributions include
crates/fff-nvim/src/lib.rsfor FFI functions,lua/fff/main.luafor API extensions, andlua/fff/conf.luafor configuration options. - Always run
make formatandmake lintbefore submitting to ensurestyluaandcargo clippycompliance. - Update both
doc/fff.nvim.txtandREADME.mdwhen modifying public APIs or adding new features.
Frequently Asked Questions
Do I need to know Rust to contribute to fff.nvim?
No, many contributions require only Lua knowledge. You can extend the UI in lua/fff/picker_ui.lua, add configuration options in lua/fff/conf.lua, or improve documentation without touching Rust. However, features involving search algorithms, file indexing, or performance optimizations require modifying the Rust codebase in crates/.
How do I run only the Lua tests during development?
While make test runs the full suite including Rust and JavaScript tests, you can run Lua-specific tests using the standard Plenary test harness. Ensure you have run make test-setup first to install plenary.nvim in the tests/ directory, then execute the specific test file such as tests/fff_core_spec.lua using Plenary's test runner from within Neovim.
Why does my build fail with missing libfff_c errors?
This indicates the Rust library was not built or not copied to the expected location. Run make build to compile the core, and if you are working with the Node.js or Bun packages, additionally run make prepare-node or make prepare-bun. These targets copy the compiled shared objects from target/release/ into the appropriate packages/*/bin/ directories.
Can I add custom keymaps to the fff.nvim picker UI?
Yes, though the default keymaps are defined within lua/fff/picker_ui.lua. To contribute new default keybindings or make mappings configurable, modify the conf.lua file to accept new mapping options, then implement the handling logic in picker_ui.lua. Ensure any new keymaps respect the existing hl (highlight) tables and window management patterns used in the codebase.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →