mirror of
https://github.com/neovim/neovim.git
synced 2025-10-26 12:27:24 +00:00
feat(fs): add vim.fs.find()
This is a pure Lua implementation of the Vim findfile() and finddir() functions without the special syntax.
This commit is contained in:
@@ -2183,6 +2183,46 @@ dirname({file}) *vim.fs.dirname()*
|
||||
Return: ~
|
||||
(string) Parent directory of {file}
|
||||
|
||||
find({names}, {opts}) *vim.fs.find()*
|
||||
Find files or directories in the given path.
|
||||
|
||||
Finds any files or directories given in {names} starting from
|
||||
{path}. If {upward} is "true" then the search traverses upward
|
||||
through parent directories; otherwise, the search traverses
|
||||
downward. Note that downward searches are recursive and may
|
||||
search through many directories! If {stop} is non-nil, then
|
||||
the search stops when the directory given in {stop} is
|
||||
reached. The search terminates when {limit} (default 1)
|
||||
matches are found. The search can be narrowed to find only
|
||||
files or or only directories by specifying {type} to be "file"
|
||||
or "directory", respectively.
|
||||
|
||||
Parameters: ~
|
||||
{names} (string|table) Names of the files and directories
|
||||
to find. Must be base names, paths and globs are
|
||||
not supported.
|
||||
{opts} (table) Optional keyword arguments:
|
||||
• path (string): Path to begin searching from. If
|
||||
omitted, the current working directory is used.
|
||||
• upward (boolean, default false): If true,
|
||||
search upward through parent directories.
|
||||
Otherwise, search through child directories
|
||||
(recursively).
|
||||
• stop (string): Stop searching when this
|
||||
directory is reached. The directory itself is
|
||||
not searched.
|
||||
• type (string): Find only files ("file") or
|
||||
directories ("directory"). If omitted, both
|
||||
files and directories that match {name} are
|
||||
included.
|
||||
• limit (number, default 1): Stop the search
|
||||
after finding this many matches. Use
|
||||
`math.huge` to place no limit on the number of
|
||||
matches.
|
||||
|
||||
Return: ~
|
||||
(table) The paths of all matching files or directories
|
||||
|
||||
parents({start}) *vim.fs.parents()*
|
||||
Iterate over all the parents of the given file or directory.
|
||||
|
||||
|
||||
@@ -61,4 +61,121 @@ function M.dir(path)
|
||||
end, vim.loop.fs_scandir(path)
|
||||
end
|
||||
|
||||
--- Find files or directories in the given path.
|
||||
---
|
||||
--- Finds any files or directories given in {names} starting from {path}. If
|
||||
--- {upward} is "true" then the search traverses upward through parent
|
||||
--- directories; otherwise, the search traverses downward. Note that downward
|
||||
--- searches are recursive and may search through many directories! If {stop}
|
||||
--- is non-nil, then the search stops when the directory given in {stop} is
|
||||
--- reached. The search terminates when {limit} (default 1) matches are found.
|
||||
--- The search can be narrowed to find only files or or only directories by
|
||||
--- specifying {type} to be "file" or "directory", respectively.
|
||||
---
|
||||
---@param names (string|table) Names of the files and directories to find. Must
|
||||
--- be base names, paths and globs are not supported.
|
||||
---@param opts (table) Optional keyword arguments:
|
||||
--- - path (string): Path to begin searching from. If
|
||||
--- omitted, the current working directory is used.
|
||||
--- - upward (boolean, default false): If true, search
|
||||
--- upward through parent directories. Otherwise,
|
||||
--- search through child directories
|
||||
--- (recursively).
|
||||
--- - stop (string): Stop searching when this directory is
|
||||
--- reached. The directory itself is not searched.
|
||||
--- - type (string): Find only files ("file") or
|
||||
--- directories ("directory"). If omitted, both
|
||||
--- files and directories that match {name} are
|
||||
--- included.
|
||||
--- - limit (number, default 1): Stop the search after
|
||||
--- finding this many matches. Use `math.huge` to
|
||||
--- place no limit on the number of matches.
|
||||
---@return (table) The paths of all matching files or directories
|
||||
function M.find(names, opts)
|
||||
opts = opts or {}
|
||||
vim.validate({
|
||||
names = { names, { 's', 't' } },
|
||||
path = { opts.path, 's', true },
|
||||
upward = { opts.upward, 'b', true },
|
||||
stop = { opts.stop, 's', true },
|
||||
type = { opts.type, 's', true },
|
||||
limit = { opts.limit, 'n', true },
|
||||
})
|
||||
|
||||
names = type(names) == 'string' and { names } or names
|
||||
|
||||
local path = opts.path or vim.loop.cwd()
|
||||
local stop = opts.stop
|
||||
local limit = opts.limit or 1
|
||||
|
||||
local matches = {}
|
||||
|
||||
---@private
|
||||
local function add(match)
|
||||
matches[#matches + 1] = match
|
||||
if #matches == limit then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if opts.upward then
|
||||
---@private
|
||||
local function test(p)
|
||||
local t = {}
|
||||
for _, name in ipairs(names) do
|
||||
local f = p .. '/' .. name
|
||||
local stat = vim.loop.fs_stat(f)
|
||||
if stat and (not opts.type or opts.type == stat.type) then
|
||||
t[#t + 1] = f
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
for _, match in ipairs(test(path)) do
|
||||
if add(match) then
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
for parent in M.parents(path) do
|
||||
if stop and parent == stop then
|
||||
break
|
||||
end
|
||||
|
||||
for _, match in ipairs(test(parent)) do
|
||||
if add(match) then
|
||||
return matches
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local dirs = { path }
|
||||
while #dirs > 0 do
|
||||
local dir = table.remove(dirs, 1)
|
||||
if stop and dir == stop then
|
||||
break
|
||||
end
|
||||
|
||||
for other, type in M.dir(dir) do
|
||||
local f = dir .. '/' .. other
|
||||
for _, name in ipairs(names) do
|
||||
if name == other and (not opts.type or opts.type == type) then
|
||||
if add(f) then
|
||||
return matches
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type == 'directory' then
|
||||
dirs[#dirs + 1] = f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -66,4 +66,17 @@ describe('vim.fs', function()
|
||||
]], nvim_dir, nvim_prog_basename))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('find()', function()
|
||||
it('works', function()
|
||||
eq({test_build_dir}, exec_lua([[
|
||||
local dir = ...
|
||||
return vim.fs.find('build', { path = dir, upward = true, type = 'directory' })
|
||||
]], nvim_dir))
|
||||
eq({nvim_prog}, exec_lua([[
|
||||
local dir, nvim = ...
|
||||
return vim.fs.find(nvim, { path = dir, type = 'file' })
|
||||
]], test_build_dir, nvim_prog_basename))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user