dmtrKovalenko/fff.nvim

GitHub: dmtrKovalenko/fff.nvim

FFF.nvim 是一款带智能记忆功能的快速文件搜索器,同时服务于 Neovim 用户和 AI 编程助手,通过多维评分让搜索结果越用越精准。

Stars: 1728 | Forks: 75

FFF

AI agents (MCP)   |   Neovim 用户

一款为你的 AI 和 neovim 准备的快速文件搜索工具,内置记忆功能

Stars Issues Contributors

**FFF** 代表 ~~超快的模糊文件查找器~~ (从中选 3 个 F),它是一款专为你的 AI agent 和 Neovim 打造的、具有鲜明观点的模糊文件选择器。仅仅用于文件搜索,但我们把文件搜索做得真 TM 好。 FFF 是一款用于 grep、模糊文件匹配、globbing 和多重 grep 的工具,重点关注性能和有用的搜索结果。对于人类用户 —— 提供难以置信的抗拼写错误体验;对于 AI agents —— 实现最快的文件搜索,并附带额外的免费“记忆”功能,根据频率/新近度、git 状态 (git status)、文件大小、定义匹配等多种因素建议最佳搜索结果。 ## MCP FFF 是一种通过为你的 AI agent 的文件搜索工具内置一点记忆功能,从而减少时间和 token 消耗的绝佳方式。它让你的 AI 能够更快地找到代码,通过减少往返次数和阅读无用文件来节省 token。 ![显示 fff.nvim 优于内置 claude code 工具的图表](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/8f4a7308b9004238.png) 你可以使用一个简单的 bash 脚本将 FFF 作为 AI agent 的依赖项安装: ``` curl -L https://dmtrkovalenko.dev/install-fff-mcp.sh | bash ``` 它将打印出如何将其连接到你的 `Claude Code`、`Codex`、`OpenCode` 等工具的说明。连接完成后,只需告诉你的 agent “使用 fff” 即可。 这是一个可以完美运行的 `CLAUDE.md` 示例添加内容: ``` # CLAUDE.md For any file search or grep in the current git indexed directory use fff tools ``` ## Neovim 指南 这是在 linux 仓库 (100k 文件, 8GB) 上的一些演示,但你最好亲自尝试一下,看看它的魔力 https://github.com/user-attachments/assets/5d0e1ce9-642c-4c44-aa88-01b05bb86abb ### 安装说明 FFF.nvim 需要 neovim 0.10.0 或更高版本 #### lazy.nvim ``` { '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("") }) end, desc = 'Search current word', }, } } ``` #### vim.pack ``` 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' } ) ``` ### 配置 FFF.nvim 拥有合理的默认设置。以下是包含所有可用选项的完整配置: ``` 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 false 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, cursorlineopt = 'both', -- the cursorlineopt used for lines in grep file previews, see :h cursorlineopt wrap_lines = false, filetypes = { svg = { wrap_lines = true }, markdown = { wrap_lines = true }, text = { wrap_lines = true }, }, }, keymaps = { close = '', select = '', select_split = '', select_vsplit = '', select_tab = '', -- you can assign multiple keys to any action move_up = { '', '' }, move_down = { '', '' }, preview_scroll_up = '', preview_scroll_down = '', toggle_debug = '', -- grep mode: cycle between plain text, regex, and fuzzy search cycle_grep_modes = '', -- goes to the previous query in history cycle_previous_query = '', -- multi-select keymaps for quickfix toggle_select = '', send_to_quickfix = '', -- this are specific for the normal mode (you can exit it using any other keybind like jj) focus_list = 'l', focus_preview = '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, -- Show file info panel in preview show_scores = false, -- Show scores inline in the UI }, 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 }, }) ``` ### 核心功能 #### 可用方法 ``` require('fff').find_files() -- Find files in current 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 list 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 ``` 只需跳转到定义处,看看还有哪些暴露的 API,我们有很多 #### 命令 FFF.nvim 提供了几个用于与文件选择器交互的命令: - `:FFFScan` - 手动触发重新扫描当前目录中的文件 - `:FFFRefreshGit` - 手动刷新所有文件的 git 状态 - `:FFFClearCache [all|frecency|files]` - 清除各种缓存 - `:FFFHealth` - 检查 FFF 健康状态和依赖项 - `:FFFDebug [on|off|toggle]` - 切换分数显示调试 - `:FFFOpenLog` - 在新标签页中打开 FFF 日志文件 #### 调试模式 切换评分信息显示: - 在选择器中按 `F2` - 使用 `:FFFDebug` 命令 - 通过 `debug.show_scores = true` 默认启用 #### 多选和 Quickfix 集成 选择多个文件并将其发送到 Neovim 的 quickfix 列表(快捷键可配置): - `` - 切换当前文件的选择状态(在标号列显示粗边框 `▊`) - `` - 将选中的文件发送到 quickfix 列表并关闭选择器 #### Live Grep 搜索模式 Live grep 支持三种搜索模式,通过 `` 循环切换: - **纯文本** (Plain text) (默认) - 查询内容按字面意思匹配。特殊正则字符如 `.`、`*`、`(`、`)`、`$` 没有特殊含义。这是搜索包含正则元字符的代码时最安全的模式。 - **正则** (Regex) - 查询内容被解释为正则表达式。支持字符类 (`[a-z]`)、量词 (`+`, `*`, `{n}`)、交替 (`foo|bar`)、锚点 (`^`, `$`)、单词边界 (`\b`) 等。 - **模糊** (Fuzzy) - 使用 Smith-Waterman 算法进行模糊匹配查询。兼容拼写错误和分散的字符(例如,“mtxlk” 匹配 “mutex_lock”)。结果会经过质量阈值过滤,以避免过于模糊的匹配。 当前模式显示在输入框的右侧(例如 `plain`、`regex`、`fuzzy`),并带有颜色编码的高亮显示。 你可以在配置中全局自定义哪些模式可用及其循环顺序,或者在调用 `live_grep()` 时单独设置。 **全局配置:** ``` require('fff').setup({ grep = { modes = { 'plain', 'regex' }, -- Only plain and regex, no fuzzy } }) ``` **单次调用配置:** ``` -- 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' }) ``` 当只配置了一种模式时,模式指示器会完全隐藏,且循环快捷键将不起作用。 #### 约束条件 你可以使用多种约束条件来优化 grep 和文件搜索模式下的搜索: - `git:modified` - 仅显示已修改的文件 (可选值: `modified`, `staged`, `deleted`, `renamed`, `untracked`, `ignored` 之一) - `test/` - 任意 test/ 目录下的任何深层嵌套子项 - `!something` - 排除匹配 something 的结果 - `!test/`, `!git:modified` - 与任何其他约束条件组合使用可实现否定效果 - `./**/*.{rs,lua}` - 任何有效的 glob 表达式,通过 [最快的 glob 库](https://github.com/dmtrKovalenko/zlob) 支持 仅适用于 grep: - `*.md`, `*.{c,h}` - 扩展名过滤 - `src/main.rs` - 在单个文件中 grep 此外,所有约束条件可以组合使用,例如: ``` git:modified src/**/*.rs !src/**/mod.rs user controller ``` 这将找到符合所有约束条件的文件,并且: - 同时匹配 user 和 controller(文件模式) - 匹配 "user controller"(grep 模式) #### 跨模式建议 当搜索未返回结果时,FFF 会自动查询相反的搜索模式并将结果显示为建议: - **文件搜索无匹配** → 显示同一查询的推荐**内容匹配**(grep 结果) - **Grep 搜索无匹配** → 显示同一查询的推荐**文件名匹配** 建议项会清晰地标记有“未找到结果。建议...”横幅(使用 `hl.suggestion_header` 高亮)。你可以像浏览普通结果一样导航并选择建议项 —— 选择一个 grep 建议将在匹配行处打开文件。 #### Git 状态高亮 FFF 与 git 集成,通过标号列指示符(默认启用)和可选的文件名文本着色来显示文件状态。 **标号列指示符** (Sign Column Indicators)(默认启用) - 在标号列中显示边框字符: ``` 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)(可选) - 根据 git 状态为文件名应用颜色: 要启用 git 状态文本着色,请设置 `git.status_text_color = true`: ``` 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 } }) ``` 该插件提供了合理的默认高亮组,链接到常见的 git 高亮组(例如 GitSignsAdd, GitSignsChange)。你可以使用自定义高亮组覆盖它们,以匹配你的配色方案。 **示例 - 自定义明亮文本颜色:** ``` 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', } }) ``` #### 文件过滤 FFF.nvim 自动遵守 `.gitignore` 模式。要在不修改 `.gitignore` 的情况下从选择器中过滤文件,请在项目根目录下创建一个 `.ignore` 文件: ``` # 排除所有 markdown 文件 *.md # 排除特定子目录 docs/archive/**/*.md ``` 如需强制重新扫描,请运行 `:FFFScan`。 ### 故障排除 #### 健康检查 运行 `:FFFHealth` 检查 FFF.nvim 及其依赖项的状态。这将验证: - 文件选择器初始化状态 - 可选依赖项 (git, 图片预览工具) - 数据库连接 #### 查看日志 如果遇到问题,请检查日志文件: ``` :FFFOpenLog ``` 或手动打开位于 `~/.local/state/nvim/log/fff.log` 的日志文件(默认位置)。
标签:AI代理, DLL 劫持, HTTP工具, MCP, Neovim, rizin, Rust, 人工智能, 代码导航, 代码编辑器, 内存增强, 可视化界面, 大语言模型, 威胁情报, 开发效率, 开发者工具, 性能优化, 插件, 文件搜索, 文件检索, 检测绕过, 模糊匹配, 模糊查找, 生产力工具, 用户模式Hook绕过, 网络可观测性, 网络安全审计, 网络流量审计, 通知系统