aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/fs.lua
diff options
context:
space:
mode:
authorYochem van Rosmalen <git@yochem.nl>2026-02-27 23:45:07 +0100
committerGitHub <noreply@github.com>2026-02-27 17:45:07 -0500
commitdc5d313d66c620699a926aeefaf20bb0ec0d653e (patch)
tree6f9d4bade619e5f7e20e0357f2b935a2d13768e6 /runtime/lua/vim/fs.lua
parent47479757540fa3b56a48145c74eeb93db81c90f7 (diff)
downloadrneovim-dc5d313d66c620699a926aeefaf20bb0ec0d653e.tar.gz
rneovim-dc5d313d66c620699a926aeefaf20bb0ec0d653e.tar.bz2
rneovim-dc5d313d66c620699a926aeefaf20bb0ec0d653e.zip
fix(vim.fs): joinpath() should ignore empty items #38077
Problem: vim.fs.joinpath treats empty string as a path segment (it adds a path separator for each empty item): print(vim.fs.joinpath('', 'after/lsp', '')) -- '/after/lsp/' print(vim.fs.joinpath('', '')) -- '/' Especially problematic if the empty segment is the first segment, as that converts the path to an absolute path. Solution: Ignore empty (length of 0) path segments. Benchmark: local function test(func) local t = vim.uv.hrtime() for _ = 1, 100000, 1 do func('', 'this/is', 'a/very/long/path', '', 'it', 'really', 'is') end print(math.floor((vim.uv.hrtime() - t) / 1e6), 'ms') end - with Iter():filter() --> 370 ms - building new segments table --> 208 ms - with vim.tbl_filter --> 232 ms - Instead of gsub split on `/` in all parts --> 1870 ms
Diffstat (limited to 'runtime/lua/vim/fs.lua')
-rw-r--r--runtime/lua/vim/fs.lua19
1 files changed, 14 insertions, 5 deletions
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 6e590178d2..dc26790d14 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -116,21 +116,30 @@ end
--- Concatenates partial paths (one absolute or relative path followed by zero or more relative
--- paths). Slashes are normalized: redundant slashes are removed, and (on Windows) backslashes are
---- replaced with forward-slashes. Paths are not expanded/resolved.
+--- replaced with forward-slashes. Empty segments are removed. Paths are not expanded/resolved.
---
--- Examples:
--- - "foo/", "/bar" => "foo/bar"
+--- - "", "after/plugin" => "after/plugin"
--- - Windows: "a\foo\", "\bar" => "a/foo/bar"
---
---@since 12
---@param ... string
---@return string
function M.joinpath(...)
- local path = table.concat({ ... }, '/')
- if iswin then
- path = path:gsub('\\', '/')
+ local n = select('#', ...)
+ ---@type string[]
+ local segments = {}
+ for i = 1, n do
+ local s = select(i, ...)
+ if s and #s > 0 then
+ segments[#segments + 1] = s
+ end
end
- return (path:gsub('//+', '/'))
+
+ local path = table.concat(segments, '/')
+
+ return (path:gsub(iswin and '[/\\][/\\]*' or '//+', '/'))
end
--- @class vim.fs.dir.Opts