From 1ca3876f2e9a7d91908f1b70aaeaef3f8ea2bfcc Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Sun, 23 Aug 2026 21:31:49 -0700 Subject: [PATCH] feat(fff-nvim): support user-defined picker input mappings --- README.md | 4 ++++ lua/fff/conf.lua | 35 ++++++++++++++++++++++++++++++++ lua/fff/picker_ui/ui_creator.lua | 7 +++++++ 3 files changed, 46 insertions(+) diff --git a/README.md b/README.md index d825fdc33..15acbdd1b 100644 --- a/README.md +++ b/README.md @@ -362,6 +362,10 @@ require('fff').setup({ focus_list = 'l', focus_preview = 'p', }, + -- extra keymaps for the picker input, keyed by mode, applied over the built-ins + mappings = { + -- i = { [''] = function() vim.api.nvim_input('') end }, + }, frecency = { enabled = true, db_path = vim.fn.stdpath('cache') .. '/fff_nvim', diff --git a/lua/fff/conf.lua b/lua/fff/conf.lua index 0b7ad0b06..1915e51e3 100644 --- a/lua/fff/conf.lua +++ b/lua/fff/conf.lua @@ -44,6 +44,8 @@ local M = {} --- @field focus_list string --- @field focus_preview string +--- @alias FffMappingsConfig table> + --- @class FffFrecencyConfig --- @field enabled boolean --- @field db_path string @@ -82,6 +84,7 @@ local M = {} --- @field layout FffLayoutConfig --- @field preview FffPreviewConfig --- @field keymaps FffKeymapsConfig +--- @field mappings FffMappingsConfig extra keymaps for the picker input, keyed by mode --- @field hl table --- @field frecency FffFrecencyConfig --- @field history FffHistoryConfig @@ -205,6 +208,33 @@ local function fallback_hl(name) return resolved_hl or name[#name] end +-- Drops malformed user mappings so a bad entry can't break picker creation +local function sanitize_mappings(mappings) + if type(mappings) ~= 'table' then return {} end + + for mode, maps in pairs(mappings) do + if type(mode) ~= 'string' or type(maps) ~= 'table' then + vim.notify( + ('fff: ignoring mappings[%s], expected a table of keymaps'):format(vim.inspect(mode)), + vim.log.levels.WARN + ) + mappings[mode] = nil + else + for lhs, rhs in pairs(maps) do + if type(lhs) ~= 'string' or not vim.tbl_contains({ 'string', 'function' }, type(rhs)) then + vim.notify( + ('fff: ignoring mappings.%s[%s], rhs must be a string or function'):format(mode, vim.inspect(lhs)), + vim.log.levels.WARN + ) + maps[lhs] = nil + end + end + end + end + + return mappings +end + local function init() local config = vim.g.fff or {} local default_config = { @@ -294,6 +324,9 @@ local function init() focus_list = 'l', focus_preview = 'p', }, + -- extra keymaps for the picker input, keyed by mode, e.g. + -- mappings = { i = { [''] = function() vim.api.nvim_input('') end } } + mappings = {}, hl = { border = 'FloatBorder', normal = 'NormalFloat', @@ -461,6 +494,8 @@ local function init() merged_config.debug.show_file_info = default_sections end + merged_config.mappings = sanitize_mappings(merged_config.mappings) + state.config = merged_config end diff --git a/lua/fff/picker_ui/ui_creator.lua b/lua/fff/picker_ui/ui_creator.lua index 502057b10..68b7db44a 100644 --- a/lua/fff/picker_ui/ui_creator.lua +++ b/lua/fff/picker_ui/ui_creator.lua @@ -462,6 +462,13 @@ function M.setup_keymaps() set_keymap('n', keymaps.send_to_quickfix, P.send_to_quickfix, preview_opts) end + -- Applied last so a user mapping wins over the built-in on the same lhs. + for mode, maps in pairs(S.config.mappings or {}) do + for lhs, rhs in pairs(maps) do + set_keymap(mode, lhs, rhs, input_opts) + end + end + vim.api.nvim_buf_attach(S.input_buf, false, { on_lines = function() vim.schedule(function() P.on_input_change() end)