From 2424c3e6967ef953222cf48564629ffe5812d415 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Mar 2024 18:05:02 +0100 Subject: [PATCH] fix: support UNC paths in vim.fs.normalize Closes https://github.com/neovim/neovim/issues/27068. --- runtime/lua/vim/fs.lua | 24 +++++++++++++++++++++--- test/functional/lua/fs_spec.lua | 9 +++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index b7718ac87a..ad0d914ea2 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -371,7 +371,8 @@ function M.normalize(path, opts) expand_env = { opts.expand_env, { 'boolean' }, true }, }) - if path:sub(1, 1) == '~' then + -- Expand ~ to users home directory + if vim.startswith(path, '~') then local home = vim.uv.os_homedir() or '~' if home:sub(-1) == os_sep then home = home:sub(1, -2) @@ -379,15 +380,32 @@ function M.normalize(path, opts) path = home .. path:sub(2) end + -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then path = path:gsub('%$([%w_]+)', vim.uv.os_getenv) end - path = path:gsub(os_sep, '/'):gsub('/+', '/') + -- Convert path separator to `/` + path = path:gsub(os_sep, '/') + + -- Don't modify leading double slash as those have implementation-defined behavior according to + -- POSIX. They are also valid UNC paths. Three or more leading slashes are however collapsed to + -- a single slash. + if vim.startswith(path, '//') and not vim.startswith(path, '///') then + path = '/' .. path:gsub('/+', '/') + else + path = path:gsub('/+', '/') + end + + -- Ensure last slash is not truncated from root drive on Windows if iswin and path:match('^%w:/$') then return path end - return (path:gsub('(.)/$', '%1')) + + -- Remove trailing slashes + path = path:gsub('(.)/$', '%1') + + return path end return M diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index d43f32726d..6e7811908b 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -308,6 +308,15 @@ describe('vim.fs', function() ) end) + it('works with UNC paths', function() + eq('//foo', vim.fs.normalize('//foo')) -- UNC path + eq('//foo/bar', vim.fs.normalize('//foo//bar////')) -- UNC path + eq('/foo', vim.fs.normalize('///foo')) -- Not a UNC path + eq('/', vim.fs.normalize('//')) -- Not a UNC path + eq('/', vim.fs.normalize('///')) -- Not a UNC path + eq('/foo/bar', vim.fs.normalize('/foo//bar////')) -- Not a UNC path + end) + if is_os('win') then it('Last slash is not truncated from root drive', function() eq('C:/', vim.fs.normalize('C:/'))