* fix(#2094): fix the path resolving in windows * fix test case * tweak test case
This commit is contained in:
@@ -6,6 +6,7 @@ local lsp = vim.lsp
|
||||
---@field tokens avante.utils.tokens
|
||||
---@field root avante.utils.root
|
||||
---@field file avante.utils.file
|
||||
---@field path avante.utils.path
|
||||
---@field environment avante.utils.environment
|
||||
---@field lsp avante.utils.lsp
|
||||
local M = {}
|
||||
@@ -32,20 +33,9 @@ function M.has(plugin)
|
||||
return res
|
||||
end
|
||||
|
||||
local _is_win = nil
|
||||
function M.is_win() return M.path.is_win() end
|
||||
|
||||
function M.is_win()
|
||||
if _is_win == nil then _is_win = jit.os:find("Windows") ~= nil end
|
||||
return _is_win
|
||||
end
|
||||
|
||||
M.path_sep = (function()
|
||||
if M.is_win() then
|
||||
return "\\"
|
||||
else
|
||||
return "/"
|
||||
end
|
||||
end)()
|
||||
M.path_sep = M.path.SEP
|
||||
|
||||
---@return "linux" | "darwin" | "windows"
|
||||
function M.get_os_name()
|
||||
@@ -340,7 +330,7 @@ end
|
||||
|
||||
---@param path string
|
||||
---@return string
|
||||
function M.norm(path) return vim.fs.normalize(path) end
|
||||
function M.norm(path) return M.path.normalize(path) end
|
||||
|
||||
---@param msg string|string[]
|
||||
---@param opts? LazyNotifyOpts
|
||||
@@ -900,26 +890,9 @@ function M.get_parent_path(filepath)
|
||||
return res
|
||||
end
|
||||
|
||||
function M.make_relative_path(filepath, base_dir)
|
||||
if filepath:sub(-2) == M.path_sep .. "." then filepath = filepath:sub(1, -3) end
|
||||
if base_dir:sub(-2) == M.path_sep .. "." then base_dir = base_dir:sub(1, -3) end
|
||||
if filepath == base_dir then return "." end
|
||||
if filepath:sub(1, #base_dir) == base_dir then
|
||||
filepath = filepath:sub(#base_dir + 1)
|
||||
if filepath:sub(1, 2) == "." .. M.path_sep then
|
||||
filepath = filepath:sub(3)
|
||||
elseif filepath:sub(1, 1) == M.path_sep then
|
||||
filepath = filepath:sub(2)
|
||||
end
|
||||
end
|
||||
return filepath
|
||||
end
|
||||
function M.make_relative_path(filepath, base_dir) return M.path.relative(base_dir, filepath, false) end
|
||||
|
||||
function M.is_absolute_path(path)
|
||||
if not path then return false end
|
||||
if M.is_win() then return path:match("^%a:[/\\]") ~= nil end
|
||||
return path:match("^/") ~= nil
|
||||
end
|
||||
function M.is_absolute_path(path) return M.path.is_absolute(path) end
|
||||
|
||||
function M.to_absolute_path(path)
|
||||
if not path or path == "" then return path end
|
||||
@@ -939,16 +912,13 @@ function M.join_paths(...)
|
||||
goto continue
|
||||
end
|
||||
|
||||
if path:sub(1, 2) == "." .. M.path_sep then path = path:sub(3) end
|
||||
|
||||
if result ~= "" and result:sub(-1) ~= M.path_sep then result = result .. M.path_sep end
|
||||
result = result .. path
|
||||
result = result == "" and path or M.path.join(result, path)
|
||||
::continue::
|
||||
end
|
||||
return M.norm(result)
|
||||
end
|
||||
|
||||
function M.path_exists(path) return vim.loop.fs_stat(path) ~= nil end
|
||||
function M.path_exists(path) return M.path.is_exist(path) end
|
||||
|
||||
function M.is_first_letter_uppercase(str) return string.match(str, "^[A-Z]") ~= nil end
|
||||
|
||||
|
||||
174
lua/avante/utils/path.lua
Normal file
174
lua/avante/utils/path.lua
Normal file
@@ -0,0 +1,174 @@
|
||||
local OS_NAME = vim.uv.os_uname().sysname ---@type string|nil
|
||||
local IS_WIN = OS_NAME == "Windows_NT" ---@type boolean
|
||||
|
||||
local SEP = IS_WIN and "\\" or "/" ---@type string
|
||||
|
||||
local BYTE_SLASH = 0x2f ---@type integer '/'
|
||||
local BYTE_BACKSLASH = 0x5c ---@type integer '\\'
|
||||
local BYTE_COLON = 0x3a ---@type integer ':'
|
||||
local BYTE_PATHSEP = string.byte(SEP) ---@type integer
|
||||
|
||||
---@class avante.utils.path
|
||||
local M = {}
|
||||
|
||||
M.SEP = SEP
|
||||
|
||||
---@return boolean
|
||||
function M.is_win() return IS_WIN end
|
||||
|
||||
---@param filepath string
|
||||
---@return string
|
||||
function M.basename(filepath)
|
||||
if filepath == "" then return "" end
|
||||
|
||||
local pos_invalid = #filepath + 1 ---@type integer
|
||||
local pos_sep = 0 ---@type integer
|
||||
|
||||
for i = #filepath, 1, -1 do
|
||||
local byte = string.byte(filepath, i, i) ---@type integer
|
||||
if byte == BYTE_SLASH or byte == BYTE_BACKSLASH then
|
||||
if i + 1 == pos_invalid then
|
||||
pos_invalid = i
|
||||
else
|
||||
pos_sep = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if pos_sep == 0 and pos_invalid == #filepath + 1 then return filepath end
|
||||
return string.sub(filepath, pos_sep + 1, pos_invalid - 1)
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return string
|
||||
function M.dirname(filepath)
|
||||
local pieces = M.split(filepath)
|
||||
if #pieces == 1 then
|
||||
local piece = pieces[1] ---@type string
|
||||
return piece == "" and string.byte(filepath, 1, 1) == BYTE_SLASH and "/" or piece
|
||||
end
|
||||
local dirpath = #pieces > 0 and table.concat(pieces, SEP, 1, #pieces - 1) or "" ---@type string
|
||||
return dirpath == "" and string.byte(filepath, 1, 1) == BYTE_SLASH and "/" or dirpath
|
||||
end
|
||||
|
||||
---@param filename string
|
||||
---@return string
|
||||
function M.extname(filename) return filename:match("%.[^.]+$") or "" end
|
||||
|
||||
---@param filepath string
|
||||
---@return boolean
|
||||
function M.is_absolute(filepath)
|
||||
if IS_WIN then return #filepath > 1 and string.byte(filepath, 2, 2) == BYTE_COLON end
|
||||
return string.byte(filepath, 1, 1) == BYTE_PATHSEP
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return boolean
|
||||
function M.is_exist(filepath)
|
||||
local stat = vim.uv.fs_stat(filepath)
|
||||
return stat ~= nil and not vim.tbl_isempty(stat)
|
||||
end
|
||||
|
||||
---@param dirpath string
|
||||
---@return boolean
|
||||
function M.is_exist_dirpath(dirpath)
|
||||
local stat = vim.uv.fs_stat(dirpath)
|
||||
return stat ~= nil and stat.type == "directory"
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return boolean
|
||||
function M.is_exist_filepath(filepath)
|
||||
local stat = vim.uv.fs_stat(filepath)
|
||||
return stat ~= nil and stat.type == "file"
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
---@return string
|
||||
function M.join(from, to) return M.normalize(from .. SEP .. to) end
|
||||
|
||||
function M.mkdir_if_nonexist(dirpath)
|
||||
if not M.is_exist(dirpath) then vim.fn.mkdir(dirpath, "p") end
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return string
|
||||
function M.normalize(filepath)
|
||||
if filepath == "/" and not IS_WIN then return "/" end
|
||||
|
||||
if filepath == "" then return "." end
|
||||
|
||||
filepath = filepath:gsub("%%(%x%x)", function(hex) return string.char(tonumber(hex, 16)) end)
|
||||
return table.concat(M.split(filepath), SEP)
|
||||
end
|
||||
|
||||
---@param from string
|
||||
---@param to string
|
||||
---@param prefer_slash boolean
|
||||
---@return string
|
||||
function M.relative(from, to, prefer_slash)
|
||||
local is_from_absolute = M.is_absolute(from) ---@type boolean
|
||||
local is_to_absolute = M.is_absolute(to) ---@type boolean
|
||||
|
||||
if is_from_absolute and not is_to_absolute then return M.normalize(to) end
|
||||
|
||||
if is_to_absolute and not is_from_absolute then return M.normalize(to) end
|
||||
|
||||
local from_pieces = M.split(from) ---@type string[]
|
||||
local to_pieces = M.split(to) ---@type string[]
|
||||
local L = #from_pieces < #to_pieces and #from_pieces or #to_pieces
|
||||
|
||||
local i = 1
|
||||
while i <= L do
|
||||
if from_pieces[i] ~= to_pieces[i] then break end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if i == 2 and is_to_absolute then return M.normalize(to) end
|
||||
|
||||
local sep = prefer_slash and "/" or SEP
|
||||
local p = "" ---@type string
|
||||
for _ = i, #from_pieces do
|
||||
p = p .. sep .. ".." ---@type string
|
||||
end
|
||||
for j = i, #to_pieces do
|
||||
p = p .. sep .. to_pieces[j] ---@type string
|
||||
end
|
||||
|
||||
if p == "" then return "." end
|
||||
return #p > 1 and string.sub(p, 2) or p
|
||||
end
|
||||
|
||||
---@param cwd string
|
||||
---@param to string
|
||||
function M.resolve(cwd, to) return M.is_absolute(to) and M.normalize(to) or M.normalize(cwd .. SEP .. to) end
|
||||
|
||||
---@param filepath string
|
||||
---@return string[]
|
||||
function M.split(filepath)
|
||||
local pieces = {} ---@type string[]
|
||||
local pattern = "([^/\\]+)" ---@type string
|
||||
local has_sep_prefix = SEP == "/" and string.byte(filepath, 1, 1) == BYTE_PATHSEP ---@type boolean
|
||||
local has_sep_suffix = #filepath > 1 and string.byte(filepath, #filepath, #filepath) == BYTE_PATHSEP ---@type boolean
|
||||
|
||||
if has_sep_prefix then pieces[1] = "" end
|
||||
|
||||
for piece in string.gmatch(filepath, pattern) do
|
||||
if piece ~= "" and piece ~= "." then
|
||||
if piece == ".." and (has_sep_prefix or #pieces > 0) then
|
||||
pieces[#pieces] = nil
|
||||
else
|
||||
pieces[#pieces + 1] = piece
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if has_sep_suffix then pieces[#pieces + 1] = "" end
|
||||
|
||||
if IS_WIN and #filepath > 1 and string.byte(filepath, 2, 2) == BYTE_COLON then pieces[1] = pieces[1]:upper() end
|
||||
return pieces
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -34,9 +34,9 @@ describe("join_paths", function()
|
||||
assert.equals("path" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should return empty string when no paths provided", function()
|
||||
it("should handle no paths provided", function()
|
||||
local result = utils.join_paths()
|
||||
assert.equals("", result)
|
||||
assert.equals(".", result)
|
||||
end)
|
||||
|
||||
it("should return first path when only one path provided", function()
|
||||
|
||||
Reference in New Issue
Block a user