aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
authorMike <4576770+mike325@users.noreply.github.com>2025-01-15 01:39:17 +0100
committerGitHub <noreply@github.com>2025-01-14 16:39:17 -0800
commit611ef354919f1c6564efd2ff8074545941458ccc (patch)
treee4fe577188f7e05f8c618c3a9f271a48b0ef2f7c /runtime
parente8a6c1b02122852da83dc52184e78369598d8240 (diff)
downloadrneovim-611ef354919f1c6564efd2ff8074545941458ccc.tar.gz
rneovim-611ef354919f1c6564efd2ff8074545941458ccc.tar.bz2
rneovim-611ef354919f1c6564efd2ff8074545941458ccc.zip
feat(vim.fs): find(), dir() can "follow" symlinks #31551
Problem: vim.fs.dir(), vim.fs.find() do not follow symlinks. Solution: - Add "follow" flag. - Enable it by default.
Diffstat (limited to 'runtime')
-rw-r--r--runtime/doc/lua.txt11
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/fs.lua23
3 files changed, 30 insertions, 6 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 44cbf238cf..6e5a77ff7a 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -2983,6 +2983,7 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
• skip: (fun(dir_name: string): boolean)|nil Predicate to
control traversal. Return false to stop searching the
current directory. Only useful when depth > 1
+ • follow: boolean|nil Follow symbolic links. (default: true)
Return: ~
(`Iterator`) over items in {path}. Each iteration yields two values:
@@ -3024,7 +3025,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'})
<
@@ -3038,8 +3039,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.
@@ -3053,6 +3056,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/doc/news.txt b/runtime/doc/news.txt
index 23266d536f..8aa0a8d57b 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -286,6 +286,8 @@ LUA
• |vim.json.encode()| has an option to enable forward slash escaping
• |vim.fs.abspath()| converts paths to absolute paths.
• |vim.fs.relpath()| gets relative path compared to base path.
+• |vim.fs.dir()| and |vim.fs.find()| now follow symbolic links by default,
+ the behavior can be turn off using the new `follow` option.
OPTIONS
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 91e06688b3..5940fa4386 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -136,6 +136,7 @@ end
--- - skip: (fun(dir_name: string): boolean)|nil Predicate
--- to control traversal. Return false to stop searching the current directory.
--- Only useful when depth > 1
+--- - follow: boolean|nil Follow symbolic links. (default: true)
---
---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type".
--- "name" is the basename of the item relative to {path}.
@@ -147,6 +148,7 @@ function M.dir(path, opts)
vim.validate('path', path, 'string')
vim.validate('depth', opts.depth, 'number', true)
vim.validate('skip', opts.skip, 'function', true)
+ vim.validate('follow', opts.follow, 'boolean', true)
path = M.normalize(path)
if not opts.depth or opts.depth == 1 then
@@ -177,7 +179,9 @@ function M.dir(path, opts)
if
opts.depth
and level < opts.depth
- and t == 'directory'
+ and (t == 'directory' or (t == 'link' and opts.follow ~= false and (vim.uv.fs_stat(
+ M.joinpath(path, f)
+ ) or {}).type == 'directory'))
and (not opts.skip or opts.skip(f) ~= false)
then
dirs[#dirs + 1] = { f, level + 1 }
@@ -211,6 +215,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.
---
@@ -234,7 +242,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'})
--- ```
---
@@ -244,6 +252,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:
@@ -256,6 +265,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 }
@@ -345,7 +355,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