Skip to content

Commit 7fbe515

Browse files
committed
fix(live): use fzf start event
refactor(resume): set_protected, actions convert chore: deprecate fzf < 0.36, it's been out since Jan 2023 ci: api test adjustments
1 parent 32e19e0 commit 7fbe515

11 files changed

Lines changed: 76 additions & 64 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim)
5656
### Dependencies
5757

5858
- [`neovim`](https://github.com/neovim/neovim/releases) version >= `0.9`
59-
- [`fzf`](https://github.com/junegunn/fzf) version > `0.25`
59+
- [`fzf`](https://github.com/junegunn/fzf) version > `0.36`
6060
or [`skim`](https://github.com/skim-rs/skim) binary installed
6161
- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons)
6262
or [mini.icons](https://github.com/echasnovski/mini.icons)

doc/fzf-lua.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*fzf-lua.txt* For Neovim >= 0.9.0 Last change: 2025 June 28
1+
*fzf-lua.txt* For Neovim >= 0.9.0 Last change: 2025 July 12
22

33
==============================================================================
44
Table of Contents *fzf-lua-table-of-contents*
@@ -100,7 +100,7 @@ Using lazy.nvim <https://github.com/folke/lazy.nvim>
100100
DEPENDENCIES *fzf-lua-dependencies*
101101

102102
- `neovim` <https://github.com/neovim/neovim/releases> version >= `0.9`
103-
- `fzf` <https://github.com/junegunn/fzf> version > `0.25` or `skim`
103+
- `fzf` <https://github.com/junegunn/fzf> version > `0.35` or `skim`
104104
<https://github.com/skim-rs/skim> binary installed
105105
- nvim-web-devicons <https://github.com/nvim-tree/nvim-web-devicons> or
106106
mini.icons <https://github.com/echasnovski/mini.icons> (optional)
@@ -1146,7 +1146,7 @@ CUSTOMIZATION *fzf-lua-customization*
11461146
},
11471147
quickfix = {
11481148
file_icons = true,
1149-
only_valid = false, -- select among only the valid quickfix entries
1149+
valid_only = false, -- select among only the valid quickfix entries
11501150
},
11511151
quickfix_stack = {
11521152
prompt = "Quickfix Stack> ",
@@ -1624,4 +1624,4 @@ I missed your name feel free to contact me and I'll add it below:
16241624
previewer code while using nvim-bqf
16251625
<https://github.com/kevinhwang91/nvim-bqf>
16261626

1627-
vim:tw=78:ts=8:ft=help:norl:
1627+
vim:tw=78:ts=8:ft=help:norl:

lua/fzf-lua/config.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,8 @@ function M.normalize_opts(opts, globals, __resume_key)
671671
if not opts.__FZF_VERSION then
672672
utils.err(string.format("'fzf --version' failed with error %s: %s", rc, err))
673673
return nil
674-
elseif not utils.has(opts, "fzf", { 0, 25 }) then
675-
utils.err(string.format("fzf version %s is lower than minimum (0.25), aborting.",
674+
elseif not utils.has(opts, "fzf", { 0, 36 }) then
675+
utils.err(string.format("fzf version %s is lower than minimum (0.36), aborting.",
676676
utils.ver2str(opts.__FZF_VERSION)))
677677
return nil
678678
end

lua/fzf-lua/core.lua

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,10 @@ M.fzf_exec = function(contents, opts)
155155
-- the API accepts both tables and functions which we "stringify"
156156
-- We also send string commands as stringify is also responsible
157157
-- for multiprocess wrapping of shell commands with processing
158-
contents = contents and shell.stringify(contents, opts) or nil
158+
shell.clear_protected()
159+
contents = contents and shell.stringify(contents, opts, nil, true) or nil
159160
assert(contents == nil or type(contents) == "string", "contents must be of type string")
160-
return M.fzf_wrap(contents, opts)
161+
return M.fzf_wrap(contents, opts, true)
161162
end
162163

163164
---@param contents string|fun(query: string): string|string[]|function
@@ -172,12 +173,14 @@ M.fzf_live = function(contents, opts)
172173
-- AKA "live": fzf acts as a selector only (fuzzy matching is disabled)
173174
-- each keypress reloads fzf's input usually based on the typed query
174175
-- utilizes fzf's 'change:reload' event or skim's "interactive" mode
175-
opts.fn_reload = shell.stringify(contents, opts)
176+
-- convert "reload" actions to fzf's `reload` binds
177+
-- convert "exec_silent" actions to fzf's `execute-silent` binds
178+
shell.clear_protected()
179+
opts.fn_reload = shell.stringify(contents, opts, nil, true)
176180
local fzf_field_index = M.fzf_field_index(opts)
177181
local cmd = M.expand_query(opts.fn_reload, fzf_field_index)
178-
opts = M.setup_fzf_interactive_flags(cmd, fzf_field_index, opts)
179-
contents = opts.__fzf_init_cmd
180-
return M.fzf_wrap(contents, opts)
182+
contents, opts = M.setup_fzf_interactive_flags(cmd, fzf_field_index, opts)
183+
return M.fzf_wrap(contents, opts, true)
181184
end
182185

183186
M.fzf_resume = function(opts)
@@ -191,15 +194,19 @@ M.fzf_resume = function(opts)
191194
assert(opts == config.__resume_data.opts)
192195
opts = M.set_header(opts, opts.headers or {})
193196
opts.cwd = opts.cwd and libuv.expand(opts.cwd) or nil
194-
opts.__resuming = true
195-
M.fzf_exec(config.__resume_data.contents, config.__resume_data.opts)
197+
M.fzf_wrap(config.__resume_data.contents, config.__resume_data.opts)
196198
end
197199

198200
---@param contents string?
199201
---@param opts table
202+
---@param convert_actions boolean?
200203
---@return thread, string, table
201-
M.fzf_wrap = function(contents, opts)
204+
M.fzf_wrap = function(contents, opts, convert_actions)
202205
opts = opts or {}
206+
if convert_actions and type(opts.actions) == "table" then
207+
opts = M.convert_reload_actions(contents, opts)
208+
opts = M.convert_exec_silent_actions(opts)
209+
end
203210
local _co
204211
local wrapped = coroutine.wrap(function()
205212
_co = coroutine.running()
@@ -316,11 +323,6 @@ M.fzf = function(contents, opts)
316323
opts.actions[k] = actions.dummy_abort
317324
end
318325
end
319-
if not opts.__resuming then
320-
-- `opts.__resuming` is only set from `fzf_resume`, since we
321-
-- not resuming clear the shell protected functions registry
322-
shell.clear_protected()
323-
end
324326
-- store last call opts for resume
325327
config.resume_set(nil, opts.__call_opts, opts)
326328
-- caller specified not to resume this call (used by "builtin" provider)
@@ -393,10 +395,6 @@ M.fzf = function(contents, opts)
393395

394396
fzf_win:attach_previewer(previewer)
395397
local fzf_bufnr = fzf_win:create()
396-
-- convert "reload" actions to fzf's `reload` binds
397-
-- convert "exec_silent" actions to fzf's `execute-silent` binds
398-
opts = M.convert_reload_actions(contents, opts)
399-
opts = M.convert_exec_silent_actions(opts)
400398
local selected, exit_code = fzf.raw_fzf(contents, M.build_fzf_cli(opts, fzf_win),
401399
{
402400
fzf_bin = opts.fzf_bin,
@@ -998,7 +996,7 @@ local patch_shell_action = function(v, opts)
998996
items = (zero_matched and zero_selected) and {} or items
999997
end
1000998
v.fn(items, opts)
1001-
end, opts, field_index)
999+
end, opts, field_index, true)
10021000
end
10031001

10041002
-- converts actions defined with "reload=true" to use fzf's `reload` bind
@@ -1084,7 +1082,8 @@ M.convert_reload_actions = function(reload_cmd, opts)
10841082
-- NOTE: this fixes existence of both load as function and rebind, e.g. git_status with:
10851083
-- setup({ keymap = { fzf = { true, load = function() _G._fzf_load_called = true end } } }
10861084
if type(opts.keymap.fzf.load) == "function" then
1087-
opts.keymap.fzf.load = "execute-silent:" .. shell.stringify_data(opts.keymap.fzf.load, opts)
1085+
opts.keymap.fzf.load = "execute-silent:"
1086+
.. shell.stringify_data(opts.keymap.fzf.load, opts, nil, true)
10881087
end
10891088
if rebind and type(opts.keymap.fzf.load) == "string" then
10901089
return string.format("%s+%s", rebind, opts.keymap.fzf.load)
@@ -1148,7 +1147,7 @@ end
11481147
---@param command string
11491148
---@param fzf_field_index string
11501149
---@param opts table
1151-
---@return table
1150+
---@return string?, table
11521151
M.setup_fzf_interactive_flags = function(command, fzf_field_index, opts)
11531152
-- query cannot be 'nil'
11541153
opts.query = opts.query or ""
@@ -1190,8 +1189,6 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_index, opts)
11901189
)
11911190

11921191
if opts._is_skim then
1193-
-- skim interactive mode does not need a piped command
1194-
opts.__fzf_init_cmd = nil
11951192
opts.prompt = opts.__prompt or opts.prompt or opts.fzf_opts["--prompt"]
11961193
if opts.prompt then
11971194
opts.fzf_opts["--prompt"] = opts.prompt:match("[^%*]+")
@@ -1215,10 +1212,6 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_index, opts)
12151212
opts._fzf_cli_args = string.format("--interactive --cmd %s",
12161213
libuv.shellescape(no_query_condi .. reload_command))
12171214
else
1218-
-- **send an empty table to avoid running $FZF_DEFAULT_COMMAND
1219-
-- The above seems to create a hang in some systems
1220-
-- use `true` as $FZF_DEFAULT_COMMAND instead (#510)
1221-
opts.__fzf_init_cmd = utils.shell_nop()
12221215
if opts.exec_empty_query or (opts.query and #opts.query > 0) then
12231216
local q = not utils.__IS_WINDOWS and opts.query
12241217
or libuv.escape_fzf(opts.query, utils.has(opts, "fzf", { 0, 52 }) and 0.52 or 0)
@@ -1234,9 +1227,13 @@ M.setup_fzf_interactive_flags = function(command, fzf_field_index, opts)
12341227
end
12351228
opts._fzf_cli_args = string.format("--bind=%s", libuv.shellescape(
12361229
string.format("change:reload:%s%s", no_query_condi, reload_command)))
1230+
if utils.has(opts, "fzf", { 0, 35 }) then
1231+
opts._fzf_cli_args = opts._fzf_cli_args .. string.format(" --bind=%s",
1232+
libuv.shellescape(string.format("start:reload:%s%s", no_query_condi, reload_command)))
1233+
end
12371234
end
12381235

1239-
return opts
1236+
return utils.shell_nop(), opts
12401237
end
12411238

12421239
-- query placeholder for "live" queries

lua/fzf-lua/previewer/builtin.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ function Previewer.base:cmdline(_)
454454
-- save last entry even if we don't display
455455
self.last_entry = entry
456456
return ""
457-
end, self.opts, "{} {q} {n}")
457+
end, self.opts, "{} {q} {n}", false)
458458
return act
459459
end
460460

@@ -481,7 +481,7 @@ function Previewer.base:zero(_)
481481
self.last_entry = nil
482482
vim.fn.delete(self._zero_lock, "d")
483483
end, self.delay)
484-
end, self.opts, ""))
484+
end, self.opts, "", false))
485485
return act
486486
end
487487

