Skip to content

Commit f75e50d

Browse files
committed
feat: history picker alias to oldfiles with buffers
closes #2509
1 parent 940ffbf commit f75e50d

File tree

11 files changed

+109
-47
lines changed

11 files changed

+109
-47
lines changed

OPTIONS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,10 @@ Open buffers in tabs
808808

809809
File history (output of `:oldfiles`)
810810

811+
#### history
812+
813+
Combined current buffer + file history (output of `:oldfiles`)
814+
811815
#### quickfix
812816

813817
Quickfix list (output of `:copen`)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ Fzf-Lua conveniently comes with a VS-Code like picker by default
201201
| `buffers` | open buffers |
202202
| `files` | `find` or `fd` on a path |
203203
| `oldfiles` | opened files history |
204+
| `history` | opened buffers/files history |
204205
| `quickfix` | quickfix list |
205206
| `quickfix_stack` | quickfix stack |
206207
| `loclist` | location list |
@@ -1067,6 +1068,7 @@ previewers = {
10671068
-- stat_file = FzfLua.utils.file_is_readable,
10681069
-- stat_file = function() return true end,
10691070
include_current_session = false, -- include bufs from current session
1071+
ignore_current_buffer = true, -- exclude current buf from session
10701072
},
10711073
buffers = {
10721074
prompt = 'Buffers❯ ',

doc/fzf-lua-opts.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,12 @@ File history (output of `:oldfiles`)
10851085

10861086

10871087

1088+
history *fzf-lua-opts-history*
1089+
1090+
Combined current buffer + file history (output of `:oldfiles`)
1091+
1092+
1093+
10881094
quickfix *fzf-lua-opts-quickfix*
10891095

10901096
Quickfix list (output of `:copen`)

lua/fzf-lua/defaults.lua

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -820,18 +820,28 @@ M.defaults.args = {
820820
---@class fzf-lua.config.Oldfiles: fzf-lua.config.Base
821821
---@field stat_file? boolean
822822
---@field include_current_session? boolean
823+
---@field ignore_current_buffer? boolean
823824
M.defaults.oldfiles = {
824-
previewer = M._default_previewer_fn,
825-
file_icons = 1, ---@type integer|boolean
826-
color_icons = true,
827-
git_icons = false,
828-
stat_file = true,
829-
fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = true },
830-
_fzf_nth_devicons = true,
831-
_actions = function() return M.globals.actions.files end,
832-
_headers = { "cwd" },
825+
previewer = M._default_previewer_fn,
826+
file_icons = 1, ---@type integer|boolean
827+
color_icons = true,
828+
git_icons = false,
829+
stat_file = true,
830+
include_current_session = false,
831+
ignore_current_buffer = true,
832+
fzf_opts = { ["--tiebreak"] = "index", ["--multi"] = true },
833+
_fzf_nth_devicons = true,
834+
_actions = function() return M.globals.actions.files end,
835+
_headers = { "cwd" },
836+
_resume_reload = true,
833837
}
834838

839+
---@class fzf-lua.config.History: fzf-lua.config.Oldfiles
840+
M.defaults.history = vim.tbl_deep_extend("force", {}, M.defaults.oldfiles, {
841+
include_current_session = true,
842+
ignore_current_buffer = false,
843+
})
844+
835845
---@class fzf-lua.config.Quickfix: fzf-lua.config.Base
836846
---@field separator string
837847
---@field valid_only boolean
@@ -1553,7 +1563,7 @@ M.defaults.command_history = {
15531563
actions = {
15541564
["enter"] = actions.ex_run_cr,
15551565
["ctrl-e"] = actions.ex_run,
1556-
["ctrl-x"] = { fn = actions.ex_del, field_index = "{+n}", reload = true }
1566+
["ctrl-x"] = { fn = actions.ex_del, field_index = "{+n}", reload = true }
15571567
},
15581568
_headers = { "actions" },
15591569
}
@@ -1568,7 +1578,7 @@ M.defaults.search_history = {
15681578
actions = {
15691579
["enter"] = actions.search_cr,
15701580
["ctrl-e"] = actions.search,
1571-
["ctrl-x"] = { fn = actions.search_del, field_index = "{+n}", reload = true }
1581+
["ctrl-x"] = { fn = actions.search_del, field_index = "{+n}", reload = true }
15721582
},
15731583
_headers = { "actions" },
15741584
}

lua/fzf-lua/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ local lazyloaded_modules = {
285285
git_worktrees = { "fzf-lua.providers.git", "worktrees" },
286286
git_tags = { "fzf-lua.providers.git", "tags" },
287287
oldfiles = { "fzf-lua.providers.oldfiles", "oldfiles" },
288+
history = { "fzf-lua.providers.oldfiles", "history" },
288289
undotree = { "fzf-lua.providers.undotree", "undotree" },
289290
quickfix = { "fzf-lua.providers.quickfix", "quickfix" },
290291
quickfix_stack = { "fzf-lua.providers.quickfix", "quickfix_stack" },

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ return {
2626
},
2727
args = prompt("Args"),
2828
oldfiles = prompt("Oldfiles"),
29+
history = prompt("History"),
2930
undotree = prompt("Undotree"),
3031
quickfix = prompt("Quickfix"),
3132
quickfix_stack = prompt("Quickfix Stack"),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ return {
3030
},
3131
args = title("Args"),
3232
oldfiles = title("Oldfiles"),
33+
history = title("History"),
3334
undotree = title("Undotree"),
3435
quickfix = title("Quickfix List"),
3536
quickfix_stack = title("Quickfix List Stack"),

lua/fzf-lua/profiles/fzf-vim.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ local function setup_commands(no_override, prefix)
2525
["Marks"] = utils.create_user_command_callback("marks"),
2626
["Jumps"] = utils.create_user_command_callback("jumps"),
2727
["Commands"] = utils.create_user_command_callback("commands"),
28-
["History"] = utils.create_user_command_callback("oldfiles", "query", {
28+
["History"] = utils.create_user_command_callback("history", "query", {
2929
[":"] = "command_history",
3030
["/"] = "search_history",
3131
}),

lua/fzf-lua/profiles/hide.lua

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@ return {
9494
end
9595
-- Hijack the resize event to reload buffer/tab list on unhide
9696
FzfLua.win.on_SIGWINCH(opts, "win.unhide", function()
97-
if type(opts._contents) == "string"
97+
local reload = type(opts._contents) == "string"
9898
and (opts._resume_reload == true
9999
---@diagnostic disable-next-line: need-check-nil
100100
or type(opts._resume_reload) == "function" and opts._resume_reload(opts))
101-
then
102-
return string.format("reload:%s", opts._contents)
101+
if reload then
102+
return string.format("%sreload:%s",
103+
type(reload) == "string" and reload .. "+" or "",
104+
opts._contents)
103105
end
104106
end)
105107
return opts

lua/fzf-lua/providers/oldfiles.lua

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,18 @@ local make_entry = require "fzf-lua.make_entry"
88
local M = {}
99

1010
---@param opts fzf-lua.config.Oldfiles|{}?
11+
---@param globals string|(fzf-lua.Config|{})?
1112
---@return thread?, string?, table?
12-
M.oldfiles = function(opts)
13+
M.oldfiles = function(opts, globals)
1314
---@type fzf-lua.config.Oldfiles
14-
opts = config.normalize_opts(opts, "oldfiles")
15+
opts = config.normalize_opts(opts, globals or "oldfiles")
1516
if not opts then return end
1617

1718
-- cwd implies we want `cwd_only=true`
1819
if opts.cwd and opts.cwd_only == nil then
1920
opts.cwd_only = true
2021
end
2122

22-
local current_buffer = vim.api.nvim_get_current_buf()
23-
local current_file = vim.api.nvim_buf_get_name(current_buffer)
24-
local sess_tbl = {}
25-
local sess_map = {}
26-
2723
local stat_fn = not opts.stat_file and function(_) return true end
2824
or type(opts.stat_file) == "function" and opts.stat_file
2925
or function(file)
@@ -34,47 +30,79 @@ M.oldfiles = function(opts)
3430
and utils.file_is_readable(file))
3531
end
3632

37-
if opts.include_current_session then
38-
for _, buffer in ipairs(vim.split(vim.fn.execute(":buffers! t"), "\n")) do
39-
local bufnr = utils.tointeger(buffer:match("%s*(%d+)"))
40-
if bufnr then
33+
local sorted_named_buffers = function()
34+
local bufnrs = {}
35+
for i, buffer in ipairs(vim.split(vim.fn.execute(":buffers! t"), "\n")) do
36+
if i > 1 then -- line[1] == "\n"
37+
local bufnr = assert(utils.tointeger(buffer:match("%s*(%d+)")))
4138
local file = vim.api.nvim_buf_get_name(bufnr)
42-
local fs_stat = stat_fn(file)
43-
if #file > 0 and fs_stat and bufnr ~= current_buffer then
44-
sess_map[file] = true
45-
table.insert(sess_tbl, file)
39+
local fs_stat = #file > 0 and stat_fn(file)
40+
if fs_stat then
41+
table.insert(bufnrs, { bufnr = bufnr, file = file, curbuf = bufnr == utils.CTX().bufnr })
4642
end
4743
end
4844
end
45+
return bufnrs
4946
end
5047

51-
local contents = function(cb)
52-
local function add_entry(x, co)
53-
x = make_entry.file(x, opts)
54-
if not x then return end
55-
cb(x, function(err)
56-
coroutine.resume(co)
57-
if err then
58-
-- close the pipe to fzf, this
59-
-- removes the loading indicator in fzf
60-
cb(nil)
61-
end
62-
end)
63-
coroutine.yield()
48+
-- current buffer as a header line
49+
-- since we exclude unnamed/terminal/non-existent files
50+
-- only set header line if first entry matches current buffer
51+
local header_line = function()
52+
if opts.include_current_session and not opts.ignore_current_buffer then
53+
local buflist = sorted_named_buffers()
54+
if buflist[1] and buflist[1].bufnr == utils.CTX().bufnr then
55+
return true
56+
end
6457
end
58+
end
59+
60+
if header_line() then utils.map_set(opts, "fzf_opts.--header-lines", 1) end
61+
62+
-- NOTE: fzf does not yet support "change-header-lines" transform
63+
-- https://github.com/junegunn/fzf/issues/4659
64+
-- opts._resume_reload = function()
65+
-- return string.format("change-header-lines(%d)", header_line() and 1 or 0)
66+
-- end
6567

68+
local contents = function(cb)
6669
-- run in a coroutine for async progress indication
6770
coroutine.wrap(function()
6871
local co = coroutine.running()
6972

70-
for _, file in ipairs(sess_tbl) do
71-
add_entry(file, co)
73+
local curr_buf = utils.CTX().bufnr
74+
local curr_file = vim.api.nvim_buf_get_name(curr_buf)
75+
local sess_map = {} -- dedup files from current buffers
76+
77+
local function add_entry(x, co, force)
78+
x = make_entry.file(x,
79+
force and vim.tbl_deep_extend("force", {}, opts, { cwd_only = false }) or opts)
80+
if not x then return end
81+
cb(x, function(err)
82+
coroutine.resume(co)
83+
if err then
84+
-- close the pipe to fzf, this
85+
-- removes the loading indicator in fzf
86+
cb(nil)
87+
end
88+
end)
89+
coroutine.yield()
90+
end
91+
92+
if opts.include_current_session then
93+
for _, buf in ipairs(sorted_named_buffers()) do
94+
if not opts.ignore_current_buffer or buf.bufnr ~= curr_buf then
95+
sess_map[buf.file] = true
96+
-- 3rd arg forces addition of current buffer with cwd_only
97+
add_entry(buf.file, co, buf.curbuf)
98+
end
99+
end
72100
end
73101

74102
-- local start = os.time(); for _ = 1,10000,1 do
75103
for _, file in ipairs(vim.v.oldfiles) do
76104
local fs_stat = stat_fn(file)
77-
if fs_stat and file ~= current_file and not sess_map[file] then
105+
if fs_stat and file ~= curr_file and not sess_map[file] then
78106
add_entry(file, co)
79107
end
80108
end
@@ -90,4 +118,10 @@ M.oldfiles = function(opts)
90118
return core.fzf_exec(contents, opts)
91119
end
92120

121+
---@param opts fzf-lua.config.History|{}?
122+
---@return thread?, string?, table?
123+
M.history = function(opts)
124+
return M.oldfiles(opts, "history")
125+
end
126+
93127
return M

0 commit comments

Comments
 (0)