diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 73cf5859f4..ff963ecad1 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2594,18 +2594,22 @@ Example: >lua < -vim.fs.abspath({path}) *vim.fs.abspath()* +vim.fs.abspath({path}, {opts}) *vim.fs.abspath()* Converts `path` to an absolute path. Expands tilde (~) at the beginning of - the path to the user's home directory. Does not check if the path exists, - normalize the path, resolve symlinks or hardlinks (including `.` and - `..`), or expand environment variables. If the path is already absolute, - it is returned unchanged. Also converts `\` path separators to `/`. + the path (unless plain=true). Does not check if the path exists, normalize + the path, resolve symlinks or hardlinks (including "." and ".."), or + expand environment variables. If the path is already absolute, it is + returned unchanged. Converts `\` path separators to `/`. Attributes: ~ Since: 0.11.0 Parameters: ~ • {path} (`string`) Path + • {opts} (`table?`) A table with the following fields: + • {cwd}? (`string`) Resolve the path relative to this + directory. + • {plain}? (`boolean`) Do not expand tilde (~). Return: ~ (`string`) Absolute path diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index fac8da95f6..53d9d68da7 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -317,6 +317,7 @@ LUA • |vim.net.request()| can now accept `method` param overload for multiple HTTP methods. • |writefile()| treats Lua and RPC strings as |Blob|, so it can be used to write binary data. +• |vim.fs.abspath()| gained `cwd` and `plain` parameters. • |vim.fs.dir()| with `opts.err=true`, reports errors. An inaccessible root dir yields a single (name, nil, err) item. • |vim.fs.dir()| gained a `normalize` parameter. diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 1c48944807..0be6333d76 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -865,21 +865,37 @@ function M.rm(path, opts) end end ---- Converts `path` to an absolute path. Expands tilde (~) at the beginning of the path ---- to the user's home directory. Does not check if the path exists, normalize the path, resolve ---- symlinks or hardlinks (including `.` and `..`), or expand environment variables. If the path is ---- already absolute, it is returned unchanged. Also converts `\` path separators to `/`. +--- @class vim.fs.abspath.Opts +--- @inlinedoc +--- +--- Resolve the path relative to this directory. +--- @field cwd? string +--- +--- Do not expand tilde (~). +--- @field plain? boolean + +--- Converts `path` to an absolute path. Expands tilde (~) at the beginning of the path (unless +--- plain=true). Does not check if the path exists, normalize the path, resolve symlinks or +--- hardlinks (including "." and ".."), or expand environment variables. If the path is already +--- absolute, it is returned unchanged. Converts `\` path separators to `/`. --- --- @since 13 --- @param path string Path +--- @param opts? vim.fs.abspath.Opts --- @return string Absolute path -function M.abspath(path) +function M.abspath(path, opts) -- TODO(justinmk): mark f_fnamemodify as API_FAST and use it, ":p:h" should be safe... + -- + opts = opts or {} vim.validate('path', path, 'string') + vim.validate('cwd', opts.cwd, 'string', true) + vim.validate('plain', opts.plain, 'boolean', true) -- Expand ~ to user's home directory - path = expand_home(path) + if not opts.plain then + path = expand_home(path) + end -- Convert path separator to `/` path = path:gsub(os_sep, '/') @@ -897,7 +913,8 @@ function M.abspath(path) -- Windows allows paths like C:foo/bar, these paths are relative to the current working directory -- of the drive specified in the path - local cwd = assert((iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or uv.cwd()) + local cwd = + assert((iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or opts.cwd or uv.cwd()) -- Convert cwd path separator to `/` cwd = cwd:gsub(os_sep, '/') diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 5a328303e6..88cb63a692 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -852,17 +852,31 @@ describe('vim.fs', function() eq([[C:/foo]], vim.fs.abspath([[C:\foo]])) eq([[C:/foo/../.]], vim.fs.abspath([[C:\foo\..\.]])) eq('//foo/bar', vim.fs.abspath('\\\\foo\\bar')) + 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('with `cwd`', function() + local parent_cwd = vim.fs.dirname(cwd) + eq(parent_cwd, vim.fs.abspath('.', { cwd = parent_cwd })) + eq(parent_cwd .. '/foo', vim.fs.abspath('foo', { cwd = parent_cwd })) + eq(parent_cwd .. '/.././../foo', vim.fs.abspath('.././../foo', { cwd = parent_cwd })) + eq('/foo/bar', vim.fs.abspath('/foo/bar', { cwd = parent_cwd })) + end) + it('expands ~', function() eq(home .. '/foo', vim.fs.abspath('~/foo')) eq(home .. '/./.././foo', vim.fs.abspath('~/./.././foo')) end) + it('does not expand ~ if plain=true', function() + eq(cwd .. '/~/foo', vim.fs.abspath('~/foo', { plain = true })) + eq(cwd .. '/~/./.././foo', vim.fs.abspath('~/./.././foo', { plain = true })) + end) + if is_os('win') then it('works with drive-specific cwd on Windows', function() local cwd_drive = cwd:match('^%w:')