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