From bc3edb68cff337179d4ebd5d1a7ede5eb6abab07 Mon Sep 17 00:00:00 2001 From: bhagwan Date: Sat, 18 Dec 2021 20:12:43 -0800 Subject: [PATCH] resume adjustments for buffers|tabs, 'ctrl-x' bwipe now resumes --- README.md | 6 +- doc/fzf-lua.txt | 6 +- lua/fzf-lua/actions.lua | 24 ++++- lua/fzf-lua/config.lua | 2 +- lua/fzf-lua/core.lua | 14 ++- lua/fzf-lua/providers/buffers.lua | 148 ++++++++++++++---------------- 6 files changed, 115 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index b12cf54..e7625bc 100644 --- a/README.md +++ b/README.md @@ -506,7 +506,11 @@ require'fzf-lua'.setup { ["ctrl-s"] = actions.buf_split, ["ctrl-v"] = actions.buf_vsplit, ["ctrl-t"] = actions.buf_tabedit, - ["ctrl-x"] = actions.buf_del, + -- by supplying a table of functions we're telling + -- fzf-lua to not close the fzf window, this way we + -- can resume the buffers picker on the same window + -- eliminating an otherwise unaesthetic win "flash" + ["ctrl-x"] = { actions.buf_del, actions.resume }, } }, lines = { diff --git a/doc/fzf-lua.txt b/doc/fzf-lua.txt index e57f237..212c27c 100644 --- a/doc/fzf-lua.txt +++ b/doc/fzf-lua.txt @@ -540,7 +540,11 @@ Consult the list below for available settings: ["ctrl-s"] = actions.buf_split, ["ctrl-v"] = actions.buf_vsplit, ["ctrl-t"] = actions.buf_tabedit, - ["ctrl-x"] = actions.buf_del, + -- by supplying a table of functions we're telling + -- fzf-lua to not close the fzf window, this way we + -- can resume the buffers picker on the same window + -- eliminating an otherwise unaesthetic win "flash" + ["ctrl-x"] = { actions.buf_del, actions.resume }, } }, lines = { diff --git a/lua/fzf-lua/actions.lua b/lua/fzf-lua/actions.lua index a61c44b..bf7ff95 100644 --- a/lua/fzf-lua/actions.lua +++ b/lua/fzf-lua/actions.lua @@ -30,6 +30,7 @@ M.normalize_selected = function(actions, selected) -- The below makes separates the keybind from the item(s) -- and makes sure 'selected' contains only items or {} -- so it can always be enumerated safely + if not actions or not selected then return end local action = _default_action if utils.tbl_length(actions)>1 then -- keybind should be in item #1 @@ -51,12 +52,29 @@ end M.act = function(actions, selected, opts) if not actions or not selected then return end - local action, entries = M.normalize_selected(actions, selected) - if actions[action] then - actions[action](entries, opts) + local keybind, entries = M.normalize_selected(actions, selected) + local action = actions[keybind] + if type(action) == 'table' then + for _, f in ipairs(action) do + f(entries, opts) + end + elseif type(action) == 'function' then + action(entries, opts) + elseif type(action) == 'string' then + vim.cmd(action) + else + utils.warn(("unsupported action: '%s', type:%s") + :format(action, type(action))) end end +M.resume = function(_, _) + -- must call via vim.cmd or we create + -- circular 'require' + -- TODO: is this really a big deal? + vim.cmd("lua require'fzf-lua'.resume()") +end + M.vimcmd = function(vimcmd, selected) for i = 1, #selected do vim.cmd(vimcmd .. " " .. vim.fn.fnameescape(selected[i])) diff --git a/lua/fzf-lua/config.lua b/lua/fzf-lua/config.lua index 5b38b6d..a5633a7 100644 --- a/lua/fzf-lua/config.lua +++ b/lua/fzf-lua/config.lua @@ -302,7 +302,7 @@ M.globals.buffers = { ["ctrl-s"] = actions.buf_split, ["ctrl-v"] = actions.buf_vsplit, ["ctrl-t"] = actions.buf_tabedit, - ["ctrl-x"] = actions.buf_del, + ["ctrl-x"] = { actions.buf_del, actions.resume }, }, } M.globals.tabs = { diff --git a/lua/fzf-lua/core.lua b/lua/fzf-lua/core.lua index e22abbe..52f13e0 100644 --- a/lua/fzf-lua/core.lua +++ b/lua/fzf-lua/core.lua @@ -104,13 +104,25 @@ M.fzf = function(opts, contents) end end + if opts.fn_pre_fzf then + -- some functions such as buffers|tabs + -- need to reacquire current buffer|tab state + opts.fn_pre_fzf(opts) + end + fzf_win:attach_previewer(previewer) fzf_win:create() local selected, exit_code = fzf.raw_fzf(contents, M.build_fzf_cli(opts), { fzf_binary = opts.fzf_bin, fzf_cwd = opts.cwd }) libuv.process_kill(opts._pid) fzf_win:check_exit_status(exit_code) - if fzf_win:autoclose() == nil or fzf_win:autoclose() then + -- retrieve the future action and check: + -- * if it's a single function we can close the window + -- * if it's a table of functions we do not close the window + local keybind = actions.normalize_selected(opts.actions, selected) + local action = keybind and opts.actions and opts.actions[keybind] + -- only close the window if autoclose wasn't specified or is 'true' + if (not fzf_win:autoclose() == false) and type(action) ~= 'table' then fzf_win:close() end return selected diff --git a/lua/fzf-lua/providers/buffers.lua b/lua/fzf-lua/providers/buffers.lua index 115b3ab..4569ed6 100644 --- a/lua/fzf-lua/providers/buffers.lua +++ b/lua/fzf-lua/providers/buffers.lua @@ -1,6 +1,5 @@ local core = require "fzf-lua.core" local path = require "fzf-lua.path" -local shell = require "fzf-lua.shell" local utils = require "fzf-lua.utils" local config = require "fzf-lua.config" local actions = require "fzf-lua.actions" @@ -8,21 +7,26 @@ local fn, api = vim.fn, vim.api local M = {} -local function getbufnumber(line) - return tonumber(string.match(line, "%[(%d+)")) -end +-- will hold current/previous buffer/tab +local __STATE = {} -local function getfilename(line) - -- return string.match(line, "%[.*%] (.+)") - -- greedy match anything after last nbsp - return line:match("[^" .. utils.nbsp .. "]*$") +local UPDATE_STATE = function() + __STATE = { + curtab = vim.api.nvim_win_get_tabpage(0), + curbuf = vim.api.nvim_get_current_buf(), + prevbuf = vim.fn.bufnr('#'), + } end local filter_buffers = function(opts, unfiltered) + + if type(unfiltered) == 'function' then + unfiltered = unfiltered() + end + local curtab_bufnrs = {} if opts.current_tab_only then - local curtab = vim.api.nvim_win_get_tabpage(0) - for _, w in ipairs(vim.api.nvim_tabpage_list_wins(curtab)) do + for _, w in ipairs(vim.api.nvim_tabpage_list_wins(__STATE.curtab)) do local b = vim.api.nvim_win_get_buf(w) curtab_bufnrs[b] = true end @@ -37,7 +41,7 @@ local filter_buffers = function(opts, unfiltered) if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(b) then excluded[b] = true end - if opts.ignore_current_buffer and b == vim.api.nvim_get_current_buf() then + if opts.ignore_current_buffer and b == __STATE.curbuf then excluded[b] = true end if opts.current_tab_only and not curtab_bufnrs[b] then @@ -55,12 +59,11 @@ local filter_buffers = function(opts, unfiltered) return bufnrs, excluded end -local make_buffer_entries = function(opts, bufnrs, tabnr, curbuf) - local header_line = false +local populate_buffer_entries = function(opts, bufnrs, tabnr) local buffers = {} - curbuf = curbuf or vim.fn.bufnr('') for _, bufnr in ipairs(bufnrs) do - local flag = bufnr == curbuf and '%' or (bufnr == vim.fn.bufnr('#') and '#' or ' ') + local flag = (bufnr == __STATE.curbuf and '%') or + (bufnr == __STATE.prevbuf and '#') or ' ' local element = { bufnr = bufnr, @@ -76,10 +79,6 @@ local make_buffer_entries = function(opts, bufnrs, tabnr, curbuf) end end - if opts.sort_lastused and flag == "%" then - header_line = true - end - table.insert(buffers, element) end if opts.sort_lastused then @@ -87,11 +86,11 @@ local make_buffer_entries = function(opts, bufnrs, tabnr, curbuf) return a.info.lastused > b.info.lastused end) end - return buffers, header_line + return buffers end -local function add_buffer_entry(opts, buf, items, header_line) +local function gen_buffer_entry(opts, buf, hl_curbuf) -- local hidden = buf.info.hidden == 1 and 'h' or 'a' local hidden = '' local readonly = vim.api.nvim_buf_get_option(buf.bufnr, 'readonly') and '=' or ' ' @@ -104,7 +103,8 @@ local function add_buffer_entry(opts, buf, items, header_line) utils._if(buf.info.lnum>0, buf.info.lnum, "")) if buf.flag == '%' then flags = utils.ansi_codes.red(buf.flag) .. flags - if not header_line then + if hl_curbuf then + -- no header line, highlight current buffer leftbr = utils.ansi_codes.green('[') rightbr = utils.ansi_codes.green(']') bufname = utils.ansi_codes.green(bufname) @@ -140,48 +140,36 @@ local function add_buffer_entry(opts, buf, items, header_line) buficon, utils.nbsp, bufname) - table.insert(items, item_str) - return items + return item_str end + M.buffers = function(opts) opts = config.normalize_opts(opts, config.globals.buffers) if not opts then return end - local act = shell.action(function (items, fzf_lines, _) - -- only preview first item - local item = items[1] - local buf = getbufnumber(item) - if api.nvim_buf_is_loaded(buf) then - return api.nvim_buf_get_lines(buf, 0, fzf_lines, false) - else - local name = getfilename(item) - if fn.filereadable(name) ~= 0 then - return fn.readfile(name, "", fzf_lines) - end - return "UNLOADED: " .. name - end - end) - - local filtered = filter_buffers(opts, - opts._list_bufs and opts._list_bufs() or vim.api.nvim_list_bufs()) + -- get current tab/buffer/previos buffer + -- save as a func ref for resume to reuse + opts.fn_pre_fzf = UPDATE_STATE - if not next(filtered) then return end + local contents = function(cb) - local items = {} + local filtered = filter_buffers(opts, vim.api.nvim_list_bufs) - local buffers, header_line = make_buffer_entries(opts, filtered, nil, opts.curbuf) - for _, buf in pairs(buffers) do - items = add_buffer_entry(opts, buf, items, header_line) + if next(filtered) then + local buffers = populate_buffer_entries(opts, filtered) + for _, bufinfo in pairs(buffers) do + cb(gen_buffer_entry(opts, bufinfo, not opts.sort_lastused)) + end + end + cb(nil) end - opts.fzf_opts['--preview'] = act - if header_line and not opts.ignore_current_buffer then - opts.fzf_opts['--header-lines'] = '1' - end + opts.fzf_opts['--header-lines'] = + (not opts.ignore_current_buffer and opts.sort_lastused) and '1' - core.fzf_wrap(opts, items, function(selected) + core.fzf_wrap(opts, contents, function(selected) if not selected then return end @@ -205,9 +193,12 @@ end M.buffer_lines = function(opts) if not opts then return end + opts.fn_pre_fzf = UPDATE_STATE + opts.fn_pre_fzf() + local buffers = filter_buffers(opts, - opts.current_buffer_only and { vim.api.nvim_get_current_buf() } or - vim.api.nvim_list_bufs()) + opts.current_buffer_only and { __STATE.curbuf } or + vim.api.nvim_list_bufs) local items = {} @@ -273,7 +264,7 @@ M.tabs = function(opts) opts = config.normalize_opts(opts, config.globals.tabs) if not opts then return end - local curtab = vim.api.nvim_win_get_tabpage(0) + opts.fn_pre_fzf = UPDATE_STATE opts._tab_to_buf = {} opts._list_bufs = function() @@ -289,36 +280,37 @@ M.tabs = function(opts) return res end + local contents = function(cb) + local filtered, excluded = filter_buffers(opts, opts._list_bufs) + if not next(filtered) then return end - local filtered, excluded = filter_buffers(opts, opts._list_bufs()) - if not next(filtered) then return end - - -- remove the filtered-out buffers - for b, _ in pairs(excluded) do - for _, bufnrs in pairs(opts._tab_to_buf) do - bufnrs[b] = nil + -- remove the filtered-out buffers + for b, _ in pairs(excluded) do + for _, bufnrs in pairs(opts._tab_to_buf) do + bufnrs[b] = nil + end end - end - local items = {} - - for t, bufnrs in pairs(opts._tab_to_buf) do + for t, bufnrs in pairs(opts._tab_to_buf) do - table.insert(items, ("%d)%s%s\t%s"):format(t, utils.nbsp, - utils.ansi_codes.blue("%s%s#%d"):format(opts.tab_title, utils.nbsp, t), - (t==curtab) and utils.ansi_codes.blue(utils.ansi_codes.bold(opts.tab_marker)) or '')) + cb(("%d)%s%s\t%s"):format(t, utils.nbsp, + utils.ansi_codes.blue("%s%s#%d"):format(opts.tab_title, utils.nbsp, t), + (t==__STATE.curtab) and + utils.ansi_codes.blue(utils.ansi_codes.bold(opts.tab_marker)) or '')) - local bufnrs_flat = {} - for b, _ in pairs(bufnrs) do - table.insert(bufnrs_flat, b) - end + local bufnrs_flat = {} + for b, _ in pairs(bufnrs) do + table.insert(bufnrs_flat, b) + end - opts.sort_lastused = false - opts._prefix = ("%d)%s%s%s"):format(t, utils.nbsp, utils.nbsp, utils.nbsp) - local buffers = make_buffer_entries(opts, bufnrs_flat, t) - for _, buf in pairs(buffers) do - items = add_buffer_entry(opts, buf, items, true) + opts.sort_lastused = false + opts._prefix = ("%d)%s%s%s"):format(t, utils.nbsp, utils.nbsp, utils.nbsp) + local buffers = populate_buffer_entries(opts, bufnrs_flat, t) + for _, bufinfo in pairs(buffers) do + cb(gen_buffer_entry(opts, bufinfo, false)) + end end + cb(nil) end opts.fzf_opts["--no-multi"] = '' @@ -326,7 +318,7 @@ M.tabs = function(opts) opts.fzf_opts["--delimiter"] = vim.fn.shellescape('[\\)]') opts.fzf_opts["--with-nth"] = '2' - core.fzf_wrap(opts, items, function(selected) + core.fzf_wrap(opts, contents, function(selected) if not selected then return end