mirror of
https://github.com/neovim/neovim.git
synced 2026-03-28 19:32:01 +00:00
Problem: Checking the extension of a file is done often, e.g. in Nvim's codebase for differentiating Lua and Vimscript files in the runtime. The current way to do this in Lua is (1) a Lua pattern match, which has pitfalls such as not considering filenames starting with a dot, or (2) fnamemodify() which is both hard to discover and hard to use / read if not very familiar with the possible modifiers. vim.fs.ext() returns the file extension including the leading dot of the extension. Similar to the "file extension" implementation of many other stdlibs (including fnamemodify(file, ":e")), a leading dot doesn't indicate the start of the extension. E.g.: the .git folder in a repository doesn't have the extension .git, but it simply has no extension, similar to a folder named git or any other filename without dot(s).
763 lines
25 KiB
Lua
763 lines
25 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
|
|
local clear = n.clear
|
|
local exec_lua = n.exec_lua
|
|
local eq = t.eq
|
|
local eq_paths = t.eq_paths
|
|
local mkdir_p = n.mkdir_p
|
|
local rmdir = n.rmdir
|
|
local nvim_dir = n.nvim_dir
|
|
local command = n.command
|
|
local api = n.api
|
|
local fn = n.fn
|
|
local test_build_dir = t.paths.test_build_dir
|
|
local test_source_path = t.paths.test_source_path
|
|
local nvim_prog = n.nvim_prog
|
|
local is_os = t.is_os
|
|
local mkdir = t.mkdir
|
|
|
|
local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim'
|
|
|
|
local link_limit = is_os('win') and 64 or (is_os('mac') or is_os('bsd')) and 33 or 41
|
|
|
|
local test_basename_dirname_eq = {
|
|
'~/foo/',
|
|
'~/foo',
|
|
'~/foo/bar.lua',
|
|
'foo.lua',
|
|
' ',
|
|
'',
|
|
'.',
|
|
'..',
|
|
'../',
|
|
'~',
|
|
'/usr/bin',
|
|
'/usr/bin/gcc',
|
|
'/',
|
|
'/usr/',
|
|
'/usr',
|
|
'c:/usr',
|
|
'c:/',
|
|
'c:',
|
|
'c:/users/foo',
|
|
'c:/users/foo/bar.lua',
|
|
'c:/users/foo/bar/../',
|
|
'~/foo/bar\\baz',
|
|
}
|
|
|
|
local tests_windows_paths = {
|
|
'c:\\usr',
|
|
'c:\\',
|
|
'c:',
|
|
'c:\\users\\foo',
|
|
'c:\\users\\foo\\bar.lua',
|
|
'c:\\users\\foo\\bar\\..\\',
|
|
}
|
|
|
|
setup(clear)
|
|
|
|
describe('vim.fs', function()
|
|
describe('parents()', function()
|
|
it('works', function()
|
|
local test_dir = nvim_dir .. '/test'
|
|
mkdir_p(test_dir)
|
|
local dirs = {} --- @type string[]
|
|
for dir in vim.fs.parents(test_dir .. '/foo.txt') do
|
|
dirs[#dirs + 1] = dir
|
|
if dir == test_build_dir then
|
|
break
|
|
end
|
|
end
|
|
eq({ test_dir, nvim_dir, test_build_dir }, dirs)
|
|
rmdir(test_dir)
|
|
end)
|
|
end)
|
|
|
|
describe('dirname()', function()
|
|
it('works', function()
|
|
eq(test_build_dir, vim.fs.dirname(nvim_dir))
|
|
|
|
---@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(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, true)
|
|
end
|
|
end)
|
|
end)
|
|
|
|
describe('basename()', function()
|
|
it('works', function()
|
|
eq(nvim_prog_basename, vim.fs.basename(nvim_prog))
|
|
|
|
---@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(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, true)
|
|
end
|
|
end)
|
|
end)
|
|
|
|
describe('dir()', function()
|
|
before_each(function()
|
|
mkdir('testd')
|
|
mkdir('testd/a')
|
|
mkdir('testd/a/b')
|
|
mkdir('testd/a/b/c')
|
|
end)
|
|
|
|
after_each(function()
|
|
rmdir('testd')
|
|
end)
|
|
|
|
it('works', function()
|
|
eq(
|
|
true,
|
|
exec_lua(function()
|
|
for name, type in vim.fs.dir(nvim_dir) do
|
|
if name == nvim_prog_basename and type == 'file' then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end)
|
|
)
|
|
end)
|
|
|
|
it('works with opts.depth, opts.skip and opts.follow', function()
|
|
io.open('testd/a1', 'w'):close()
|
|
io.open('testd/b1', 'w'):close()
|
|
io.open('testd/c1', 'w'):close()
|
|
io.open('testd/a/a2', 'w'):close()
|
|
io.open('testd/a/b2', 'w'):close()
|
|
io.open('testd/a/c2', 'w'):close()
|
|
io.open('testd/a/b/a3', 'w'):close()
|
|
io.open('testd/a/b/b3', 'w'):close()
|
|
io.open('testd/a/b/c3', 'w'):close()
|
|
io.open('testd/a/b/c/a4', 'w'):close()
|
|
io.open('testd/a/b/c/b4', 'w'):close()
|
|
io.open('testd/a/b/c/c4', 'w'):close()
|
|
|
|
local function run(dir, depth, skip, follow)
|
|
return exec_lua(function(follow_)
|
|
local r = {} --- @type table<string, string>
|
|
local skip_f --- @type function
|
|
if skip then
|
|
skip_f = function(n0)
|
|
if vim.tbl_contains(skip or {}, n0) then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f, follow = follow_ }) do
|
|
r[name] = type_
|
|
end
|
|
return r
|
|
end, follow)
|
|
end
|
|
|
|
local exp = {}
|
|
|
|
exp['a1'] = 'file'
|
|
exp['b1'] = 'file'
|
|
exp['c1'] = 'file'
|
|
exp['a'] = 'directory'
|
|
|
|
eq(exp, run('testd', 1))
|
|
|
|
exp['a/a2'] = 'file'
|
|
exp['a/b2'] = 'file'
|
|
exp['a/c2'] = 'file'
|
|
exp['a/b'] = 'directory'
|
|
local lexp = vim.deepcopy(exp)
|
|
|
|
eq(exp, run('testd', 2))
|
|
|
|
exp['a/b/a3'] = 'file'
|
|
exp['a/b/b3'] = 'file'
|
|
exp['a/b/c3'] = 'file'
|
|
exp['a/b/c'] = 'directory'
|
|
|
|
eq(exp, run('testd', 3))
|
|
eq(exp, run('testd', 999, { 'a/b/c' }))
|
|
|
|
exp['a/b/c/a4'] = 'file'
|
|
exp['a/b/c/b4'] = 'file'
|
|
exp['a/b/c/c4'] = 'file'
|
|
|
|
eq(exp, run('testd', 999))
|
|
|
|
vim.uv.fs_symlink(vim.uv.fs_realpath('testd/a'), 'testd/l', { junction = true, dir = true })
|
|
lexp['l'] = 'link'
|
|
eq(lexp, run('testd', 2, nil, false))
|
|
|
|
lexp['l/a2'] = 'file'
|
|
lexp['l/b2'] = 'file'
|
|
lexp['l/c2'] = 'file'
|
|
lexp['l/b'] = 'directory'
|
|
eq(lexp, run('testd', 2, nil, true))
|
|
end)
|
|
|
|
it('follow=true handles symlink loop', function()
|
|
local cwd = 'testd/a/b/c'
|
|
local symlink = cwd .. '/link_loop' ---@type string
|
|
vim.uv.fs_symlink(vim.uv.fs_realpath(cwd), symlink, { junction = true, dir = true })
|
|
|
|
eq(
|
|
link_limit,
|
|
exec_lua(function()
|
|
return #vim.iter(vim.fs.dir(cwd, { depth = math.huge, follow = true })):totable()
|
|
end)
|
|
)
|
|
end)
|
|
end)
|
|
|
|
describe('find()', function()
|
|
it('works', function()
|
|
eq(
|
|
{ test_build_dir .. '/bin' },
|
|
vim.fs.find('bin', { path = nvim_dir, upward = true, type = 'directory' })
|
|
)
|
|
eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' }))
|
|
|
|
local parent, name = nvim_dir:match('^(.*/)([^/]+)$')
|
|
eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' }))
|
|
end)
|
|
|
|
it('follows symlinks', function()
|
|
local build_dir = test_build_dir ---@type string
|
|
local symlink = test_source_path .. '/build_link' ---@type string
|
|
vim.uv.fs_symlink(build_dir, symlink, { junction = true, dir = true })
|
|
|
|
finally(function()
|
|
vim.uv.fs_unlink(symlink)
|
|
end)
|
|
|
|
local cases = { nvim_prog, symlink .. '/bin/' .. nvim_prog_basename }
|
|
table.sort(cases)
|
|
|
|
eq(
|
|
cases,
|
|
vim.fs.find(nvim_prog_basename, {
|
|
path = test_source_path,
|
|
type = 'file',
|
|
limit = 2,
|
|
follow = true,
|
|
})
|
|
)
|
|
|
|
if t.is_zig_build() then
|
|
return pending('broken with build.zig')
|
|
end
|
|
eq(
|
|
{ nvim_prog },
|
|
vim.fs.find(nvim_prog_basename, {
|
|
path = test_source_path,
|
|
type = 'file',
|
|
limit = 2,
|
|
follow = false,
|
|
})
|
|
)
|
|
end)
|
|
|
|
it('follow=true handles symlink loop', function()
|
|
if t.is_zig_build() then
|
|
return pending('broken/slow with build.zig')
|
|
end
|
|
local cwd = vim.uv.fs_realpath(test_source_path) ---@type string
|
|
local symlink = cwd .. '/loop_link' ---@type string
|
|
vim.uv.fs_symlink(cwd, symlink, { junction = true, dir = true })
|
|
|
|
finally(function()
|
|
vim.uv.fs_unlink(symlink)
|
|
end)
|
|
|
|
eq(link_limit, #vim.fs.find(nvim_prog_basename, {
|
|
path = cwd,
|
|
type = 'file',
|
|
limit = math.huge,
|
|
follow = true,
|
|
}))
|
|
end)
|
|
|
|
it('accepts predicate as names', function()
|
|
local opts = { path = nvim_dir, upward = true, type = 'directory' }
|
|
eq(
|
|
{ test_build_dir .. '/bin' },
|
|
vim.fs.find(function(x)
|
|
return x == 'bin'
|
|
end, opts)
|
|
)
|
|
eq(
|
|
{ nvim_prog },
|
|
vim.fs.find(function(x)
|
|
return x == nvim_prog_basename
|
|
end, { path = test_build_dir, type = 'file' })
|
|
)
|
|
eq(
|
|
{},
|
|
vim.fs.find(function(x)
|
|
return x == 'no-match'
|
|
end, opts)
|
|
)
|
|
|
|
opts = { path = test_source_path .. '/contrib', limit = math.huge }
|
|
eq(
|
|
exec_lua(function()
|
|
return vim.tbl_map(
|
|
vim.fs.basename,
|
|
vim.fn.glob(test_source_path .. '/contrib/*', false, true)
|
|
)
|
|
end),
|
|
vim.tbl_map(
|
|
vim.fs.basename,
|
|
vim.fs.find(function(_, d)
|
|
return d:match('[\\/]contrib$')
|
|
end, opts)
|
|
)
|
|
)
|
|
end)
|
|
end)
|
|
|
|
describe('root()', function()
|
|
before_each(function()
|
|
command('edit test/functional/fixtures/tty-test.c')
|
|
end)
|
|
|
|
after_each(function()
|
|
command('bwipe!')
|
|
end)
|
|
|
|
it('works with a single marker', function()
|
|
eq_paths(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
|
|
end)
|
|
|
|
it('works with multiple markers', function()
|
|
local bufnr = api.nvim_get_current_buf()
|
|
eq_paths(
|
|
vim.fs.joinpath(test_source_path, 'test/functional/fixtures'),
|
|
exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', 'CMakePresets.json'})]], bufnr)
|
|
)
|
|
end)
|
|
|
|
it('nested markers have equal priority', function()
|
|
local bufnr = api.nvim_get_current_buf()
|
|
eq_paths(
|
|
vim.fs.joinpath(test_source_path, 'test/functional'),
|
|
exec_lua(
|
|
[[return vim.fs.root(..., { 'example_spec.lua', {'CMakeLists.txt', 'CMakePresets.json'}, '.luarc.json'})]],
|
|
bufnr
|
|
)
|
|
)
|
|
eq_paths(
|
|
vim.fs.joinpath(test_source_path, 'test/functional/fixtures'),
|
|
exec_lua(
|
|
[[return vim.fs.root(..., { {'CMakeLists.txt', 'CMakePresets.json'}, 'example_spec.lua', '.luarc.json'})]],
|
|
bufnr
|
|
)
|
|
)
|
|
eq_paths(
|
|
vim.fs.joinpath(test_source_path, 'test/functional/fixtures'),
|
|
exec_lua(
|
|
[[return vim.fs.root(..., {
|
|
function(name, _)
|
|
return name:match('%.txt$')
|
|
end,
|
|
'example_spec.lua',
|
|
'.luarc.json' })]],
|
|
bufnr
|
|
)
|
|
)
|
|
end)
|
|
|
|
it('works with a function', function()
|
|
---@type string
|
|
local result = exec_lua(function()
|
|
return vim.fs.root(0, function(name, _)
|
|
return name:match('%.txt$')
|
|
end)
|
|
end)
|
|
eq_paths(vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), result)
|
|
end)
|
|
|
|
it('works with a filename argument', function()
|
|
eq(test_source_path, exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], nvim_prog))
|
|
end)
|
|
|
|
it('works with a relative path', function()
|
|
eq_paths(
|
|
test_source_path,
|
|
exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], vim.fs.basename(nvim_prog))
|
|
)
|
|
end)
|
|
|
|
it('returns CWD (absolute path) for unnamed buffers', function()
|
|
assert(n.fn.isabsolutepath(test_source_path) == 1)
|
|
command('new')
|
|
eq_paths(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
|
|
end)
|
|
|
|
it("returns CWD (absolute path) for buffers with non-empty 'buftype'", function()
|
|
assert(n.fn.isabsolutepath(test_source_path) == 1)
|
|
command('new')
|
|
command('set buftype=nofile')
|
|
command('file lua://')
|
|
eq_paths(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
|
|
end)
|
|
|
|
it('returns CWD (absolute path) if no match is found', function()
|
|
assert(n.fn.isabsolutepath(test_source_path) == 1)
|
|
eq_paths(
|
|
test_source_path,
|
|
exec_lua([[return vim.fs.root('file://bogus', 'CMakePresets.json')]])
|
|
)
|
|
end)
|
|
end)
|
|
|
|
describe('joinpath()', function()
|
|
it('works', function()
|
|
eq('foo/bar/baz', vim.fs.joinpath('foo', 'bar', 'baz'))
|
|
eq('foo/bar/baz', vim.fs.joinpath('foo', '/bar/', '/baz'))
|
|
end)
|
|
it('rewrites backslashes on Windows', function()
|
|
if is_os('win') then
|
|
eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]]))
|
|
else
|
|
eq([[foo/\\bar\\\\baz/zub\]], vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]]))
|
|
end
|
|
end)
|
|
it('strips redundant slashes', function()
|
|
if is_os('win') then
|
|
eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo//]], [[\\bar\\\\baz]], [[zub\]]))
|
|
else
|
|
eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[//bar////baz]], [[zub/]]))
|
|
end
|
|
end)
|
|
it('handles empty segments', function()
|
|
eq('foo/bar', vim.fs.joinpath('', 'foo', '', 'bar', ''))
|
|
eq('foo/bar', vim.fs.joinpath('', '', 'foo', 'bar', '', ''))
|
|
eq('', vim.fs.joinpath(''))
|
|
eq('', vim.fs.joinpath('', '', '', ''))
|
|
end)
|
|
end)
|
|
|
|
describe('normalize()', function()
|
|
it('removes trailing /', function()
|
|
eq('/home/user', vim.fs.normalize('/home/user/'))
|
|
end)
|
|
it('works with /', function()
|
|
eq('/', vim.fs.normalize('/'))
|
|
end)
|
|
it('works with ~', function()
|
|
eq(vim.fs.normalize(assert(vim.uv.os_homedir())) .. '/src/foo', vim.fs.normalize('~/src/foo'))
|
|
end)
|
|
it('works with environment variables', function()
|
|
local xdg_config_home = test_build_dir .. '/.config'
|
|
eq(
|
|
xdg_config_home .. '/nvim',
|
|
exec_lua(function()
|
|
return vim._with({ env = { XDG_CONFIG_HOME = xdg_config_home } }, function()
|
|
return vim.fs.normalize('$XDG_CONFIG_HOME/nvim')
|
|
end)
|
|
end)
|
|
)
|
|
end)
|
|
|
|
-- Opts required for testing posix paths and win paths
|
|
local posix_opts = { win = false }
|
|
local win_opts = { win = true }
|
|
|
|
it('preserves leading double slashes in POSIX paths', function()
|
|
eq('//foo', vim.fs.normalize('//foo', posix_opts))
|
|
eq('//foo/bar', vim.fs.normalize('//foo//bar////', posix_opts))
|
|
eq('/foo', vim.fs.normalize('///foo', posix_opts))
|
|
eq('//', vim.fs.normalize('//', posix_opts))
|
|
eq('/', vim.fs.normalize('///', posix_opts))
|
|
eq('/foo/bar', vim.fs.normalize('/foo//bar////', posix_opts))
|
|
end)
|
|
|
|
it('normalizes drive letter', function()
|
|
eq('C:/', vim.fs.normalize('C:/', win_opts))
|
|
eq('C:/', vim.fs.normalize('c:/', win_opts))
|
|
eq('D:/', vim.fs.normalize('d:/', win_opts))
|
|
eq('C:', vim.fs.normalize('C:', win_opts))
|
|
eq('C:', vim.fs.normalize('c:', win_opts))
|
|
eq('D:', vim.fs.normalize('d:', win_opts))
|
|
eq('C:/foo/test', vim.fs.normalize('C:/foo/test/', win_opts))
|
|
eq('C:/foo/test', vim.fs.normalize('c:/foo/test/', win_opts))
|
|
eq('D:foo/test', vim.fs.normalize('D:foo/test/', win_opts))
|
|
eq('D:foo/test', vim.fs.normalize('d:foo/test/', win_opts))
|
|
end)
|
|
|
|
it('always treats paths as case-sensitive #31833', function()
|
|
eq('TEST', vim.fs.normalize('TEST', win_opts))
|
|
eq('test', vim.fs.normalize('test', win_opts))
|
|
eq('C:/FOO/test', vim.fs.normalize('C:/FOO/test', win_opts))
|
|
eq('C:/foo/test', vim.fs.normalize('C:/foo/test', win_opts))
|
|
eq('//SERVER/SHARE/FOO/BAR', vim.fs.normalize('//SERVER/SHARE/FOO/BAR', win_opts))
|
|
eq('//server/share/foo/bar', vim.fs.normalize('//server/share/foo/bar', win_opts))
|
|
eq('C:/FOO/test', vim.fs.normalize('c:/FOO/test', win_opts))
|
|
end)
|
|
|
|
it('allows backslashes on unix-based os', function()
|
|
eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world', posix_opts))
|
|
end)
|
|
|
|
it('preserves / after drive letters', function()
|
|
eq('C:/', vim.fs.normalize([[C:\]], win_opts))
|
|
end)
|
|
|
|
it('works with UNC and DOS device paths', function()
|
|
eq('//server/share/foo/bar', vim.fs.normalize([[\\server\\share\\\foo\bar\\\]], win_opts))
|
|
eq('//system07/C$/', vim.fs.normalize([[\\system07\C$\\\\]], win_opts))
|
|
eq('//./C:/foo/bar', vim.fs.normalize([[\\.\\C:\foo\\\\bar]], win_opts))
|
|
eq('//?/C:/foo/bar', vim.fs.normalize([[\\?\C:\\\foo\bar\\\\]], win_opts))
|
|
eq(
|
|
'//?/UNC/server/share/foo/bar',
|
|
vim.fs.normalize([[\\?\UNC\server\\\share\\\\foo\\\bar]], win_opts)
|
|
)
|
|
eq('//./BootPartition/foo/bar', vim.fs.normalize([[\\.\BootPartition\\foo\bar]], win_opts))
|
|
eq(
|
|
'//./Volume{12345678-1234-1234-1234-1234567890AB}/foo/bar',
|
|
vim.fs.normalize([[\\.\Volume{12345678-1234-1234-1234-1234567890AB}\\\foo\bar\\]], win_opts)
|
|
)
|
|
end)
|
|
|
|
it('handles invalid UNC and DOS device paths', function()
|
|
eq('//server/share', vim.fs.normalize([[\\server\share]], win_opts))
|
|
eq('//server/', vim.fs.normalize([[\\server\]], win_opts))
|
|
eq('//./UNC/server/share', vim.fs.normalize([[\\.\UNC\server\share]], win_opts))
|
|
eq('//?/UNC/server/', vim.fs.normalize([[\\?\UNC\server\]], win_opts))
|
|
eq('//?/UNC/server/..', vim.fs.normalize([[\\?\UNC\server\..]], win_opts))
|
|
eq('//./', vim.fs.normalize([[\\.\]], win_opts))
|
|
eq('//./foo', vim.fs.normalize([[\\.\foo]], win_opts))
|
|
eq('//./BootPartition', vim.fs.normalize([[\\.\BootPartition]], win_opts))
|
|
end)
|
|
|
|
it('converts backward slashes', function()
|
|
eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe]], win_opts))
|
|
end)
|
|
|
|
describe('. and .. component resolving', function()
|
|
it('works', function()
|
|
-- Windows paths
|
|
eq('C:/Users', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\..\]], win_opts))
|
|
eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\.\.\]], win_opts))
|
|
eq('C:/', vim.fs.normalize('C:/Users/jdoe/Downloads/./../../../', win_opts))
|
|
eq('C:foo', vim.fs.normalize([[C:foo\bar\.\..\.]], win_opts))
|
|
-- POSIX paths
|
|
eq('/home', vim.fs.normalize('/home/jdoe/Downloads/./../..', posix_opts))
|
|
eq('/home/jdoe', vim.fs.normalize('/home/jdoe/Downloads/./../././', posix_opts))
|
|
eq('/', vim.fs.normalize('/home/jdoe/Downloads/./../../../', posix_opts))
|
|
-- OS-agnostic relative paths
|
|
eq('foo/bar/baz', vim.fs.normalize('foo/bar/foobar/../baz/./'))
|
|
eq('foo/bar', vim.fs.normalize('foo/bar/foobar/../baz/./../../bar/./.'))
|
|
end)
|
|
|
|
it('works when relative path reaches current directory', function()
|
|
eq('C:', vim.fs.normalize('C:foo/bar/../../.', win_opts))
|
|
|
|
eq('.', vim.fs.normalize('.'))
|
|
eq('.', vim.fs.normalize('././././'))
|
|
eq('.', vim.fs.normalize('foo/bar/../../.'))
|
|
end)
|
|
|
|
it('works when relative path goes outside current directory', function()
|
|
eq('../../foo/bar', vim.fs.normalize('../../foo/bar'))
|
|
eq('../foo', vim.fs.normalize('foo/bar/../../../foo'))
|
|
|
|
eq('C:../foo', vim.fs.normalize('C:../foo', win_opts))
|
|
eq('C:../../foo/bar', vim.fs.normalize('C:foo/../../../foo/bar', win_opts))
|
|
end)
|
|
|
|
it('.. in root directory resolves to itself', function()
|
|
eq('C:/', vim.fs.normalize('C:/../../', win_opts))
|
|
eq('C:/foo', vim.fs.normalize('C:/foo/../../foo', win_opts))
|
|
|
|
eq('//server/share/', vim.fs.normalize([[\\server\share\..\..]], win_opts))
|
|
eq('//server/share/foo', vim.fs.normalize([[\\server\\share\foo\..\..\foo]], win_opts))
|
|
|
|
eq('//./C:/', vim.fs.normalize([[\\.\C:\..\..]], win_opts))
|
|
eq('//?/C:/foo', vim.fs.normalize([[\\?\C:\..\..\foo]], win_opts))
|
|
|
|
eq('//./UNC/server/share/', vim.fs.normalize([[\\.\UNC\\server\share\..\..\]], win_opts))
|
|
eq(
|
|
'//?/UNC/server/share/foo',
|
|
vim.fs.normalize([[\\?\UNC\server\\share\..\..\foo]], win_opts)
|
|
)
|
|
|
|
eq('//?/BootPartition/', vim.fs.normalize([[\\?\BootPartition\..\..]], win_opts))
|
|
eq('//./BootPartition/foo', vim.fs.normalize([[\\.\BootPartition\..\..\foo]], win_opts))
|
|
|
|
eq('/', vim.fs.normalize('/../../', posix_opts))
|
|
eq('/foo', vim.fs.normalize('/foo/../../foo', posix_opts))
|
|
end)
|
|
end)
|
|
end)
|
|
|
|
describe('abspath()', function()
|
|
local cwd = assert(t.fix_slashes(assert(vim.uv.cwd())))
|
|
local home = t.fix_slashes(assert(vim.uv.os_homedir()))
|
|
|
|
it('expands relative paths', function()
|
|
assert(n.fn.isabsolutepath(cwd) == 1)
|
|
eq(cwd, vim.fs.abspath('.'))
|
|
eq(cwd .. '/foo', vim.fs.abspath('foo'))
|
|
eq(cwd .. '/././foo', vim.fs.abspath('././foo'))
|
|
eq(cwd .. '/.././../foo', vim.fs.abspath('.././../foo'))
|
|
end)
|
|
|
|
it('works with absolute paths', function()
|
|
if is_os('win') then
|
|
eq([[C:/foo]], vim.fs.abspath([[C:\foo]]))
|
|
eq([[C:/foo/../.]], vim.fs.abspath([[C:\foo\..\.]]))
|
|
eq('//foo/bar', vim.fs.abspath('\\\\foo\\bar'))
|
|
else
|
|
eq('/foo/../.', vim.fs.abspath('/foo/../.'))
|
|
eq('/foo/bar', vim.fs.abspath('/foo/bar'))
|
|
end
|
|
end)
|
|
|
|
it('expands ~', function()
|
|
eq(home .. '/foo', vim.fs.abspath('~/foo'))
|
|
eq(home .. '/./.././foo', vim.fs.abspath('~/./.././foo'))
|
|
end)
|
|
|
|
if is_os('win') then
|
|
it('works with drive-specific cwd on Windows', function()
|
|
local cwd_drive = cwd:match('^%w:')
|
|
|
|
eq(cwd .. '/foo', vim.fs.abspath(cwd_drive .. 'foo'))
|
|
end)
|
|
end
|
|
end)
|
|
|
|
describe('relpath()', function()
|
|
it('works', function()
|
|
local cwd = assert(t.fix_slashes(assert(vim.uv.cwd())))
|
|
local my_dir = vim.fs.joinpath(cwd, 'foo')
|
|
|
|
eq(nil, vim.fs.relpath('/var/lib', '/var'))
|
|
eq(nil, vim.fs.relpath('/var/lib', '/bin'))
|
|
eq(nil, vim.fs.relpath(my_dir, 'bin'))
|
|
eq(nil, vim.fs.relpath(my_dir, './bin'))
|
|
eq(nil, vim.fs.relpath(my_dir, '././'))
|
|
eq(nil, vim.fs.relpath(my_dir, '../'))
|
|
eq(nil, vim.fs.relpath('/var/lib', '/'))
|
|
eq(nil, vim.fs.relpath('/var/lib', '//'))
|
|
eq(nil, vim.fs.relpath(' ', '/var'))
|
|
eq(nil, vim.fs.relpath(' ', '/var'))
|
|
eq('.', vim.fs.relpath('/var/lib', '/var/lib'))
|
|
eq('lib', vim.fs.relpath('/var/', '/var/lib'))
|
|
eq('var/lib', vim.fs.relpath('/', '/var/lib'))
|
|
eq('bar/package.json', vim.fs.relpath('/foo/test', '/foo/test/bar/package.json'))
|
|
eq('foo/bar', vim.fs.relpath(cwd, 'foo/bar'))
|
|
eq('foo/bar', vim.fs.relpath('.', vim.fs.joinpath(cwd, 'foo/bar')))
|
|
eq('bar', vim.fs.relpath('foo', 'foo/bar'))
|
|
eq(nil, vim.fs.relpath('/var/lib', '/var/library/foo'))
|
|
|
|
if is_os('win') then
|
|
eq(nil, vim.fs.relpath('/', ' '))
|
|
eq(nil, vim.fs.relpath('/', 'var'))
|
|
else
|
|
local cwd_rel_root = cwd:sub(2)
|
|
eq(cwd_rel_root .. '/ ', vim.fs.relpath('/', ' '))
|
|
eq(cwd_rel_root .. '/var', vim.fs.relpath('/', 'var'))
|
|
end
|
|
|
|
if is_os('win') then
|
|
eq(nil, vim.fs.relpath('c:/aaaa/', '/aaaa/cccc'))
|
|
eq(nil, vim.fs.relpath('c:/aaaa/', './aaaa/cccc'))
|
|
eq(nil, vim.fs.relpath('c:/aaaa/', 'aaaa/cccc'))
|
|
eq(nil, vim.fs.relpath('c:/blah\\blah', 'd:/games'))
|
|
eq(nil, vim.fs.relpath('c:/games', 'd:/games'))
|
|
eq(nil, vim.fs.relpath('c:/games', 'd:/games/foo'))
|
|
eq(nil, vim.fs.relpath('c:/aaaa/bbbb', 'c:/aaaa'))
|
|
eq('cccc', vim.fs.relpath('c:/aaaa/', 'c:/aaaa/cccc'))
|
|
eq('aaaa/bbbb', vim.fs.relpath('C:/', 'c:\\aaaa\\bbbb'))
|
|
eq('bar/package.json', vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json'))
|
|
eq('baz', vim.fs.relpath('\\\\foo\\bar', '\\\\foo\\bar\\baz'))
|
|
eq(nil, vim.fs.relpath('a/b/c', 'a\\b'))
|
|
eq('d', vim.fs.relpath('a/b/c', 'a\\b\\c\\d'))
|
|
eq('.', vim.fs.relpath('\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'))
|
|
eq(nil, vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\Test\\bar\\package.json'))
|
|
end
|
|
end)
|
|
end)
|
|
|
|
describe('rm()', function()
|
|
before_each(function()
|
|
t.mkdir('Xtest_fs-rm')
|
|
t.write_file('Xtest_fs-rm/file-to-link', 'File to link')
|
|
t.mkdir('Xtest_fs-rm/dir-to-link')
|
|
t.write_file('Xtest_fs-rm/dir-to-link/file', 'File in dir to link')
|
|
end)
|
|
|
|
after_each(function()
|
|
vim.uv.fs_unlink('Xtest_fs-rm/dir-to-link/file')
|
|
vim.uv.fs_rmdir('Xtest_fs-rm/dir-to-link')
|
|
vim.uv.fs_unlink('Xtest_fs-rm/file-to-link')
|
|
vim.uv.fs_rmdir('Xtest_fs-rm')
|
|
end)
|
|
|
|
it('symlink', function()
|
|
-- File
|
|
vim.uv.fs_symlink('Xtest_fs-rm/file-to-link', 'Xtest_fs-rm/file-as-link')
|
|
vim.fs.rm('Xtest_fs-rm/file-as-link')
|
|
eq(vim.uv.fs_stat('Xtest_fs-rm/file-as-link'), nil)
|
|
eq({ 'File to link' }, fn.readfile('Xtest_fs-rm/file-to-link'))
|
|
|
|
-- Directory
|
|
local function assert_rm_symlinked_dir(opts)
|
|
vim.uv.fs_symlink('Xtest_fs-rm/dir-to-link', 'Xtest_fs-rm/dir-as-link')
|
|
vim.fs.rm('Xtest_fs-rm/dir-as-link', opts)
|
|
eq(vim.uv.fs_stat('Xtest_fs-rm/dir-as-link'), nil)
|
|
eq({ 'File in dir to link' }, fn.readfile('Xtest_fs-rm/dir-to-link/file'))
|
|
end
|
|
|
|
assert_rm_symlinked_dir({})
|
|
assert_rm_symlinked_dir({ force = true })
|
|
assert_rm_symlinked_dir({ recursive = true })
|
|
assert_rm_symlinked_dir({ recursive = true, force = true })
|
|
end)
|
|
end)
|
|
|
|
describe('ext()', function()
|
|
it('works', function()
|
|
-- See test/functional/vimscript/fnamemodify_spec.lua
|
|
end)
|
|
end)
|
|
end)
|