lua/fzf-lua/previewer/fzf.lua

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ function Previewer.cmd:action(o)
8787
local act = shell.stringify_data(function(items, _, _)
8888
local entry = path.entry_to_file(items[1], self.opts)
8989
return entry.bufname or entry.path
90-
end, self.opts, self.opts.field_index_expr or "{}")
90+
end, self.opts, self.opts.field_index_expr or "{}", false)
9191
return act
9292
end
9393

@@ -214,7 +214,7 @@ function Previewer.cmd_async:cmdline(o)
214214
local cmd = errcmd or ("%s %s %s"):format(
215215
self.cmd, self.args, libuv.shellescape(filepath))
216216
return cmd
217-
end, self.opts, "{} {q}")
217+
end, self.opts, "{} {q}", false)
218218
return act
219219
end
220220

@@ -278,7 +278,7 @@ function Previewer.bat_async:cmdline(o)
278278
line_range,
279279
libuv.shellescape(filepath))
280280
return cmd
281-
end, self.opts, "{} {q}")
281+
end, self.opts, "{} {q}", false)
282282
return act
283283
end
284284

@@ -379,7 +379,7 @@ function Previewer.git_diff:cmdline(o)
379379
-- cmd = string.format("%s %s %s", table.concat(setenv, " "), cmd, pager)
380380
cmd = string.format("%s %s", cmd, pager)
381381
return { cmd = cmd, env = env }
382-
end, self.opts, "{}")
382+
end, self.opts, "{}", false)
383383
return act
384384
end
385385

