mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	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.
This commit is contained in:
		| @@ -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()* | vim.fs.basename({file})                                    *vim.fs.basename()* | ||||||
|     Return the basename of the given path |     Return the basename of the given path | ||||||
|  |  | ||||||
|   | |||||||
| @@ -268,6 +268,7 @@ LUA | |||||||
|   is more performant and easier to read. |   is more performant and easier to read. | ||||||
| • |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures | • |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures | ||||||
|   supporting two new parameters, `encoding` and `strict_indexing`. |   supporting two new parameters, `encoding` and `strict_indexing`. | ||||||
|  | • |vim.fs.abspath()| converts paths to absolute paths. | ||||||
|  |  | ||||||
| OPTIONS | OPTIONS | ||||||
|  |  | ||||||
|   | |||||||
| @@ -505,6 +505,27 @@ local function path_resolve_dot(path) | |||||||
|   return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/') |   return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/') | ||||||
| end | 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 | --- @class vim.fs.normalize.Opts | ||||||
| --- @inlinedoc | --- @inlinedoc | ||||||
| --- | --- | ||||||
| @@ -568,14 +589,8 @@ function M.normalize(path, opts) | |||||||
|     return '' |     return '' | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   -- Expand ~ to users home directory |   -- Expand ~ to user's home directory | ||||||
|   if vim.startswith(path, '~') then |   path = expand_home(path, os_sep_local) | ||||||
|     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 environment variables if `opts.expand_env` isn't `false` |   -- Expand environment variables if `opts.expand_env` isn't `false` | ||||||
|   if opts.expand_env == nil or opts.expand_env then |   if opts.expand_env == nil or opts.expand_env then | ||||||
| @@ -679,4 +694,42 @@ function M.rm(path, opts) | |||||||
|   end |   end | ||||||
| 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 | return M | ||||||
|   | |||||||
| @@ -454,4 +454,38 @@ describe('vim.fs', function() | |||||||
|       end) |       end) | ||||||
|     end) |     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) | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Famiu Haque
					Famiu Haque