aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Trew <66286082+jamestrew@users.noreply.github.com>2024-03-29 12:23:01 -0400
committerGitHub <noreply@github.com>2024-03-29 17:23:01 +0100
commit38e38d1b401e38459842f0e4da431e3dd6c2e527 (patch)
tree3784f17a2835641bdf576c230e2eddfc14bb04b3
parent36acb2a8ec905069e382bb3b6db6b6ac677bce39 (diff)
downloadrneovim-38e38d1b401e38459842f0e4da431e3dd6c2e527.tar.gz
rneovim-38e38d1b401e38459842f0e4da431e3dd6c2e527.tar.bz2
rneovim-38e38d1b401e38459842f0e4da431e3dd6c2e527.zip
fix(fs): allow backslash characters in unix paths
Backslashes are valid characters in unix style paths. Fix the conversion of backslashes to forward slashes in several `vim.fs` functions when not on Windows. On Windows, backslashes will still be converted to forward slashes.
-rw-r--r--runtime/doc/lua.txt9
-rw-r--r--runtime/lua/vim/fs.lua37
-rw-r--r--test/functional/lua/fs_spec.lua68
3 files changed, 65 insertions, 49 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index b9bc73e0b8..e02ed20644 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -2954,13 +2954,14 @@ vim.fs.joinpath({...}) *vim.fs.joinpath()*
vim.fs.normalize({path}, {opts}) *vim.fs.normalize()*
Normalize a path to a standard format. A tilde (~) character at the
- beginning of the path is expanded to the user's home directory and any
- backslash (\) characters are converted to forward slashes (/). Environment
- variables are also expanded.
+ beginning of the path is expanded to the user's home directory and
+ environment variables are also expanded.
+
+ On Windows, backslash (\) characters are converted to forward slashes (/).
Examples: >lua
vim.fs.normalize('C:\\\\Users\\\\jdoe')
- -- 'C:/Users/jdoe'
+ -- On Windows: 'C:/Users/jdoe'
vim.fs.normalize('~/src/neovim')
-- '/home/jdoe/src/neovim'
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index f9fe122f01..b7718ac87a 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -1,6 +1,7 @@
local M = {}
local iswin = vim.uv.os_uname().sysname == 'Windows_NT'
+local os_sep = iswin and '\\' or '/'
--- Iterate over all the parents of the given path.
---
@@ -47,19 +48,23 @@ function M.dirname(file)
return nil
end
vim.validate({ file = { file, 's' } })
- if iswin and file:match('^%w:[\\/]?$') then
- return (file:gsub('\\', '/'))
- elseif not file:match('[\\/]') then
+ if iswin then
+ file = file:gsub(os_sep, '/') --[[@as string]]
+ if file:match('^%w:/?$') then
+ return file
+ end
+ end
+ if not file:match('/') then
return '.'
elseif file == '/' or file:match('^/[^/]+$') then
return '/'
end
---@type string
- local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]')
+ local dir = file:match('/$') and file:sub(1, #file - 1) or file:match('^(/?.+)/')
if iswin and dir:match('^%w:$') then
return dir .. '/'
end
- return (dir:gsub('\\', '/'))
+ return dir
end
--- Return the basename of the given path
@@ -72,10 +77,13 @@ function M.basename(file)
return nil
end
vim.validate({ file = { file, 's' } })
- if iswin and file:match('^%w:[\\/]?$') then
- return ''
+ if iswin then
+ file = file:gsub(os_sep, '/') --[[@as string]]
+ if file:match('^%w:/?$') then
+ return ''
+ end
end
- return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/'))
+ return file:match('/$') and '' or (file:match('[^/]*$'))
end
--- Concatenate directories and/or file paths into a single path with normalization
@@ -334,15 +342,16 @@ end
--- @field expand_env boolean
--- Normalize a path to a standard format. A tilde (~) character at the
---- beginning of the path is expanded to the user's home directory and any
---- backslash (\) characters are converted to forward slashes (/). Environment
---- variables are also expanded.
+--- beginning of the path is expanded to the user's home directory and
+--- environment variables are also expanded.
+---
+--- On Windows, backslash (\) characters are converted to forward slashes (/).
---
--- Examples:
---
--- ```lua
--- vim.fs.normalize('C:\\\\Users\\\\jdoe')
---- -- 'C:/Users/jdoe'
+--- -- On Windows: 'C:/Users/jdoe'
---
--- vim.fs.normalize('~/src/neovim')
--- -- '/home/jdoe/src/neovim'
@@ -364,7 +373,7 @@ function M.normalize(path, opts)
if path:sub(1, 1) == '~' then
local home = vim.uv.os_homedir() or '~'
- if home:sub(-1) == '\\' or home:sub(-1) == '/' then
+ if home:sub(-1) == os_sep then
home = home:sub(1, -2)
end
path = home .. path:sub(2)
@@ -374,7 +383,7 @@ function M.normalize(path, opts)
path = path:gsub('%$([%w_]+)', vim.uv.os_getenv)
end
- path = path:gsub('\\', '/'):gsub('/+', '/')
+ path = path:gsub(os_sep, '/'):gsub('/+', '/')
if iswin and path:match('^%w:/$') then
return path
end
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index a5cdfdc225..d43f32726d 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -36,6 +36,7 @@ local test_basename_dirname_eq = {
'c:/users/foo',
'c:/users/foo/bar.lua',
'c:/users/foo/bar/../',
+ '~/foo/bar\\baz',
}
local tests_windows_paths = {
@@ -70,26 +71,26 @@ describe('vim.fs', function()
it('works', function()
eq(test_build_dir, vim.fs.dirname(nvim_dir))
- --- @param paths string[]
- local function test_paths(paths)
+ ---@param paths string[]
+ ---@param is_win? boolean
+ local function test_paths(paths, is_win)
+ local gsub = is_win and [[:gsub('\\', '/')]] or ''
+ local code = string.format(
+ [[
+ local path = ...
+ return vim.fn.fnamemodify(path,':h')%s
+ ]],
+ gsub
+ )
+
for _, path in ipairs(paths) do
- eq(
- exec_lua(
- [[
- local path = ...
- return vim.fn.fnamemodify(path,':h'):gsub('\\', '/')
- ]],
- path
- ),
- vim.fs.dirname(path),
- path
- )
+ eq(exec_lua(code, path), vim.fs.dirname(path), path)
end
end
test_paths(test_basename_dirname_eq)
if is_os('win') then
- test_paths(tests_windows_paths)
+ test_paths(tests_windows_paths, true)
end
end)
end)
@@ -98,26 +99,26 @@ describe('vim.fs', function()
it('works', function()
eq(nvim_prog_basename, vim.fs.basename(nvim_prog))
- --- @param paths string[]
- local function test_paths(paths)
+ ---@param paths string[]
+ ---@param is_win? boolean
+ local function test_paths(paths, is_win)
+ local gsub = is_win and [[:gsub('\\', '/')]] or ''
+ local code = string.format(
+ [[
+ local path = ...
+ return vim.fn.fnamemodify(path,':t')%s
+ ]],
+ gsub
+ )
+
for _, path in ipairs(paths) do
- eq(
- exec_lua(
- [[
- local path = ...
- return vim.fn.fnamemodify(path,':t'):gsub('\\', '/')
- ]],
- path
- ),
- vim.fs.basename(path),
- path
- )
+ eq(exec_lua(code, path), vim.fs.basename(path), path)
end
end
test_paths(test_basename_dirname_eq)
if is_os('win') then
- test_paths(tests_windows_paths)
+ test_paths(tests_windows_paths, true)
end
end)
end)
@@ -284,9 +285,6 @@ describe('vim.fs', function()
end)
describe('normalize()', function()
- it('works with backward slashes', function()
- eq('C:/Users/jdoe', vim.fs.normalize('C:\\Users\\jdoe'))
- end)
it('removes trailing /', function()
eq('/home/user', vim.fs.normalize('/home/user/'))
end)
@@ -309,10 +307,18 @@ describe('vim.fs', function()
)
)
end)
+
if is_os('win') then
it('Last slash is not truncated from root drive', function()
eq('C:/', vim.fs.normalize('C:/'))
end)
+ it('converts backward slashes', function()
+ eq('C:/Users/jdoe', vim.fs.normalize('C:\\Users\\jdoe'))
+ end)
+ else
+ it('allows backslashes on unix-based os', function()
+ eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world'))
+ end)
end
end)
end)