@@ -398,7 +398,7 @@ function Previewer.man_pages:cmdline(o)
398398
local manpage = require("fzf-lua.providers.manpages").manpage_sh_arg(items[1])
399399
local cmd = self.cmd:format(manpage)
400400
return cmd
401-
end, self.opts, "{}")
401+
end, self.opts, "{}", false)
402402
return act
403403
end
404404

@@ -435,7 +435,7 @@ function Previewer.help_tags:cmdline(o)
435435
end
436436
end
437437
return cmd
438-
end, self.opts, "{}")
438+
end, self.opts, "{}", false)
439439
return act
440440
end
441441

lua/fzf-lua/profiles/telescope.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ end
88
return {
99
{ "default-title" }, -- base profile
1010
desc = "match telescope default highlights|keybinds",
11-
fzf_opts = { ["--layout"] = "default", ["--marker"] = "+" , ["--cycle"] = true},
11+
fzf_opts = { ["--layout"] = "default", ["--marker"] = "+", ["--cycle"] = true },
1212
winopts = {
1313
width = 0.8,
1414
height = 0.9,

lua/fzf-lua/rpc.lua

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ local rpc_nvim_exec_lua = function(opts)
8282
vim.fn.chanclose(chan_id)
8383
end)
8484

85-
if not success or opts.debug then
85+
if not success or opts.debug == "v" or opts.debug == 2 then
8686
io.stderr:write(("[DEBUG] debug = %s\n"):format(opts.debug))
8787
io.stderr:write(("[DEBUG] function ID = %d\n"):format(opts.fnc_id))
8888
io.stderr:write(("[DEBUG] fzf_lua_server = %s\n"):format(opts.fzf_lua_server))
@@ -107,7 +107,18 @@ local args = vim.deepcopy(_G.arg)
107107
args[0] = nil -- remove filename
108108
local opts = {
109109
fnc_id = tonumber(table.remove(args, 1)),
110-
debug = table.remove(args, 1) == "true",
110+
debug = (function()
111+
local ret = table.remove(args, 1)
112+
if ret == "nil" then
113+
return nil
114+
elseif ret == "true" then
115+
return true
116+
elseif ret == "false" then
117+
return false
118+
else
119+
return tonumber(ret) or tostring(ret)
120+
end
121+
end)(),
111122
fzf_selection = args,
112123
fzf_lua_server = vim.env.FZF_LUA_SERVER or vim.env.SKIM_FZF_LUA_SERVER or vim.env.NVIM,
113124
}

lua/fzf-lua/shell.lua

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ end
235235
---Fzf field index expression, e.g. "{+}" (selected), "{q}" (query)
236236
---@param fzf_field_index string?
237237
---@return string, integer?
238-
M.stringify = function(contents, opts, fzf_field_index)
238+
M.stringify = function(contents, opts, fzf_field_index, protect)
239239
assert(contents, "must supply contents")
240240

241241
-- TODO: should we let this assert?
@@ -411,20 +411,27 @@ M.stringify = function(contents, opts, fzf_field_index)
411411
end
412412
end, fzf_field_index or "", opts.debug)
413413

