Skip to content

Commit

Permalink
feat(profiles): added hide (improved resume, #1686)
Browse files Browse the repository at this point in the history
The profile changes the default binds to hide the fzf-lua window
instead of aborting it, therefore keeping cursor position, selection,
etc.

Note that this also continues long running operations in the background
so be mindful of sending fzf to background on a large mono repo, can
still be aborted with `ctrl-c` or `alt-esc`.

Enable with:
```lua
-- Set as base profile
:lua require("fzf-lua").setup({"hide"})
-- More than one profile
:lua require("fzf-lua").setup({"border-fused","hide"})
-- Or with `profiles`:
:FzfLua profiles load=hide
-- More than one profile
:FzfLua profiles load={"border-fused","hide"}
```
  • Loading branch information
ibhagwan committed Jan 18, 2025
1 parent a534583 commit 24980df
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 54 deletions.
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,18 @@ Alternatively, resuming work on a specific picker:
> By default pressing esc or ctrl-c terminates the fzf process,
> as such resume is not perfect and is limited to resuming the
> picker/query and sometimes additional parameters such as regex
> in grep, etc, for a more "complete" resume press alt-esc to
> hide the fzf process instead, this will keep the fzf process
> running in the background and thus will restore the process
> entirely including cursor position and selection.
> in grep, etc, for a more complete resume use the "hide" profile,
> this will keep the fzf process running in the background allowing
> `:FzfLua resume` to restore the picker state entirely, including
> cursor position and selection.
> To configure hiding by default:
> ```lua
> require("fzf-lua").setup({ keymap = { builtin = { true, ["<Esc>"] = "hide" } } })
> require("fzf-lua").setup({
> "hide",
> -- your other settings here
> })
> ```
**LIST OF AVAILABLE COMMANDS BELOW** 👇
## Commands
Expand Down Expand Up @@ -1077,10 +1079,6 @@ previewers = {
winopts = { height = 0.55, width = 0.30, },
-- uncomment to ignore colorschemes names (lua patterns)
-- ignore_patterns = { "^delek$", "^blue$" },
-- uncomment to execute a callback on preview|close
-- e.g. a call to reset statusline highlights
-- cb_preview = function() ... end,
-- cb_exit = function() ... end,
},
awesome_colorschemes = {
prompt = 'Colorschemes❯ ',
Expand All @@ -1099,9 +1097,6 @@ previewers = {
["ctrl-r"] = { fn = actions.cs_update, reload = true },
["ctrl-x"] = { fn = actions.cs_delete, reload = true },
},
-- uncomment to execute a callback on preview|close
-- cb_preview = function() ... end,
-- cb_exit = function() ... end,
},
keymaps = {
prompt = "Keymaps> ",
Expand Down Expand Up @@ -1359,6 +1354,7 @@ require('fzf-lua').setup({'fzf-vim'})
| `borderless` | borderless and minimalistic seamless look &amp; feel |
| `borderless-full` | borderless with description in window title (instead of prompt) |
| `border-fused` | single border around both fzf and the previewer |
| `hide` | send fzf process to background instead of termination |

</details>

Expand Down
4 changes: 4 additions & 0 deletions lua/fzf-lua/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,10 @@ function M.normalize_opts(opts, globals, __resume_key)
end
end

if type(opts.enrich) == "function" then
opts = opts.enrich(opts)
end

-- mark as normalized
opts._normalized = true

Expand Down
54 changes: 43 additions & 11 deletions lua/fzf-lua/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ M.create_fzf_binds = function(opts)
-- Separate "transform|execute|execute-silent" binds to their own `--bind` argument, this
-- way we can use `transform:...` and not be forced to use brackets, i.e. `transform(...)`
-- this enables us to use brackets in the inner actions, e.g. "zero:transform:rebind(...)"
if action:match("transform") or action:match("execute") then
if action:match("transform") or action:match("execute") or action:match("reload") then
table.insert(separate, bind)
else
table.insert(combine, bind)
Expand Down Expand Up @@ -1103,14 +1103,20 @@ M.convert_reload_actions = function(reload_cmd, opts)
local shell_action = shell.raw_action(function(items, _, _)
v.fn(items, opts)
end, v.field_index == false and "" or v.field_index or "{+}", opts.debug)
if type(v.prefix) == "string" and not v.prefix:match("%+$") then
v.prefix = v.prefix .. "+"
end
if type(v.postfix) == "string" and not v.postfix:match("^%+") then
v.prefix = "+" .. v.postfix
end
opts.keymap.fzf[k] = {
string.format("%s%sexecute-silent(%s)+reload(%s)%s",
type(v.prefix) == "string" and v.prefix or "",
unbind and (unbind .. "+") or "",
shell_action,
reload_cmd,
type(v.postfix) == "string" and v.postfix or ""),
desc = config.get_action_helpstr(v.fn)
desc = v.desc or config.get_action_helpstr(v.fn)
}
opts.actions[k] = nil
end
Expand All @@ -1125,25 +1131,51 @@ end
---@param opts table
---@return table
M.convert_exec_silent_actions = function(opts)
-- Does not work with fzf version < 0.36, fzf fails with
-- "error 2: bind action not specified:"
if not utils.has(opts, "fzf", { 0, 36 })
or utils.has(opts, "sk") then
-- `execute-silent` actions are bugged with skim (can't use quotes)
if utils.has(opts, "sk") then
return opts
end
for k, v in pairs(opts.actions) do
if type(v) == "table" and v.exec_silent then
assert(type(v.fn) == "function")
local field_index = v.field_index == false and "" or v.field_index or "{q} {+}"
-- replace the action with shell cmd proxy to the original action
local shell_action = shell.raw_action(function(items, _, _)
if field_index:match("^{q}") then
local query = table.remove(items, 1)
config.resume_set("query", query, opts)
end
v.fn(items, opts)
end, v.field_index == false and "" or v.field_index or "{+}", opts.debug)
end, field_index, opts.debug)
if type(v.prefix) == "string" and not v.prefix:match("%+$") then
v.prefix = v.prefix .. "+"
end
if type(v.postfix) == "string" and not v.postfix:match("^%+") then
v.postfix = "+" .. v.postfix
end
-- `execute-silent(...)` with fzf version < 0.36, errors with:
-- 'error 2: bind action not specified' (due to inner brackets)
-- changing to `execute-silent:...` removes the need to care for
-- brackets within the command with the limitation of not using
-- potfix (must be the last part of the arg), from `man fzf`:
--
-- action-name:...
-- The last one is the special form that frees you from parse
-- errors as it does not expect the closing character. The catch is
-- that it should be the last one in the comma-separated list of
-- key-action pairs.
--
local has_fzf036 = utils.has(opts, "fzf", { 0, 36 })
opts.keymap.fzf[k] = {
string.format("%sexecute-silent(%s)%s",
string.format("%sexecute-silent%s%s",
type(v.prefix) == "string" and v.prefix or "",
shell_action,
type(v.postfix) == "string" and v.postfix or ""),
desc = config.get_action_helpstr(v.fn)
-- prefer "execute-silent:..." unless we have postfix
has_fzf036 and type(v.postfix) == "string"
and string.format("(%s)", shell_action)
or string.format(":%s", shell_action),
-- can't use postfix since we use "execute-silent:..."
has_fzf036 and type(v.postfix) == "string" and v.postfix or ""),
desc = v.desc or config.get_action_helpstr(v.fn)
}
opts.actions[k] = nil
end
Expand Down
2 changes: 1 addition & 1 deletion lua/fzf-lua/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ M.defaults.git = {
prompt = "Branches> ",
cmd = "git branch --all --color",
preview = "git log --graph --pretty=oneline --abbrev-commit --color {1}",
remotes = "local",
remotes = "local",
actions = {
["enter"] = actions.git_switch,
["ctrl-x"] = { fn = actions.git_branch_del, reload = true },
Expand Down
11 changes: 6 additions & 5 deletions lua/fzf-lua/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ end
-- case the user decides not to call `setup()`
M.setup_highlights()

local function load_profiles(profiles)
function M.load_profiles(profiles, silent, opts)
local ret = {}
profiles = type(profiles) == "table" and profiles
or type(profiles) == "string" and { profiles }
Expand All @@ -155,15 +155,15 @@ local function load_profiles(profiles)
-- backward compat, renamed "borderless_full" > "borderless-full"
if profile == "borderless_full" then profile = "borderless-full" end
local fname = path.join({ vim.g.fzf_lua_directory, "profiles", profile .. ".lua" })
local profile_opts = utils.load_profile_fname(fname, nil, 1)
local profile_opts = utils.load_profile_fname(fname, nil, silent)
if type(profile_opts) == "table" then
if profile_opts[1] then
-- profile requires loading base profile(s)
profile_opts = vim.tbl_deep_extend("keep",
profile_opts, load_profiles(profile_opts[1]))
profile_opts, M.load_profiles(profile_opts[1], silent, opts))
end
if type(profile_opts.fn_load) == "function" then
profile_opts.fn_load()
profile_opts.fn_load(opts)
profile_opts.fn_load = nil
end
ret = vim.tbl_deep_extend("force", ret, profile_opts)
Expand All @@ -178,7 +178,7 @@ function M.setup(opts, do_not_reset_defaults)
opts[1] = opts[1] == nil and utils.__HAS_NVIM_09 and "default-title" or opts[1]
if opts[1] then
-- Did the user supply profile(s) to load?
opts = vim.tbl_deep_extend("keep", opts, load_profiles(opts[1]))
opts = vim.tbl_deep_extend("keep", opts, M.load_profiles(opts[1], 1, opts))
end
if do_not_reset_defaults then
-- no defaults reset requested, merge with previous setup options
Expand Down Expand Up @@ -384,6 +384,7 @@ M._exported_modules = {
M._excluded_meta = {
"setup",
"redraw",
"load_profiles",
"fzf",
"fzf_raw",
"fzf_wrap",
Expand Down
1 change: 1 addition & 0 deletions lua/fzf-lua/profiles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ telescope defaults with `bat` previewer:
| `borderless` | borderless and minimalistic seamless look &amp; feel |
| `borderless-full` | borderless with description in window title (instead of prompt) |
| `border-fused` | single border around both fzf and the previewer |
| `hide` | send fzf process to background instead of termination |


**Custom user settings which make sense and aren't mere duplications with minimal modifications
Expand Down
51 changes: 51 additions & 0 deletions lua/fzf-lua/profiles/hide.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
local uv = vim.uv or vim.loop
local fzf = require("fzf-lua")
return {
desc = "hide interface instead of abort",
defaults = {
enrich = function(opts)
if opts._is_fzf_tmux then
fzf.utils.warn("'hide' profile cannot work with tmux, ignoring.")
return opts
end
-- `execute-silent` actions are bugged with skim
if fzf.utils.has(opts, "sk") then
opts.actions["esc"] = false
opts.keymap.builtin["<esc>"] = "hide"
return opts
end
-- While we can use `keymap.builtin.<esc>` (to hide) this is better
-- as it captures the query when execute-silent action is called as
-- we add "{q}" as the first field index similar to `--print-query`
local histfile = opts.fzf_opts and opts.fzf_opts["--history"]
opts.actions["esc"] = { fn = fzf.actions.dummy_abort, desc = "hide" }
opts.actions = vim.tbl_map(function(act)
act = type(act) == "function" and { fn = act } or act
act = type(act) == "table" and type(act[1]) == "function"
and { fn = act[1], noclose = true } or act
assert(type(act) == "table" and type(act.fn) == "function" or not act)
if type(act) == "table" and
not act.exec_silent and not act.reload and not act.noclose
then
local fn = act.fn
act.exec_silent = true
act.desc = act.desc or fzf.config.get_action_helpstr(fn)
act.fn = function(s, o)
fzf.hide()
fn(s, o)
if histfile and type(o.last_query) == "string" and #o.last_query > 0 then
local fd = uv.fs_open(histfile, "a", -1)
if fd then
uv.fs_write(fd, o.last_query .. "\n", nil, function(_)
uv.fs_close(fd)
end)
end
end
end
end
return act
end, opts.actions)
return opts
end,
},
}
14 changes: 0 additions & 14 deletions lua/fzf-lua/providers/colorschemes.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ M.colorschemes = function(opts)
opts.preview = shell.raw_action(function(sel)
if opts.live_preview and sel then
vim.cmd("colorscheme " .. sel[1])
if type(opts.cb_preview) == "function" then
opts.cb_preview(sel, opts)
end
end
end, nil, opts.debug)
end
Expand All @@ -83,10 +80,6 @@ M.colorschemes = function(opts)

-- setup fzf-lua's own highlight groups
utils.setup_highlights()

if type(opts.cb_exit) == "function" then
opts.cb_exit(selected, opts)
end
end

core.fzf_exec(colors, opts)
Expand Down Expand Up @@ -486,9 +479,6 @@ M.awesome_colorschemes = function(opts)
-- wrap in pcall as some colorschemes have bg triggers that can fail
pcall(function() vim.o.background = opts._cur_background end)
M.apply_awesome_theme(dbkey, idx, opts)
if type(opts.cb_preview) == "function" then
opts.cb_preview(sel, opts)
end
else
vim.cmd("colorscheme " .. opts._cur_colorscheme)
vim.o.background = opts._cur_background
Expand Down Expand Up @@ -530,10 +520,6 @@ M.awesome_colorschemes = function(opts)

-- setup fzf-lua's own highlight groups
utils.setup_highlights()

if type(o.cb_exit) == "function" then
o.cb_exit(sel, o)
end
end

opts = core.set_header(opts, opts.headers or { "actions" })
Expand Down
5 changes: 5 additions & 0 deletions lua/fzf-lua/providers/module.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ M.profiles = function(opts)
opts = config.normalize_opts(opts, "profiles")
if not opts then return end

if opts.load then
utils.load_profiles(opts.load)
return
end

local dirs = {
path.join({ vim.g.fzf_lua_directory, "profiles" })
}
Expand Down
2 changes: 1 addition & 1 deletion lua/fzf-lua/shell.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ local M = {}
-- provider are 2 (`live_grep` with `multiprocess=false`)
-- and 4 (`git_status` with preview and 3 reload binds)
-- we can always increase if we need more
local _MAX_LEN = vim.g.fzf_lua_shell_maxlen or 10
local _MAX_LEN = 50
local _index = 0
local _registry = {}
local _protected = {}
Expand Down
19 changes: 18 additions & 1 deletion lua/fzf-lua/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ function M.CTX()
return loadstring("return require'fzf-lua'.core.CTX()")()
end

function M.__CTX()
return loadstring("return require'fzf-lua'.core.__CTX")()
end

function M.resume_get(what, opts)
local f = loadstring("return require'fzf-lua'.config.resume_get")()
return f(what, opts)
Expand All @@ -841,7 +845,7 @@ end

---@param fname string
---@param name string|nil
---@param silent boolean|number
---@param silent boolean|integer
function M.load_profile_fname(fname, name, silent)
local profile = name or vim.fn.fnamemodify(fname, ":t:r") or "<unknown>"
local ok, res = pcall(dofile, fname)
Expand All @@ -861,6 +865,19 @@ function M.load_profile_fname(fname, name, silent)
end
end

function M.load_profiles(profiles)
local serpent = require("fzf-lua.lib.serpent")
profiles = type(profiles) == "table"
and serpent.line(profiles, { comment = false, sortkeys = false })
or type(profiles) == "string" and string.format("'%s'", profiles)
or nil
if type(profiles) == "string" then
loadstring(string.format(
"require'fzf-lua'.setup(require'fzf-lua'.load_profiles(%s, false))",
profiles))()
end
end

function M.send_ctrl_c()
vim.api.nvim_feedkeys(
vim.api.nvim_replace_termcodes("<C-c>", true, false, true), "n", true)
Expand Down
Loading

0 comments on commit 24980df

Please sign in to comment.