d2e3993398
* feat: cross-compile for android (! for termux users) * chore: Update docs for - feat: cross-compile for android (! for termux users)
544 lines
19 KiB
Plaintext
544 lines
19 KiB
Plaintext
*fff.nvim.txt* For Neovim >= 0.10.0 Last change: 2026 February 28
|
||
|
||
==============================================================================
|
||
Table of Contents *fff.nvim-table-of-contents*
|
||
|
||
- Features |fff.nvim-features|
|
||
- Installation |fff.nvim-installation|
|
||
FFF.nvimFinally a smart fuzzy file picker for neovim.
|
||
|
||
|
||
|
||
**FFF** stands for ~freakin fast fuzzy file finder~ (pick 3) and it is an
|
||
opinionated fuzzy file picker for neovim. Just for files, but we’ll try to
|
||
solve file picking completely.
|
||
|
||
It comes with a dedicated rust backend runtime that keep tracks of the file
|
||
index, your file access and modifications, git status, and provides a
|
||
comprehensive typo-resistant fuzzy search experience.
|
||
|
||
|
||
FEATURES *fff.nvim-features*
|
||
|
||
- Works out of the box with no additional configuration
|
||
- Typo resistant fuzzy search <https://github.com/saghen/frizbee>
|
||
- Git status integration allowing to take advantage of last modified times within a worktree
|
||
- Separate file index maintained by a dedicated backend allows <10 milliseconds search time for 50k files codebase
|
||
- Display images in previews (for now requires snacks.nvim)
|
||
- Smart in a plenty of different ways hopefully helpful for your workflow
|
||
- This plugin initializes itself lazily by default
|
||
|
||
|
||
INSTALLATION *fff.nvim-installation*
|
||
|
||
|
||
[!NOTE] Although we’ll try to make sure to keep 100% backward compatibility,
|
||
by using you should understand that silly bugs and breaking changes may happen.
|
||
And also we hope for your contributions and feedback to make this plugin ideal
|
||
for everyone.
|
||
|
||
PREREQUISITES ~
|
||
|
||
FFF.nvim requires:
|
||
|
||
- Neovim 0.10.0+
|
||
- Rustup <https://rustup.rs/> (we require nightly for building the native backend rustup will handle toolchain automatically)
|
||
|
||
|
||
INSTALLATION ~
|
||
|
||
|
||
LAZY.NVIM
|
||
|
||
>lua
|
||
{
|
||
'dmtrKovalenko/fff.nvim',
|
||
build = function()
|
||
-- this will download prebuild binary or try to use existing rustup toolchain to build from source
|
||
-- (if you are using lazy you can use gb for rebuilding a plugin if needed)
|
||
require("fff.download").download_or_build_binary()
|
||
end,
|
||
-- if you are using nixos
|
||
-- build = "nix run .#release",
|
||
opts = { -- (optional)
|
||
debug = {
|
||
enabled = true, -- we expect your collaboration at least during the beta
|
||
show_scores = true, -- to help us optimize the scoring system, feel free to share your scores!
|
||
},
|
||
},
|
||
-- No need to lazy-load with lazy.nvim.
|
||
-- This plugin initializes itself lazily.
|
||
lazy = false,
|
||
keys = {
|
||
{
|
||
"ff", -- try it if you didn't it is a banger keybinding for a picker
|
||
function() require('fff').find_files() end,
|
||
desc = 'FFFind files',
|
||
},
|
||
{
|
||
"fg",
|
||
function() require('fff').live_grep() end,
|
||
desc = 'LiFFFe grep',
|
||
},
|
||
{
|
||
"fz",
|
||
function() require('fff').live_grep({
|
||
grep = {
|
||
modes = { 'fuzzy', 'plain' }
|
||
}
|
||
}) end,
|
||
desc = 'Live fffuzy grep',
|
||
},
|
||
{
|
||
"fc",
|
||
function() require('fff').live_grep({ query = vim.fn.expand("<cword>") }) end,
|
||
desc = 'Search current word',
|
||
},
|
||
}
|
||
}
|
||
<
|
||
|
||
|
||
VIM.PACK
|
||
|
||
>lua
|
||
vim.pack.add({ 'https://github.com/dmtrKovalenko/fff.nvim' })
|
||
|
||
vim.api.nvim_create_autocmd('PackChanged', {
|
||
callback = function(event)
|
||
if event.data.updated then
|
||
require('fff.download').download_or_build_binary()
|
||
end
|
||
end,
|
||
})
|
||
|
||
-- the plugin will automatically lazy load
|
||
vim.g.fff = {
|
||
lazy_sync = true, -- start syncing only when the picker is open
|
||
debug = {
|
||
enabled = true,
|
||
show_scores = true,
|
||
},
|
||
}
|
||
|
||
vim.keymap.set(
|
||
'n',
|
||
'ff',
|
||
function() require('fff').find_files() end,
|
||
{ desc = 'FFFind files' }
|
||
)
|
||
<
|
||
|
||
|
||
CONFIGURATION ~
|
||
|
||
FFF.nvim comes with sensible defaults. Here’s the complete configuration with
|
||
all available options:
|
||
|
||
>lua
|
||
require('fff').setup({
|
||
base_path = vim.fn.getcwd(),
|
||
prompt = '🪿 ',
|
||
title = 'FFFiles',
|
||
max_results = 100,
|
||
max_threads = 4,
|
||
lazy_sync = true, -- set to false if you want file indexing to start on open
|
||
layout = {
|
||
height = 0.8,
|
||
width = 0.8,
|
||
prompt_position = 'bottom', -- or 'top'
|
||
preview_position = 'right', -- or 'left', 'right', 'top', 'bottom'
|
||
preview_size = 0.5,
|
||
flex = { -- set to nil to disable flex layout
|
||
size = 130, -- column threshold: if screen width >= size, use preview_position; otherwise use wrap
|
||
wrap = 'top', -- position to use when screen is narrower than size
|
||
},
|
||
show_scrollbar = true, -- Show scrollbar for pagination
|
||
-- How to shorten long directory paths in the file list:
|
||
-- 'middle_number' (default): uses dots for 1-3 hidden (a/./b, a/../b, a/.../b)
|
||
-- and numbers for 4+ (a/.4./b, a/.5./b)
|
||
-- 'middle': always uses dots (a/./b, a/../b, a/.../b)
|
||
-- 'end': truncates from the end (home/user/projects)
|
||
path_shorten_strategy = 'middle_number',
|
||
},
|
||
preview = {
|
||
enabled = true,
|
||
max_size = 10 * 1024 * 1024, -- Do not try to read files larger than 10MB
|
||
chunk_size = 8192, -- Bytes per chunk for dynamic loading (8kb - fits ~100-200 lines)
|
||
binary_file_threshold = 1024, -- amount of bytes to scan for binary content (set 0 to disable)
|
||
imagemagick_info_format_str = '%m: %wx%h, %[colorspace], %q-bit',
|
||
line_numbers = false,
|
||
wrap_lines = false,
|
||
filetypes = {
|
||
svg = { wrap_lines = true },
|
||
markdown = { wrap_lines = true },
|
||
text = { wrap_lines = true },
|
||
},
|
||
},
|
||
keymaps = {
|
||
close = '<Esc>',
|
||
select = '<CR>',
|
||
select_split = '<C-s>',
|
||
select_vsplit = '<C-v>',
|
||
select_tab = '<C-t>',
|
||
-- you can assign multiple keys to any action
|
||
move_up = { '<Up>', '<C-p>' },
|
||
move_down = { '<Down>', '<C-n>' },
|
||
preview_scroll_up = '<C-u>',
|
||
preview_scroll_down = '<C-d>',
|
||
toggle_debug = '<F2>',
|
||
-- grep mode: cycle between plain text, regex, and fuzzy search
|
||
cycle_grep_modes = '<S-Tab>',
|
||
-- goes to the previous query in history
|
||
cycle_previous_query = '<C-Up>',
|
||
-- multi-select keymaps for quickfix
|
||
toggle_select = '<Tab>',
|
||
send_to_quickfix = '<C-q>',
|
||
-- this are specific for the normal mode (you can exit it using any other keybind like jj)
|
||
focus_list = '<leader>l',
|
||
focus_preview = '<leader>p',
|
||
},
|
||
hl = {
|
||
border = 'FloatBorder',
|
||
normal = 'Normal',
|
||
cursor = 'CursorLine', -- Falls back to 'Visual' if CursorLine is not defined
|
||
matched = 'IncSearch',
|
||
title = 'Title',
|
||
prompt = 'Question',
|
||
frecency = 'Number',
|
||
debug = 'Comment',
|
||
combo_header = 'Number',
|
||
scrollbar = 'Comment',
|
||
directory_path = 'Comment',
|
||
-- Multi-select highlights
|
||
selected = 'FFFSelected',
|
||
selected_active = 'FFFSelectedActive',
|
||
-- Git text highlights for file names
|
||
git_staged = 'FFFGitStaged',
|
||
git_modified = 'FFFGitModified',
|
||
git_deleted = 'FFFGitDeleted',
|
||
git_renamed = 'FFFGitRenamed',
|
||
git_untracked = 'FFFGitUntracked',
|
||
git_ignored = 'FFFGitIgnored',
|
||
-- Git sign/border highlights
|
||
git_sign_staged = 'FFFGitSignStaged',
|
||
git_sign_modified = 'FFFGitSignModified',
|
||
git_sign_deleted = 'FFFGitSignDeleted',
|
||
git_sign_renamed = 'FFFGitSignRenamed',
|
||
git_sign_untracked = 'FFFGitSignUntracked',
|
||
git_sign_ignored = 'FFFGitSignIgnored',
|
||
-- Git sign selected highlights
|
||
git_sign_staged_selected = 'FFFGitSignStagedSelected',
|
||
git_sign_modified_selected = 'FFFGitSignModifiedSelected',
|
||
git_sign_deleted_selected = 'FFFGitSignDeletedSelected',
|
||
git_sign_renamed_selected = 'FFFGitSignRenamedSelected',
|
||
git_sign_untracked_selected = 'FFFGitSignUntrackedSelected',
|
||
git_sign_ignored_selected = 'FFFGitSignIgnoredSelected',
|
||
-- Grep highlights
|
||
grep_match = 'IncSearch', -- Highlight for matched text in grep results
|
||
grep_line_number = 'LineNr', -- Highlight for :line:col location
|
||
grep_regex_active = 'DiagnosticInfo', -- Highlight for keybind + label when regex is on
|
||
grep_plain_active = 'Comment', -- Highlight for keybind + label when regex is off
|
||
grep_fuzzy_active = 'DiagnosticHint', -- Highlight for keybind + label when fuzzy is on
|
||
-- Cross-mode suggestion highlights
|
||
suggestion_header = 'WarningMsg', -- Highlight for the "No results found. Suggested..." banner
|
||
},
|
||
-- Store file open frecency
|
||
frecency = {
|
||
enabled = true,
|
||
db_path = vim.fn.stdpath('cache') .. '/fff_nvim',
|
||
},
|
||
-- Store successfully opened queries with respective matches
|
||
history = {
|
||
enabled = true,
|
||
db_path = vim.fn.stdpath('data') .. '/fff_queries',
|
||
min_combo_count = 3, -- Minimum selections before combo boost applies (3 = boost starts on 3rd selection)
|
||
combo_boost_score_multiplier = 100, -- Score multiplier for combo matches (files repeatedly opened with same query)
|
||
},
|
||
-- Git integration
|
||
git = {
|
||
status_text_color = false, -- Apply git status colors to filename text (default: false, only sign column)
|
||
},
|
||
debug = {
|
||
enabled = false, -- Set to true to show scores in the UI
|
||
show_scores = false,
|
||
show_file_info = false, -- Show file info panel in preview
|
||
},
|
||
logging = {
|
||
enabled = true,
|
||
log_file = vim.fn.stdpath('log') .. '/fff.log',
|
||
log_level = 'info',
|
||
},
|
||
-- find_files settings
|
||
file_picker = {
|
||
current_file_label = '(current)',
|
||
},
|
||
-- grep settings
|
||
grep = {
|
||
max_file_size = 10 * 1024 * 1024, -- Skip files larger than 10MB
|
||
max_matches_per_file = 100, -- Maximum matches per file (set 0 to unlimited)
|
||
smart_case = true, -- Case-insensitive unless query has uppercase
|
||
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
|
||
},
|
||
})
|
||
<
|
||
|
||
|
||
KEY FEATURES ~
|
||
|
||
|
||
AVAILABLE METHODS
|
||
|
||
>lua
|
||
require('fff').find_files() -- Find files in current directory
|
||
require('fff').find_in_git_root() -- Find files in the current git repository
|
||
require('fff').scan_files() -- Trigger rescan of files in the current directory
|
||
require('fff').refresh_git_status() -- Refresh git status for the active file lock
|
||
require('fff').find_files_in_dir(path) -- Find files in a specific directory
|
||
require('fff').change_indexing_directory(new_path) -- Change the base directory for the file picker
|
||
<
|
||
|
||
|
||
COMMANDS
|
||
|
||
FFF.nvim provides several commands for interacting with the file picker:
|
||
|
||
- `:FFFFind [path|query]` - Open file picker. Optional: provide directory path or search query
|
||
- `:FFFScan` - Manually trigger a rescan of files in the current directory
|
||
- `:FFFRefreshGit` - Manually refresh git status for all files
|
||
- `:FFFClearCache [all|frecency|files]` - Clear various caches
|
||
- `:FFFHealth` - Check FFF health status and dependencies
|
||
- `:FFFDebug [on|off|toggle]` - Toggle debug scores display
|
||
- `:FFFOpenLog` - Open the FFF log file in a new tab
|
||
|
||
|
||
MULTILINE PASTE SUPPORT
|
||
|
||
The input field automatically handles multiline clipboard content by joining
|
||
all lines into a single search query. This is particularly useful when copying
|
||
file paths from terminal output.
|
||
|
||
|
||
DEBUG MODE
|
||
|
||
Toggle scoring information display:
|
||
|
||
- Press `F2` while in the picker
|
||
- Use `:FFFDebug` command
|
||
- Enable by default with `debug.show_scores = true`
|
||
|
||
|
||
MULTI-SELECT AND QUICKFIX INTEGRATION
|
||
|
||
Select multiple files and send them to Neovim’s quickfix list (keymaps are
|
||
configurable):
|
||
|
||
- `<Tab>` - Toggle selection for the current file (shows thick border `▊` in signcolumn)
|
||
- `<C-q>` - Send selected files to quickfix list and close picker
|
||
|
||
|
||
LIVE GREP SEARCH MODES
|
||
|
||
Live grep supports three search modes, cycled with `<S-Tab>`:
|
||
|
||
- **Plain text** (default) - The query is matched literally. Special regex characters like `.`, `*`, `(`, `)`, `$` have no special meaning. This is the safest mode for searching code containing regex metacharacters.
|
||
- **Regex** - The query is interpreted as a regular expression. Supports character classes (`[a-z]`), quantifiers (`+`, `*`, `{n}`), alternation (`foo|bar`), anchors (`^`, `$`), word boundaries (`\b`), and more.
|
||
- **Fuzzy** - The query is fuzzy matched using Smith-Waterman scoring. Accommodates typos and scattered characters (e.g., "mtxlk" matches "mutex_lock"). Results are filtered by a quality threshold to avoid overly fuzzy matches.
|
||
|
||
The current mode is shown on the right side of the input field (e.g., `plain`,
|
||
`regex`, `fuzzy`) with color-coded highlighting.
|
||
|
||
You can customize which modes are available and their cycling order globally in
|
||
your configuration, or per-call when invoking `live_grep()`.
|
||
|
||
**Global configuration:**
|
||
|
||
>lua
|
||
require('fff').setup({
|
||
grep = {
|
||
modes = { 'plain', 'regex' }, -- Only plain and regex, no fuzzy
|
||
}
|
||
})
|
||
<
|
||
|
||
**Per-call configuration:**
|
||
|
||
>lua
|
||
-- Only fuzzy and plain modes for this specific grep
|
||
require('fff').live_grep({
|
||
grep = {
|
||
modes = { 'fuzzy', 'plain' },
|
||
}
|
||
})
|
||
|
||
-- Single mode (hides mode indicator completely)
|
||
require('fff').live_grep({
|
||
grep = {
|
||
modes = { 'fuzzy' },
|
||
}
|
||
})
|
||
|
||
-- Pre-fill the search with an initial query
|
||
require('fff').live_grep({ query = 'search term' })
|
||
<
|
||
|
||
When only one mode is configured, the mode indicator is hidden completely and
|
||
the cycle keybind does nothing.
|
||
|
||
|
||
CROSS-MODE SUGGESTIONS
|
||
|
||
When a search returns no results, FFF automatically queries the opposite search
|
||
mode and displays the results as suggestions:
|
||
|
||
- **File search with no matches** → shows suggested **content matches** (grep results) for the same query
|
||
- **Grep search with no matches** → shows suggested **file name matches** for the same query
|
||
|
||
Suggestions are clearly labeled with a "No results found. Suggested …" banner
|
||
(highlighted with `hl.suggestion_header`). You can navigate and select
|
||
suggestion items just like normal results — selecting a grep suggestion will
|
||
open the file at the matching line.
|
||
|
||
|
||
GIT STATUS HIGHLIGHTING
|
||
|
||
FFF integrates with git to show file status through sign column indicators
|
||
(enabled by default) and optional filename text coloring.
|
||
|
||
**Sign Column Indicators** (enabled by default) - Border characters shown in
|
||
the sign column:
|
||
|
||
>lua
|
||
hl = {
|
||
git_sign_staged = 'FFFGitSignStaged',
|
||
git_sign_modified = 'FFFGitSignModified',
|
||
git_sign_deleted = 'FFFGitSignDeleted',
|
||
git_sign_renamed = 'FFFGitSignRenamed',
|
||
git_sign_untracked = 'FFFGitSignUntracked',
|
||
git_sign_ignored = 'FFFGitSignIgnored',
|
||
}
|
||
<
|
||
|
||
**Text Highlights** (opt-in) - Apply colors to filenames based on git status:
|
||
|
||
To enable git status text coloring, set `git.status_text_color = true`:
|
||
|
||
>lua
|
||
require('fff').setup({
|
||
git = {
|
||
status_text_color = true, -- Enable git status colors on filename text
|
||
},
|
||
hl = {
|
||
git_staged = 'FFFGitStaged', -- Files staged for commit
|
||
git_modified = 'FFFGitModified', -- Modified unstaged files
|
||
git_deleted = 'FFFGitDeleted', -- Deleted files
|
||
git_renamed = 'FFFGitRenamed', -- Renamed files
|
||
git_untracked = 'FFFGitUntracked', -- New untracked files
|
||
git_ignored = 'FFFGitIgnored', -- Git-ignored files
|
||
}
|
||
})
|
||
<
|
||
|
||
The plugin provides sensible default highlight groups that link to common git
|
||
highlight groups (e.g., GitSignsAdd, GitSignsChange). You can override these
|
||
with your own custom highlight groups to match your colorscheme.
|
||
|
||
**Example - Custom Bright Colors for Text:**
|
||
|
||
>lua
|
||
vim.api.nvim_set_hl(0, 'CustomGitModified', { fg = '#FFA500' })
|
||
vim.api.nvim_set_hl(0, 'CustomGitUntracked', { fg = '#00FF00' })
|
||
|
||
require('fff').setup({
|
||
git = {
|
||
status_text_color = true,
|
||
},
|
||
hl = {
|
||
git_modified = 'CustomGitModified',
|
||
git_untracked = 'CustomGitUntracked',
|
||
}
|
||
})
|
||
<
|
||
|
||
|
||
FILE FILTERING
|
||
|
||
FFF.nvim respects `.gitignore` patterns automatically. To filter files from the
|
||
picker without modifying `.gitignore`, create a `.ignore` file in your project
|
||
root:
|
||
|
||
>gitignore
|
||
# Exclude all markdown files
|
||
*.md
|
||
|
||
# Exclude specific subdirectory
|
||
docs/archive/**/*.md
|
||
<
|
||
|
||
Run `:FFFScan` to force a rescan if needed.
|
||
|
||
|
||
TROUBLESHOOTING ~
|
||
|
||
|
||
HEALTH CHECK
|
||
|
||
Run `:FFFHealth` to check the status of FFF.nvim and its dependencies. This
|
||
will verify:
|
||
|
||
- File picker initialization status
|
||
- Optional dependencies (git, image preview tools)
|
||
- Database connectivity
|
||
|
||
|
||
VIEWING LOGS
|
||
|
||
If you encounter issues, check the log file:
|
||
|
||
>vim
|
||
:FFFOpenLog
|
||
<
|
||
|
||
Or manually open the log file at `~/.local/state/nvim/log/fff.log` (default
|
||
location).
|
||
|
||
|
||
COMMON ISSUES
|
||
|
||
**File picker not initializing:**
|
||
|
||
- Ensure the Rust backend is compiled: `cargo build --release` in the plugin directory
|
||
- Check that your Neovim version is 0.10.0 or higher
|
||
|
||
**Image previews not working:**
|
||
|
||
- Verify your terminal supports images (kitty, iTerm2, WezTerm, etc.)
|
||
- For terminals without native image support, install one of: `chafa`, `viu`, or `img2txt`
|
||
- If using snacks.nvim, ensure it’s properly configured
|
||
|
||
**Performance issues:**
|
||
|
||
- Adjust `max_threads` in configuration based on your system
|
||
- Reduce `preview.max_lines` and `preview.max_size` for large files
|
||
- Clear cache if it becomes too large: `:FFFClearCache all`
|
||
|
||
**Files not being indexed:**
|
||
|
||
- Run `:FFFScan` to manually trigger a file scan
|
||
- Check that the `base_path` is correctly set
|
||
- Verify you have read permissions for the directory
|
||
|
||
|
||
DEBUG MODE
|
||
|
||
Enable debug mode to see scoring information and troubleshoot search results:
|
||
|
||
- Press `F2` while in the picker
|
||
- Run `:FFFDebug on` to enable permanently
|
||
- Set `debug.show_scores = true` in configuration
|
||
|
||
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
|
||
|
||
vim:tw=78:ts=8:noet:ft=help:norl:
|