diff options
author | Gregory Anders <greg@gpanders.com> | 2022-05-15 20:37:35 -0600 |
---|---|---|
committer | Gregory Anders <greg@gpanders.com> | 2022-05-31 13:04:41 -0600 |
commit | f271d706611049bc53a6a439b310fe60bf0fab13 (patch) | |
tree | e02db4304f800e9ee9f3750edda5bf31b64e1f51 /runtime/lua/vim/fs.lua | |
parent | 2a62bec37ced51678ff914700d7165605d5a0d53 (diff) | |
download | rneovim-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.lua | 117 |
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 |