414-
M.set_protected(id)
414+
-- "protect" a function ptr, cleared when opening a new picker in `core.fzf()`
415+
-- protected functions include init (content) commands and acrions (reload
416+
-- and execute-silent), previewer trigger commands are excluded (zero event
417+
-- and preview callback)
418+
if protect then
419+
M.set_protected(id)
420+
end
421+
415422
return cmd, id
416423
end
417424

418-
M.stringify_cmd = function(fn, opts, fzf_field_index)
425+
M.stringify_cmd = function(fn, opts, fzf_field_index, protect)
419426
assert(type(fn) == "function", "fn must be of type function")
420427
return M.stringify(fn, {
421428
__stringify_cmd = true,
422429
PidObject = utils.pid_object("__stringify_cmd_pid", opts),
423430
debug = opts.debug,
424-
}, fzf_field_index)
431+
}, fzf_field_index, protect)
425432
end
426433

427-
M.stringify_data = function(fn, opts, fzf_field_index)
434+
M.stringify_data = function(fn, opts, fzf_field_index, protect)
428435
assert(type(fn) == "function", "fn must be of type function")
429436
return M.stringify(function(cb, _, ...)
430437
local ret = fn(...)
@@ -436,7 +443,7 @@ M.stringify_data = function(fn, opts, fzf_field_index)
436443
cb(tostring(ret))
437444
end
438445
cb(nil)
439-
end, { debug = opts.debug }, fzf_field_index)
446+
end, { debug = opts.debug }, fzf_field_index, protect)
440447
end
441448

442449
return M

lua/fzf-lua/test/helpers.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,11 @@ M.FzfLua = setmetatable({}, {
422422
if ci_opts.__postprocess_wait then
423423
opts.fn_postprocess = opts.multiprocess
424424
and [[return function(opts)
425-
vim.uv.sleep(100)
426425
local chan_id = vim.fn.sockconnect("pipe", _G._fzf_lua_server, { rpc = true })
427426
vim.rpcrequest(chan_id, "nvim_exec_lua", "_G._fzf_postprocess_called=true", {})
428427
vim.fn.chanclose(chan_id)
429428
end]]
430-
or [[return function(_) vim.uv.sleep(100); _G._fzf_postprocess_called = true end]]
429+
or [[return function(_) _G._fzf_postprocess_called = true end]]
431430
end
432431

433432
-- Stringify supplied opts

0 commit comments

Comments
 (0)