diff options
author | Famiu Haque <famiuhaque@proton.me> | 2024-04-05 14:48:13 +0600 |
---|---|---|
committer | dundargoc <33953936+dundargoc@users.noreply.github.com> | 2024-12-28 11:40:39 +0100 |
commit | 518070731003e30ea7eee96e76ccdf7b950c90da (patch) | |
tree | 71f361f6ef8de2799ba290d112ada0597a0606df | |
parent | 48c09ed4d9edd92a7c665a62aed04e8597088e60 (diff) | |
download | rneovim-518070731003e30ea7eee96e76ccdf7b950c90da.tar.gz rneovim-518070731003e30ea7eee96e76ccdf7b950c90da.tar.bz2 rneovim-518070731003e30ea7eee96e76ccdf7b950c90da.zip |
feat(lua): add `vim.fs.abspath`
Problem: There is currently no way to check if a given path is absolute or convert a relative path to an absolute path through the Lua stdlib. `vim.fs.joinpath` does not work when the path is absolute. There is also currently no way to resolve `C:foo\bar` style paths in Windows.
Solution: Add `vim.fs.abspath`, which allows converting any path to an absolute path. This also allows checking if current path is absolute by doing `vim.fs.abspath(path) == path`. It also has support for `C:foo\bar` style paths in Windows.
-rw-r--r-- | runtime/doc/lua.txt | 14 | ||||
-rw-r--r-- | runtime/doc/news.txt | 1 | ||||
-rw-r--r-- | runtime/lua/vim/fs.lua | 69 | ||||
-rw-r--r-- | test/functional/lua/fs_spec.lua | 34 |
4 files changed, 110 insertions, 8 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 711607d14b..a2a83ef229 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2945,6 +2945,20 @@ Example: >lua < +vim.fs.abspath({path}) *vim.fs.abspath()* + Convert path to an absolute path. A tilde (~) character at the beginning + of the path is expanded 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 `/`. + + Parameters: ~ + • {path} (`string`) Path + + Return: ~ + (`string`) Absolute path + vim.fs.basename({file}) *vim.fs.basename()* Return the basename of the given path diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index c4ba4f0ad1..7ea65479f3 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -268,6 +268,7 @@ LUA is more performant and easier to read. • |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures supporting two new parameters, `encoding` and `strict_indexing`. +• |vim.fs.abspath()| converts paths to absolute paths. OPTIONS diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 2f007d97c3..f2cd210cac 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -505,6 +505,27 @@ local function path_resolve_dot(path) return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/') end +--- Expand tilde (~) character at the beginning of the path to the user's home directory. +--- +--- @param path string Path to expand. +--- @param sep string|nil Path separator to use. Uses os_sep by default. +--- @return string Expanded path. +local function expand_home(path, sep) + sep = sep or os_sep + + if vim.startswith(path, '~') then + local home = uv.os_homedir() or '~' --- @type string + + if home:sub(-1) == sep then + home = home:sub(1, -2) + end + + path = home .. path:sub(2) + end + + return path +end + --- @class vim.fs.normalize.Opts --- @inlinedoc --- @@ -568,14 +589,8 @@ function M.normalize(path, opts) return '' end - -- Expand ~ to users home directory - if vim.startswith(path, '~') then - local home = uv.os_homedir() or '~' - if home:sub(-1) == os_sep_local then - home = home:sub(1, -2) - end - path = home .. path:sub(2) - end + -- Expand ~ to user's home directory + path = expand_home(path, os_sep_local) -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then @@ -679,4 +694,42 @@ function M.rm(path, opts) end end +--- Convert path to an absolute path. A tilde (~) character at the beginning of the path is expanded +--- 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 `/`. +--- +--- @param path string Path +--- @return string Absolute path +function M.abspath(path) + vim.validate('path', path, 'string') + + -- Expand ~ to user's home directory + path = expand_home(path) + + -- Convert path separator to `/` + path = path:gsub(os_sep, '/') + + local prefix = '' + + if iswin then + prefix, path = split_windows_path(path) + end + + if vim.startswith(path, '/') then + -- Path is already absolute, do nothing + return prefix .. path + end + + -- 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 = (iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or uv.cwd() + assert(cwd ~= nil) + -- Convert cwd path separator to `/` + cwd = cwd:gsub(os_sep, '/') + + -- Prefix is not needed for expanding relative paths, as `cwd` already contains it. + return M.joinpath(cwd, path) +end + return M diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 89f6ad6a0e..63444f5ba1 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -454,4 +454,38 @@ describe('vim.fs', function() end) end) end) + + describe('abspath', function() + local cwd = is_os('win') and vim.uv.cwd():gsub('\\', '/') or vim.uv.cwd() + local home = is_os('win') and vim.uv.os_homedir():gsub('\\', '/') or vim.uv.os_homedir() + + it('works', function() + 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\..\.]])) + 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) end) |