aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/fs.lua
diff options
context:
space:
mode:
authorGregory Anders <greg@gpanders.com>2022-05-15 20:37:35 -0600
committerGregory Anders <greg@gpanders.com>2022-05-31 13:04:41 -0600
commitf271d706611049bc53a6a439b310fe60bf0fab13 (patch)
treee02db4304f800e9ee9f3750edda5bf31b64e1f51 /runtime/lua/vim/fs.lua
parent2a62bec37ced51678ff914700d7165605d5a0d53 (diff)
downloadrneovim-f271d706611049bc53a6a439b310fe60bf0fab13.tar.gz
rneovim-f271d706611049bc53a6a439b310fe60bf0fab13.tar.bz2
rneovim-f271d706611049bc53a6a439b310fe60bf0fab13.zip
feat(fs): add vim.fs.find()
This is a pure Lua implementation of the Vim findfile() and finddir() functions without the special syntax.
Diffstat (limited to 'runtime/lua/vim/fs.lua')
-rw-r--r--runtime/lua/vim/fs.lua117
1 files changed, 117 insertions, 0 deletions
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index c28b06536b..4519f2a1e4 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -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