feat(fzf): match telescope recent files
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
local M = {}
|
||||
|
||||
---@param path string
|
||||
---@return boolean
|
||||
local function is_file(path)
|
||||
local stat = vim.uv.fs_stat(path)
|
||||
return stat ~= nil and stat.type == "file"
|
||||
end
|
||||
|
||||
---@param line string
|
||||
---@return integer?
|
||||
local function buffer_number(line)
|
||||
if line:match("line 0$") then
|
||||
return nil
|
||||
end
|
||||
local bufnr = tonumber(line:match("^%s*(%d+)"))
|
||||
---@cast bufnr integer?
|
||||
return bufnr
|
||||
end
|
||||
|
||||
---@return integer[]
|
||||
local function session_buffers()
|
||||
local buffers = {}
|
||||
local output = vim.fn.execute(":buffers! t")
|
||||
for _, line in ipairs(vim.split(output, "\n", { trimempty = true })) do
|
||||
local bufnr = buffer_number(line)
|
||||
if bufnr then
|
||||
table.insert(buffers, bufnr)
|
||||
end
|
||||
end
|
||||
return buffers
|
||||
end
|
||||
|
||||
---@param results string[]
|
||||
---@param seen table<string, boolean>
|
||||
---@param file string
|
||||
local function add(results, seen, file)
|
||||
if file == "" or seen[file] then
|
||||
return
|
||||
end
|
||||
seen[file] = true
|
||||
table.insert(results, file)
|
||||
end
|
||||
|
||||
---@param file string
|
||||
---@param cwd string
|
||||
---@return boolean
|
||||
local function in_cwd(file, cwd)
|
||||
if cwd:sub(-1) ~= "/" then
|
||||
cwd = cwd .. "/"
|
||||
end
|
||||
return file:sub(1, #cwd) == cwd
|
||||
end
|
||||
|
||||
---@class ow.RecentFiles.ListOpts
|
||||
---@field cwd? string
|
||||
---@field cwd_only? boolean
|
||||
---@field include_current_session? boolean
|
||||
|
||||
---@param opts ow.RecentFiles.ListOpts?
|
||||
---@return string[]
|
||||
function M.list(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local current_buffer = vim.api.nvim_get_current_buf()
|
||||
local current_file = vim.api.nvim_buf_get_name(current_buffer)
|
||||
local results = {}
|
||||
local seen = {}
|
||||
local cwd = opts.cwd_only and assert(vim.uv.cwd()) or opts.cwd
|
||||
|
||||
local function should_stat(file)
|
||||
return cwd == nil or in_cwd(file, cwd)
|
||||
end
|
||||
|
||||
if opts.include_current_session ~= false then
|
||||
for _, bufnr in ipairs(session_buffers()) do
|
||||
local file = vim.api.nvim_buf_get_name(bufnr)
|
||||
if
|
||||
bufnr ~= current_buffer
|
||||
and should_stat(file)
|
||||
and is_file(file)
|
||||
then
|
||||
add(results, seen, file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, file in ipairs(vim.v.oldfiles) do
|
||||
if file ~= current_file and should_stat(file) and is_file(file) then
|
||||
add(results, seen, file)
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
return M
|
||||
+3
-52
@@ -1,4 +1,5 @@
|
||||
local fzf = require("fzf-lua")
|
||||
local recent = require("recent_files")
|
||||
|
||||
local wide = {
|
||||
width = 160,
|
||||
@@ -8,61 +9,11 @@ local wide = {
|
||||
},
|
||||
}
|
||||
|
||||
local function valid_file(file)
|
||||
local stat = vim.uv.fs_stat(file)
|
||||
return stat ~= nil
|
||||
and not fzf.utils.path_is_directory(file, stat)
|
||||
-- FIFO blocks `fs_open` indefinitely (#908)
|
||||
and not fzf.utils.file_is_fifo(file, stat)
|
||||
and fzf.utils.file_is_readable(file)
|
||||
end
|
||||
|
||||
local function recent_files()
|
||||
local cwd = vim.fn.getcwd()
|
||||
local cwd = assert(vim.uv.cwd())
|
||||
local current = vim.api.nvim_buf_get_name(0)
|
||||
local has_current = current ~= ""
|
||||
local oldfiles_count = #vim.v.oldfiles
|
||||
local scores = {}
|
||||
|
||||
local function under_cwd(path)
|
||||
local rel = vim.fs.relpath(cwd, path)
|
||||
return rel ~= nil and not vim.startswith(rel, "..")
|
||||
end
|
||||
|
||||
local function is_uri(path)
|
||||
return path:match("^%w[%w+%-%.]*://") ~= nil
|
||||
end
|
||||
|
||||
for i, f in ipairs(vim.v.oldfiles) do
|
||||
if f ~= current and not is_uri(f) and under_cwd(f) then
|
||||
scores[f] = oldfiles_count - i + 1
|
||||
end
|
||||
end
|
||||
|
||||
for _, b in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if vim.bo[b].buftype == "" then
|
||||
local name = vim.api.nvim_buf_get_name(b)
|
||||
if
|
||||
name ~= ""
|
||||
and not is_uri(name)
|
||||
and name ~= current
|
||||
and under_cwd(name)
|
||||
and (scores[name] or valid_file(name))
|
||||
then
|
||||
local info = vim.fn.getbufinfo(b)[1]
|
||||
---@cast info -nil
|
||||
scores[name] = math.max(info.lastused, scores[name] or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local list = {}
|
||||
for f in pairs(scores) do
|
||||
table.insert(list, f)
|
||||
end
|
||||
table.sort(list, function(a, b)
|
||||
return scores[a] > scores[b]
|
||||
end)
|
||||
local list = recent.list({ cwd_only = true })
|
||||
for i, f in ipairs(list) do
|
||||
list[i] = vim.fs.relpath(cwd, f) or f
|
||||
end
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
local recent = require("recent_files")
|
||||
local t = require("test")
|
||||
|
||||
---@return string
|
||||
local function temp_dir()
|
||||
local dir = vim.fn.tempname()
|
||||
vim.fn.mkdir(dir, "p")
|
||||
t.defer(function()
|
||||
vim.fn.delete(dir, "rf")
|
||||
end)
|
||||
return dir
|
||||
end
|
||||
|
||||
---@param path string
|
||||
local function edit(path)
|
||||
vim.cmd.edit(vim.fn.fnameescape(path))
|
||||
end
|
||||
|
||||
---@param dir string
|
||||
---@param name string
|
||||
---@return string
|
||||
local function file(dir, name)
|
||||
t.write(dir, name, name)
|
||||
return vim.fs.joinpath(dir, name)
|
||||
end
|
||||
|
||||
---@param fn fun()
|
||||
local function with_clean_state(fn)
|
||||
local cwd = vim.fn.getcwd()
|
||||
local oldfiles = vim.deepcopy(vim.v.oldfiles)
|
||||
local before = {}
|
||||
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
||||
before[bufnr] = vim.api.nvim_buf_get_name(bufnr)
|
||||
end
|
||||
|
||||
local ok, err = pcall(fn)
|
||||
|
||||
t.defer(function()
|
||||
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
||||
local name = vim.api.nvim_buf_get_name(bufnr)
|
||||
if before[bufnr] ~= name and vim.api.nvim_buf_is_valid(bufnr) then
|
||||
pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
|
||||
end
|
||||
end
|
||||
vim.v.oldfiles = oldfiles
|
||||
pcall(vim.cmd.cd, vim.fn.fnameescape(cwd))
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
error(err, 0)
|
||||
end
|
||||
end
|
||||
|
||||
t.test("list orders session buffers before oldfiles", function()
|
||||
with_clean_state(function()
|
||||
local dir = temp_dir()
|
||||
vim.cmd.cd(vim.fn.fnameescape(dir))
|
||||
|
||||
local first = file(dir, "first.txt")
|
||||
local second = file(dir, "second.txt")
|
||||
local current = file(dir, "current.txt")
|
||||
local old = file(dir, "old.txt")
|
||||
local missing = vim.fs.joinpath(dir, "missing.txt")
|
||||
|
||||
edit(first)
|
||||
edit(second)
|
||||
edit(current)
|
||||
vim.v.oldfiles = { old, second, current, missing }
|
||||
|
||||
t.eq(recent.list({ cwd_only = true }), { first, second, old })
|
||||
end)
|
||||
end)
|
||||
|
||||
t.test("list can omit current session buffers", function()
|
||||
with_clean_state(function()
|
||||
local dir = temp_dir()
|
||||
vim.cmd.cd(vim.fn.fnameescape(dir))
|
||||
|
||||
local session_file = file(dir, "session.txt")
|
||||
local current = file(dir, "current.txt")
|
||||
local old = file(dir, "old.txt")
|
||||
|
||||
edit(session_file)
|
||||
edit(current)
|
||||
vim.v.oldfiles = { old }
|
||||
|
||||
t.eq(
|
||||
recent.list({
|
||||
cwd_only = true,
|
||||
include_current_session = false,
|
||||
}),
|
||||
{ old }
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
t.test("list excludes directory session buffers", function()
|
||||
with_clean_state(function()
|
||||
local dir = temp_dir()
|
||||
vim.cmd.cd(vim.fn.fnameescape(dir))
|
||||
|
||||
local current = file(dir, "current.txt")
|
||||
local old = file(dir, "old.txt")
|
||||
|
||||
edit(dir)
|
||||
edit(current)
|
||||
vim.v.oldfiles = { old }
|
||||
|
||||
t.eq(recent.list({ cwd_only = true }), { old })
|
||||
end)
|
||||
end)
|
||||
|
||||
t.test("list filters cwd entries", function()
|
||||
with_clean_state(function()
|
||||
local dir = temp_dir()
|
||||
local other_dir = temp_dir()
|
||||
vim.cmd.cd(vim.fn.fnameescape(dir))
|
||||
|
||||
local inside_session = file(dir, "inside-session.txt")
|
||||
local inside_old = file(dir, "inside-old.txt")
|
||||
local outside_session = file(other_dir, "outside-session.txt")
|
||||
local outside_old = file(other_dir, "outside-old.txt")
|
||||
local current = file(dir, "current.txt")
|
||||
|
||||
edit(inside_session)
|
||||
edit(outside_session)
|
||||
edit(current)
|
||||
vim.v.oldfiles = { inside_old, outside_old }
|
||||
|
||||
t.eq(recent.list({ cwd_only = true }), { inside_session, inside_old })
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user