Skip to content

Commit e0cca3e

Browse files
committed
refactor!: convert all content types to string
**WIP** `fzf_exec` now converts all content types (tables/funcs) to shell command strings this greatly simplifies the code as all picker inputs are now shell commands which can be used in fzf's reload, execute-silent, etc. This will also enable us to combine pickers easily with semicolon or logical and, e.g. `cmd1; cmd2` or `cmd1 && cmd2`. Fixes #2058 and prepares the ground for #1879, #2058.
1 parent e297fea commit e0cca3e

16 files changed

Lines changed: 407 additions & 513 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ previewers = {
11751175
},
11761176
quickfix = {
11771177
file_icons = true,
1178-
only_valid = false, -- select among only the valid quickfix entries
1178+
valid_only = false, -- select among only the valid quickfix entries
11791179
},
11801180
quickfix_stack = {
11811181
prompt = "Quickfix Stack> ",

lua/fzf-lua/actions.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,33 @@ local sel_to_qf = function(selected, opts, is_loclist)
347347
end
348348
end
349349

350+
M.list_del = function(selected, opts)
351+
local winid = opts.__CTX.winid
352+
local list = opts.is_loclist and vim.fn.getloclist(winid) or vim.fn.getqflist()
353+
354+
local buf_del = (function()
355+
local ret = {}
356+
for _, s in ipairs(selected) do
357+
local b = s:match("%[(%d+)%]")
358+
ret[b] = true
359+
end
360+
return ret
361+
end)()
362+
363+
local newlist = {}
364+
for _, l in ipairs(list) do
365+
if not buf_del[tostring(l.bufnr)] then
366+
table.insert(newlist, l)
367+
end
368+
end
369+
370+
if opts.is_loclist then
371+
vim.fn.setloclist(winid, newlist, "r")
372+
else
373+
vim.fn.setqflist(newlist, "r")
374+
end
375+
end
376+
350377
M.file_sel_to_qf = function(selected, opts)
351378
sel_to_qf(selected, opts)
352379
end

lua/fzf-lua/config.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ M._action_to_helpstr = {
10211021
[actions.tmux_buf_set_reg] = "set-register",
10221022
[actions.paste_register] = "paste-register",
10231023
[actions.set_qflist] = "set-{qf|loc}list",
1024+
[actions.list_del] = "list-delete",
10241025
[actions.apply_profile] = "apply-profile",
10251026
[actions.complete] = "complete",
10261027
[actions.dap_bp_del] = "dap-bp-delete",

lua/fzf-lua/core.lua

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ M.ACTION_DEFINITIONS = {
7272
},
7373
[actions.buf_del] = { "close" },
7474
[actions.arg_del] = { "delete" },
75+
[actions.list_del] = { "delete" },
7576
[actions.dap_bp_del] = { "delete" },
7677
[actions.cs_delete] = { "uninstall" },
7778
[actions.cs_update] = { "[down|re]-load" },
@@ -146,29 +147,31 @@ end
146147
---@param contents content
147148
---@param opts? {fn_reload: string|function, fn_transform: function, __fzf_init_cmd: string, _normalized: boolean}
148149
M.fzf_exec = function(contents, opts)
149-
if type(contents) == "table" and type(contents[1]) == "table" then
150-
contents = contents_from_arr(contents)
151-
end
152150
if not opts or not opts._normalized then
153151
opts = config.normalize_opts(opts or {}, {})
154152
if not opts then return end
155153
end
156-
-- save a copy of cprovider info in the opts, we later use it for better named
157-
-- quickfix lists, use `pcall` because we will circular ref main object (#776)
158-
_, opts.__INFO = pcall(loadstring("return require'fzf-lua'.get_info()"))
154+
if type(contents) == "table" and type(contents[1]) == "table" then
155+
contents = contents_from_arr(contents)
156+
end
157+
-- Save a copy of provider info in the opts, we will
158+
-- later use it for better named quickfix lists (#776)
159+
opts.__INFO = FzfLua.get_info()
160+
-- Default fzf exit callback acts upon the selected items
159161
opts.fn_selected = opts.fn_selected or function(selected, o)
160162
actions.act(selected, o)
161163
end
162-
-- wrapper for command transformer
163-
if type(contents) == "string" and (opts.fn_transform or opts.fn_preprocess) then
164-
contents = libuv.spawn_nvim_fzf_cmd({
165-
cmd = contents,
166-
cwd = opts.cwd,
167-
cb_pid = function(pid) opts.__pid = pid end,
168-
},
169-
opts.fn_transform or function(x) return x end,
170-
opts.fn_preprocess)
171-
end
164+
-- Contents sent to fzf can only be nil or a shell command (string)
165+
-- the API accepts both tables and functions which we "stringify"
166+
if contents ~= nil and type(contents) ~= "string" and not opts.__stringified then
167+
opts.__contents = contents
168+
local cmd, id = shell.stringify(opts)
169+
-- Protect function ptr from being overwritten in the circular buffer
170+
-- NOTE: will be cleared when a new picker is opened (not a resume)
171+
shell.set_protected(id)
172+
contents = cmd
173+
end
174+
assert(contents == nil or type(contents) == "string", "contents must be of type string")
172175
-- setup as "live": disables fuzzy matching and reload the content
173176
-- every keystroke (query changed), utilizes fzf's 'change:reload'
174177
-- event trigger or skim's "interactive" mode
@@ -428,7 +431,7 @@ M.fzf = function(contents, opts)
428431
local fzf_bufnr = fzf_win:create()
429432
-- convert "reload" actions to fzf's `reload` binds
430433
-- convert "exec_silent" actions to fzf's `execute-silent` binds
431-
opts = M.convert_reload_actions(opts.__reload_cmd or contents, opts)
434+
opts = M.convert_reload_actions(contents, opts)
432435
opts = M.convert_exec_silent_actions(opts)
433436
local selected, exit_code = fzf.raw_fzf(contents, M.build_fzf_cli(opts, fzf_win),
434437
{
@@ -898,22 +901,19 @@ M.mt_cmd_wrapper = function(opts)
898901
end
899902
return cmd
900903
else
901-
assert(not opts.__mt_transform or type(opts.__mt_transform) == "function")
902-
assert(not opts.__mt_preprocess or type(opts.__mt_preprocess) == "function")
903-
assert(not opts.__mt_postprocess or type(opts.__mt_postprocess) == "function")
904-
return libuv.spawn_nvim_fzf_cmd(opts,
905-
function(x)
906-
return opts.__mt_transform
907-
and opts.__mt_transform(x, opts)
908-
or make_entry.file(x, opts)
909-
end,
910-
function(o)
911-
-- setup opts.cwd and git diff files
912-
return opts.__mt_preprocess
913-
and opts.__mt_preprocess(o)
914-
or make_entry.preprocess(o)
915-
end,
916-
opts.__mt_postprocess and function(o) return opts.__mt_postprocess(o) end or nil)
904+
assert(not opts.__fn_transform or type(opts.__fn_transform) == "function")
905+
assert(not opts.__fn_preprocess or type(opts.__fn_preprocess) == "function")
906+
assert(not opts.__fn_postprocess or type(opts.__fn_postprocess) == "function")
907+
opts.__fn_transform = opts.__fn_transform == nil
908+
and function(x) return make_entry.file(x, opts) end
909+
or opts.__fn_transform
910+
opts.__fn_preprocess = opts.__fn_preprocess == nil
911+
and function(o) return make_entry.preprocess(o) end
912+
or opts.__fn_preprocess
913+
opts.__contents = opts.cmd
914+
local cmd, id = shell.stringify(opts)
915+
shell.set_protected(id)
916+
return cmd
917917
end
918918
end
919919

@@ -1431,7 +1431,7 @@ M.setup_fzf_interactive_wrap = function(opts)
14311431

14321432
-- neovim shell wrapper for parsing the query and loading contents
14331433
local fzf_field_expression = M.fzf_field_expression(opts)
1434-
local command = shell.reload_action_cmd(opts, fzf_field_expression)
1434+
local command = shell.stringify(opts, fzf_field_expression)
14351435
return M.setup_fzf_interactive_flags(command, fzf_field_expression, opts)
14361436
end
14371437

lua/fzf-lua/defaults.lua

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,13 @@ M.defaults.quickfix = {
580580
file_icons = 1,
581581
color_icons = true,
582582
git_icons = false,
583-
only_valid = false,
584-
fzf_opts = { ["--multi"] = true },
583+
valid_only = false,
584+
fzf_opts = {
585+
["--multi"] = true,
586+
["--delimiter"] = "[\\]:]",
587+
["--with-nth"] = "2..",
588+
},
589+
actions = { ["ctrl-x"] = { fn = actions.list_del, reload = true } },
585590
_actions = function() return M.globals.actions.files end,
586591
_treesitter = true,
587592
_cached_hls = { "path_colnr", "path_linenr" },
@@ -600,8 +605,13 @@ M.defaults.loclist = {
600605
file_icons = 1,
601606
color_icons = true,
602607
git_icons = false,
603-
only_valid = false,
604-
fzf_opts = { ["--multi"] = true },
608+
valid_only = false,
609+
fzf_opts = {
610+
["--multi"] = true,
611+
["--delimiter"] = "[\\]:]",
612+
["--with-nth"] = "2..",
613+
},
614+
actions = { ["ctrl-x"] = { fn = actions.list_del, reload = true } },
605615
_actions = function() return M.globals.actions.files end,
606616
_treesitter = true,
607617
_cached_hls = { "path_colnr", "path_linenr" },
@@ -828,7 +838,7 @@ M.defaults.awesome_colorschemes = {
828838
actions = {
829839
["enter"] = actions.colorscheme,
830840
["ctrl-g"] = { fn = actions.toggle_bg, exec_silent = true },
831-
["ctrl-r"] = { fn = actions.cs_update, reload = true },
841+
["ctrl-d"] = { fn = actions.cs_update, reload = true },
832842
["ctrl-x"] = { fn = actions.cs_delete, reload = true },
833843
}
834844
}

lua/fzf-lua/previewer/builtin.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1833,7 +1833,7 @@ function Previewer.nvim_options:populate_preview_buf(entry_str)
18331833

18341834
-- get_help_text might be slow. pcall to prevent errors when scrolling the list too quickly
18351835
pcall(function()
1836-
local lines = vim.list_extend(header, self:get_help_text(entry.name))
1836+
local lines = vim.list_extend(header, self:get_help_text(entry.name) or {})
18371837
vim.api.nvim_buf_set_lines(tmpbuf, 0, -1, false, lines)
18381838
self:set_preview_buf(tmpbuf)
18391839
end)

lua/fzf-lua/providers/buffers.lua

Lines changed: 58 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -187,30 +187,23 @@ M.buffers = function(opts)
187187
opts = config.normalize_opts(opts, "buffers")
188188
if not opts then return end
189189

190-
opts.__fn_reload = opts.__fn_reload or function(_)
191-
return function(cb)
192-
local filtered, _, max_bufnr = filter_buffers(opts, core.CTX().buflist)
193-
194-
if next(filtered) then
195-
local buffers = populate_buffer_entries(opts, filtered)
196-
for _, bufinfo in pairs(buffers) do
197-
local ok, entry = pcall(gen_buffer_entry, opts, bufinfo, max_bufnr)
198-
assert(ok and entry)
199-
cb(entry)
200-
end
190+
local contents = function(cb)
191+
local filtered, _, max_bufnr = filter_buffers(opts, core.CTX().buflist)
192+
193+
if next(filtered) then
194+
local buffers = populate_buffer_entries(opts, filtered)
195+
for _, bufinfo in pairs(buffers) do
196+
local ok, entry = pcall(gen_buffer_entry, opts, bufinfo, max_bufnr)
197+
assert(ok and entry)
198+
cb(entry)
201199
end
202-
cb(nil)
203200
end
201+
cb(nil)
204202
end
205203

206-
-- build the "reload" cmd and remove '-- {+}' from the initial cmd
207-
local contents, id = shell.reload_action_cmd(opts, "")
208-
opts.__reload_cmd = contents
209-
210204
-- get current tab/buffer/previous buffer
211205
-- save as a func ref for resume to reuse
212206
opts._fn_pre_fzf = function()
213-
shell.set_protected(id)
214207
core.CTX({ includeBuflist = true }) -- include `nvim_list_bufs` in context
215208
end
216209

@@ -409,79 +402,65 @@ M.tabs = function(opts)
409402
end, "", opts.debug))
410403
end
411404

412-
opts.__fn_reload = opts.__fn_reload or function(_)
413-
-- we do not return the populate function with cb directly to avoid
414-
-- E5560: nvim_exec must not be called in a lua loop callback
415-
local entries = {}
416-
local populate = function(cb)
417-
local max_bufnr = (function()
418-
local ret = 0
419-
for _, t in ipairs(vim.api.nvim_list_tabpages()) do
420-
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(t)) do
421-
local b = vim.api.nvim_win_get_buf(w)
422-
if b > ret then ret = b end
423-
end
405+
local contents = function(cb)
406+
local max_bufnr = (function()
407+
local ret = 0
408+
for _, t in ipairs(vim.api.nvim_list_tabpages()) do
409+
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(t)) do
410+
local b = vim.api.nvim_win_get_buf(w)
411+
if b > ret then ret = b end
424412
end
425-
return ret
426-
end)()
413+
end
414+
return ret
415+
end)()
427416

428-
for tabnr, tabh in ipairs(vim.api.nvim_list_tabpages()) do
429-
(function()
430-
if opts.current_tab_only and tabh ~= core.CTX().tabh then return end
431-
432-
local tab_cwd = vim.fn.getcwd(-1, tabnr)
433-
local tab_cwd_tilde = path.HOME_to_tilde(tab_cwd)
434-
local title, fn_title_hl = opt_hl(tabnr, "tab_title",
435-
function(s)
436-
return string.format("%s%s#%d%s", s, utils.nbsp, tabnr,
437-
(uv.cwd() == tab_cwd and "" or string.format(": %s", tab_cwd_tilde)))
438-
end,
439-
utils.ansi_codes[opts.hls.tab_title])
440-
441-
local marker, fn_marker_hl = opt_hl(tabnr, "tab_marker",
442-
function(s) return s end,
443-
utils.ansi_codes[opts.hls.tab_marker])
444-
445-
local tab_cwd_tilde_base64 = base64.encode(tab_cwd_tilde)
446-
if not opts.current_tab_only then
447-
cb(string.format("%s:%d:%d:0)%s%s %s",
448-
tab_cwd_tilde_base64,
449-
tabnr,
450-
tabh,
451-
utils.nbsp,
452-
fn_title_hl(title),
453-
(tabh == core.CTX().tabh) and fn_marker_hl(marker) or ""))
454-
end
417+
for tabnr, tabh in ipairs(vim.api.nvim_list_tabpages()) do
418+
(function()
419+
if opts.current_tab_only and tabh ~= core.CTX().tabh then return end
420+
421+
local tab_cwd = vim.fn.getcwd(-1, tabnr)
422+
local tab_cwd_tilde = path.HOME_to_tilde(tab_cwd)
423+
local title, fn_title_hl = opt_hl(tabnr, "tab_title",
424+
function(s)
425+
return string.format("%s%s#%d%s", s, utils.nbsp, tabnr,
426+
(uv.cwd() == tab_cwd and "" or string.format(": %s", tab_cwd_tilde)))
427+
end,
428+
utils.ansi_codes[opts.hls.tab_title])
429+
430+
local marker, fn_marker_hl = opt_hl(tabnr, "tab_marker",
431+
function(s) return s end,
432+
utils.ansi_codes[opts.hls.tab_marker])
433+
434+
local tab_cwd_tilde_base64 = base64.encode(tab_cwd_tilde)
435+
if not opts.current_tab_only then
436+
cb(string.format("%s:%d:%d:0)%s%s %s",
437+
tab_cwd_tilde_base64,
438+
tabnr,
439+
tabh,
440+
utils.nbsp,
441+
fn_title_hl(title),
442+
(tabh == core.CTX().tabh) and fn_marker_hl(marker) or ""))
443+
end
455444

456-
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(tabh)) do
457-
if tabh ~= core.CTX().tabh or core.CTX().curtab_wins[tostring(w)] then
458-
local b = filter_buffers(opts, { vim.api.nvim_win_get_buf(w) })[1]
459-
if b then
460-
local prefix = string.format("%s:%d:%d:%d)%s%s%s",
461-
tab_cwd_tilde_base64, tabnr, tabh, w, utils.nbsp, utils.nbsp, utils.nbsp)
462-
local bufinfo = populate_buffer_entries({}, { b }, w)[1]
463-
cb(gen_buffer_entry(opts, bufinfo, max_bufnr, tab_cwd, prefix))
464-
end
445+
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(tabh)) do
446+
if tabh ~= core.CTX().tabh or core.CTX().curtab_wins[tostring(w)] then
447+
local b = filter_buffers(opts, { vim.api.nvim_win_get_buf(w) })[1]
448+
if b then
449+
local prefix = string.format("%s:%d:%d:%d)%s%s%s",
450+
tab_cwd_tilde_base64, tabnr, tabh, w, utils.nbsp, utils.nbsp, utils.nbsp)
451+
local bufinfo = populate_buffer_entries({}, { b }, w)[1]
452+
cb(gen_buffer_entry(opts, bufinfo, max_bufnr, tab_cwd, prefix))
465453
end
466454
end
467-
end)()
468-
end
469-
cb(nil)
455+
end
456+
end)()
470457
end
471-
populate(function(e)
472-
if e then table.insert(entries, e) end
473-
end)
474-
return entries
458+
cb(nil)
475459
end
476460

477-
-- build the "reload" cmd and remove '-- {+}' from the initial cmd
478-
local contents, id = shell.reload_action_cmd(opts, "")
479-
opts.__reload_cmd = contents
480-
481461
-- get current tab/buffer/previous buffer
482462
-- save as a func ref for resume to reuse
483463
opts._fn_pre_fzf = function()
484-
shell.set_protected(id)
485464
core.CTX({ includeBuflist = true }) -- include `nvim_list_bufs` in context
486465
end
487466

0 commit comments

Comments
 (0)