Skip to content

Commit 9b55e49

Browse files
committed
feat: new "global" picker (closes #2058)
1 parent 279ed58 commit 9b55e49

7 files changed

Lines changed: 121 additions & 4 deletions

File tree

lua/fzf-lua/config.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ function M.normalize_opts(opts, globals, __resume_key)
382382
end
383383

384384
-- `fzf_cli_args` is string, `_fzf_cli_args` is a table used internally
385-
opts._fzf_cli_args = {}
385+
opts._fzf_cli_args = type(opts._fzf_cli_args) == "table" and opts._fzf_cli_args or {}
386386

387387
-- backward compatibility, rhs overrides lhs
388388
-- (rhs being the "old" option)

lua/fzf-lua/defaults.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,21 @@ M.defaults.files = {
360360
winopts = { preview = { winopts = { cursorline = false } } },
361361
}
362362

363+
M.defaults.global = vim.tbl_deep_extend("force", M.defaults.files, {
364+
cwd_prompt = true,
365+
line_query = true,
366+
pickers = {
367+
[""] = "files",
368+
["$"] = "buffers",
369+
["@"] = "lsp_document_symbols",
370+
["#"] = "lsp_workspace_symbols",
371+
},
372+
fzf_opts = { ["--nth"] = false, ["--with-nth"] = false },
373+
winopts = { preview = { winopts = { cursorline = true } } },
374+
_ctx = { includeBuflist = true }, -- we include a buffer picker
375+
_fzf_nth_devicons = false,
376+
})
377+
363378
-- Must construct our opts table in stages
364379
-- so we can reference 'M.globals.files'
365380
M.defaults.git = {

lua/fzf-lua/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ do
289289
tmux_buffers = { "fzf-lua.providers.tmux", "buffers" },
290290
profiles = { "fzf-lua.providers.meta", "profiles" },
291291
combine = { "fzf-lua.providers.meta", "combine" },
292+
global = { "fzf-lua.providers.meta", "global" },
292293
complete_path = { "fzf-lua.complete", "path" },
293294
complete_file = { "fzf-lua.complete", "file" },
294295
complete_line = { "fzf-lua.complete", "line" },

lua/fzf-lua/profiles/default-prompt.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ return {
66
winopts = { title_pos = "center", preview = { title_pos = "center" } },
77
-- Uses `cwd_prompt` by default
88
-- files = prompt("Files"),
9+
global = prompt("Global"),
910
buffers = prompt("Buffers"),
1011
tabs = prompt("Tabs"),
1112
lines = prompt("Lines"),

lua/fzf-lua/profiles/default-title.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ end
99
return {
1010
desc = "defaults using title for picker info",
1111
winopts = { title_pos = "center", preview = { title_pos = "center" } },
12+
global = title("Global"),
1213
files = title("Files"),
1314
buffers = title("Buffers"),
1415
tabs = title("Tabs"),

lua/fzf-lua/providers/meta.lua

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local uv = vim.uv or vim.loop
22
local core = require "fzf-lua.core"
33
local path = require "fzf-lua.path"
44
local utils = require "fzf-lua.utils"
5+
local libuv = require "fzf-lua.libuv"
56
local config = require "fzf-lua.config"
67

78
local M = {}
@@ -121,7 +122,7 @@ M.combine = function(t)
121122

122123
local cmds, opts = (function()
123124
local ret, opts = {}, nil
124-
for i, p in ipairs(t.pickers) do
125+
for _, p in ipairs(t.pickers) do
125126
-- local ok, msg, cmd, o = pcall(FzfLua[p], opts1)
126127
-- if not ok or not cmd then
127128
local _, cmd, o = FzfLua[p](opts1)
@@ -148,4 +149,100 @@ M.combine = function(t)
148149
return core.fzf_wrap(contents, opts)
149150
end
150151

152+
M.global = function(opts)
153+
opts = config.normalize_opts(opts, "global")
154+
if not opts then return end
155+
156+
if opts.line_query and not utils.has(opts, "fzf", { 0, 59 }) then
157+
utils.warn("'global' requires fzf >= 0.59, reverting to files.")
158+
return FzfLua.files(opts)
159+
end
160+
161+
-- Tells fzf_wrap to not start the fzf process
162+
opts._start = false
163+
local pickers = {}
164+
local opts_copy = vim.deepcopy(opts)
165+
for c, p in pairs(opts.pickers) do
166+
if FzfLua[p] then
167+
if #c == 0 then
168+
-- Default picker opts set the tone for this picker options
169+
-- this way convert reload / exec_silent actions will use a consistent
170+
-- opts ref in the callbacks so we can later modify internal values
171+
pickers[p] = { FzfLua[p](opts) }
172+
-- Override opts with the return opts and store a copy of `pickers[]`
173+
-- as we patch the opts when switching a picker in the change event
174+
opts = pickers[p][3]
175+
opts._start = nil -- remove the start suppression
176+
pickers[p][3] = vim.deepcopy(opts)
177+
else
178+
-- Each subsequent picker gets a fresh copy of the original opts
179+
-- (unmodified by the default picker)
180+
pickers[p] = { FzfLua[p](opts_copy) }
181+
end
182+
else
183+
utils.warn(string.format("invalid picker '%s', ignoring.", p))
184+
end
185+
end
186+
187+
---@param q string
188+
---@return table?, integer?
189+
local get_picker = function(q)
190+
q = type(q) == "string" and q or ""
191+
if #q == 0 then
192+
return pickers[opts.pickers[""]], 1
193+
end
194+
for c, p in pairs(opts.pickers) do
195+
if #c > 0 and q:match("^" .. utils.lua_regex_escape(c)) then
196+
return pickers[p], #c + 1
197+
end
198+
end
199+
end
200+
201+
-- Copy all keys without destroying the parent opts ref
202+
-- or we will mess up the actions (especially hide)
203+
local patch_opts = function(t1, t2)
204+
for k, _ in pairs(t1) do
205+
if k ~= "actions" then t1[k] = nil end
206+
end
207+
for k, v in pairs(t2) do
208+
(function()
209+
if k == "actions" then return end
210+
if type(v) == "table" then
211+
t1[k] = vim.tbl_deep_extend("force", t1[k] or {}, v)
212+
else
213+
t1[k] = v
214+
end
215+
end)()
216+
end
217+
return t1
218+
end
219+
220+
-- Get starting picker
221+
local cur_picker, cur_sub = get_picker()
222+
if not cur_picker then
223+
utils.warn("default picker not found, aborting.")
224+
return
225+
end
226+
227+
table.insert(opts._fzf_cli_args, "--bind=" .. libuv.shellescape("change:+transform:"
228+
.. FzfLua.shell.stringify_data(function(args, _, _)
229+
local q = args[1]
230+
local new_picker, new_sub = get_picker(q)
231+
local reload = ""
232+
if new_picker and new_picker ~= cur_picker then
233+
-- New picker requested, reload the contents and transform
234+
-- the search string to exclude the picker prefix
235+
cur_sub = new_sub
236+
cur_picker = new_picker
237+
-- Patch the opts refs with important values for path parsing
238+
-- e.g. formatter, path_shorten, etc
239+
opts = patch_opts(opts, cur_picker[3])
240+
reload = string.format("reload(%s)+", new_picker[2])
241+
end
242+
return reload .. string.format("search(%s)", q:sub(cur_sub))
243+
end, opts, "{q}")))
244+
245+
return core.fzf_wrap(cur_picker[2], opts)
246+
end
247+
151248
return M

lua/fzf-lua/shell.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ function LRU:get(id)
8383
end
8484

8585
-- Cache should be able to hold all function callbacks of a single picker
86-
-- max cache size of 50 should be more than enough, we don't want it to be
86+
-- max cache size of 100 should be more than enough, we don't want it to be
8787
-- too big as this will prevent clearing of referecnces to "opts" which
8888
-- prevents garabage collection from freeing the resources
89-
local function new_cache(size) return LRU:new(size or 50) end
89+
-- NOTE: with combine/global the no. of callbacks has increased significantly
90+
-- so monitor the number of callbacks
91+
local function new_cache(size) return LRU:new(size or 100) end
9092
local _cache = new_cache()
9193

9294
local M = {}

0 commit comments

Comments
 (0)