Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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"}
```
ibhagwan committed Jan 19, 2025
1 parent 4820f42 commit 3d8a1ae
Showing 14 changed files with 176 additions and 60 deletions.
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
@@ -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❯ ',
@@ -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> ",
@@ -1360,6 +1355,7 @@ require('fzf-lua').setup({'fzf-vim'})
| `borderless-full` | borderless with description in window title (instead of prompt) |
| `border-fused` | single border around both fzf and the previewer |
| `ivy` | UI at bottom, similar to telescope's ivy layout |
| `hide` | send fzf process to background instead of termination |

</details>

3 changes: 2 additions & 1 deletion lua/fzf-lua/actions.lua
Original file line number Diff line number Diff line change
@@ -696,7 +696,8 @@ M.git_yank_commit = function(selected, opts)
-- copy to the yank register regardless
vim.fn.setreg(reg, commit_hash)
vim.fn.setreg([[0]], commit_hash)
utils.info(string.format("commit hash %s copied to register %s, use 'p' to paste.", commit_hash, reg))
utils.info(string.format("commit hash %s copied to register %s, use 'p' to paste.",
commit_hash, reg))
end

M.git_checkout = function(selected, opts)
4 changes: 4 additions & 0 deletions lua/fzf-lua/config.lua
Original file line number Diff line number Diff line change
@@ -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

57 changes: 46 additions & 11 deletions lua/fzf-lua/core.lua
Original file line number Diff line number Diff line change
@@ -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)
@@ -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.postfix = "+" .. 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
@@ -1125,25 +1131,54 @@ 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")
-- Use both {q} and {+} as field indexes so we can update last query when
-- executing the action, without this we lose the last query on "hide" as
-- the process never terminates and `--print-query` isn't being printed
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
2 changes: 1 addition & 1 deletion lua/fzf-lua/defaults.lua
Original file line number Diff line number Diff line change
@@ -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 },
11 changes: 6 additions & 5 deletions lua/fzf-lua/init.lua
Original file line number Diff line number Diff line change
@@ -143,7 +143,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 }
@@ -152,15 +152,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)
@@ -175,7 +175,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
@@ -381,6 +381,7 @@ M._exported_modules = {
M._excluded_meta = {
"setup",
"redraw",
"load_profiles",
"fzf",
"fzf_raw",
"fzf_wrap",
1 change: 1 addition & 0 deletions lua/fzf-lua/profiles/README.md
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ require("fzf-lua").setup({ { "ivy", "hide" } })
| `borderless-full` | borderless with description in window title (instead of prompt) |
| `border-fused` | single border around both fzf and the previewer |
| `ivy` | UI at bottom, similar to telescope's ivy layout |
| `hide` | send fzf process to background instead of termination |


**Custom user settings which make sense and aren't mere duplications with minimal modifications
57 changes: 57 additions & 0 deletions lua/fzf-lua/profiles/hide.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
local uv = vim.uv or vim.loop
local fzf = require("fzf-lua")
return {
desc = "hide interface instead of abort",
keymap = { builtin = { true, ["<M-Esc>"] = "abort" } },
defaults = {
enrich = function(opts)
if opts._is_fzf_tmux then
fzf.utils.warn("'hide' profile cannot work with tmux, ignoring.")
return opts
end
opts.actions = opts.actions or {}
opts.keymap = opts.keymap or {}
opts.keymap.builtin = opts.keymap.builtin or {}
-- `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)
-- As the process never terminates fzf history is never written
-- manually append to the fzf history file if needed
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,
},
}
34 changes: 17 additions & 17 deletions lua/fzf-lua/providers/colorschemes.lua
Original file line number Diff line number Diff line change
@@ -61,32 +61,28 @@ M.colorschemes = function(opts)
opts.fzf_opts["--preview-window"] = "nohidden:right:0"
opts.preview = shell.raw_action(function(sel)
if opts.live_preview and sel then
opts._live = sel[1]
vim.cmd("colorscheme " .. sel[1])
if type(opts.cb_preview) == "function" then
opts.cb_preview(sel, opts)
end
end
end, nil, opts.debug)
end

opts.fn_selected = function(selected, o)
opts.winopts = opts.winopts or {}
opts.winopts.on_close = function()
-- reset color scheme if live_preview is enabled
-- and nothing or non-default action was selected
if opts.live_preview and (not selected or #selected[1] > 0) then
if opts._live and opts._live ~= current_colorscheme then
vim.cmd("colorscheme " .. current_colorscheme)
vim.o.background = current_background
end
end

opts.fn_selected = function(selected, o)
if selected then
actions.act(opts.actions, selected, o)
end

-- 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)
@@ -486,12 +482,11 @@ 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
opts._live = true
else
vim.cmd("colorscheme " .. opts._cur_colorscheme)
vim.o.background = opts._cur_background
opts._live = nil
end
end
end, "{}", opts.debug)
@@ -509,6 +504,15 @@ M.awesome_colorschemes = function(opts)
end
end

opts.winopts = opts.winopts or {}
opts.winopts.on_close = function()
-- reset color scheme if live_preview is enabled
if opts._live then
vim.cmd("colorscheme " .. opts._cur_colorscheme)
vim.o.background = opts._cur_background
end
end

opts.fn_selected = function(sel, o)
-- do not remove our cache path from packpath
-- or packadd in `apply_awesome_theme` fails
@@ -530,10 +534,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" })
5 changes: 5 additions & 0 deletions lua/fzf-lua/providers/module.lua
Original file line number Diff line number Diff line change
@@ -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" })
}
Loading

0 comments on commit 3d8a1ae

Please sign in to comment.