aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFamiu Haque <famiuhaque@proton.me>2024-04-05 14:48:13 +0600
committerdundargoc <33953936+dundargoc@users.noreply.github.com>2024-12-28 11:40:39 +0100
commit518070731003e30ea7eee96e76ccdf7b950c90da (patch)
tree71f361f6ef8de2799ba290d112ada0597a0606df
parent48c09ed4d9edd92a7c665a62aed04e8597088e60 (diff)
downloadrneovim-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.txt14
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--runtime/lua/vim/fs.lua69
-rw-r--r--test/functional/lua/fs_spec.lua34
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)