From 7c5a461b59a7488d47c4d3adb0e84cfd58bacab7 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 4 Dec 2024 20:21:04 +0100 Subject: [PATCH] feat: add follow opt to vim.fs.find --- runtime/doc/lua.txt | 10 +++++++--- runtime/lua/vim/fs.lua | 17 +++++++++++++++-- test/functional/lua/fs_spec.lua | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index dad3d92238..e42e13212e 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -3013,7 +3013,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* -- get all files ending with .cpp or .hpp inside lib/ local cpp_hpp = vim.fs.find(function(name, path) - return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') + return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') end, {limit = math.huge, type = 'file'}) < @@ -3027,8 +3027,10 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* If {names} is a function, it is called for each traversed item with args: • name: base name of the current item - • path: full path of the current item The function should - return `true` if the given item is considered a match. + • path: full path of the current item + + The function should return `true` if the given item is + considered a match. • {opts} (`table`) Optional keyword arguments: • {path}? (`string`) Path to begin searching from. If omitted, the |current-directory| is used. @@ -3042,6 +3044,8 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* • {limit}? (`number`, default: `1`) Stop the search after finding this many matches. Use `math.huge` to place no limit on the number of matches. + • {follow}? (`boolean`, default: `true`) Follow symbolic + links. Return: ~ (`string[]`) Normalized paths |vim.fs.normalize()| of all matching diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 2f007d97c3..fbf0009c27 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -202,6 +202,10 @@ end --- Use `math.huge` to place no limit on the number of matches. --- (default: `1`) --- @field limit? number +--- +--- Follow symbolic links. +--- (default: `true`) +--- @field follow? boolean --- Find files or directories (or other items as specified by `opts.type`) in the given path. --- @@ -225,7 +229,7 @@ end --- --- -- get all files ending with .cpp or .hpp inside lib/ --- local cpp_hpp = vim.fs.find(function(name, path) ---- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') +--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') --- end, {limit = math.huge, type = 'file'}) --- ``` --- @@ -235,6 +239,7 @@ end --- If {names} is a function, it is called for each traversed item with args: --- - name: base name of the current item --- - path: full path of the current item +--- --- The function should return `true` if the given item is considered a match. --- ---@param opts vim.fs.find.Opts Optional keyword arguments: @@ -247,6 +252,7 @@ function M.find(names, opts) vim.validate('stop', opts.stop, 'string', true) vim.validate('type', opts.type, 'string', true) vim.validate('limit', opts.limit, 'number', true) + vim.validate('follow', opts.follow, 'boolean', true) if type(names) == 'string' then names = { names } @@ -336,7 +342,14 @@ function M.find(names, opts) end end - if type_ == 'directory' then + if + type_ == 'directory' + or ( + type_ == 'link' + and opts.follow ~= false + and (vim.uv.fs_stat(f) or {}).type == 'directory' + ) + then dirs[#dirs + 1] = f end end diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 89f6ad6a0e..83e93e1f5e 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -217,6 +217,18 @@ describe('vim.fs', function() end) describe('find()', function() + before_each(function() + vim.uv.fs_symlink( + test_source_path .. '/build', + test_source_path .. '/build_link', + { junction = true, dir = true } + ) + end) + + after_each(function() + vim.uv.fs_unlink(test_source_path .. '/build_link') + end) + it('works', function() eq( { test_build_dir .. '/build' }, @@ -224,6 +236,26 @@ describe('vim.fs', function() ) eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' })) + eq( + { nvim_prog, test_source_path .. '/build_link/bin/' .. nvim_prog_basename }, + vim.fs.find(nvim_prog_basename, { + path = test_source_path, + type = 'file', + limit = 2, + follow = true, + }) + ) + + eq( + { nvim_prog }, + vim.fs.find(nvim_prog_basename, { + path = test_source_path, + type = 'file', + limit = 2, + follow = false, + }) + ) + local parent, name = nvim_dir:match('^(.*/)([^/]+)$') eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' })) end)