optimize: make relative (#1529)
This commit is contained in:
@@ -18,6 +18,7 @@ local FileSelector = {}
|
||||
local function has_scheme(path) return path:find("^%w+://") ~= nil end
|
||||
|
||||
function FileSelector:process_directory(absolute_path, project_root)
|
||||
if absolute_path:sub(-1) == Utils.path_sep then absolute_path = absolute_path:sub(1, -2) end
|
||||
local files = scan.scan_dir(absolute_path, {
|
||||
hidden = false,
|
||||
depth = math.huge,
|
||||
@@ -26,7 +27,7 @@ function FileSelector:process_directory(absolute_path, project_root)
|
||||
})
|
||||
|
||||
for _, file in ipairs(files) do
|
||||
local rel_path = Path:new(file):make_relative(project_root)
|
||||
local rel_path = Utils.make_relative_path(file, project_root)
|
||||
if not vim.tbl_contains(self.selected_filepaths, rel_path) then table.insert(self.selected_filepaths, rel_path) end
|
||||
end
|
||||
self:emit("update")
|
||||
@@ -61,10 +62,10 @@ end
|
||||
local function get_project_filepaths()
|
||||
local project_root = Utils.get_project_root()
|
||||
local files = Utils.scan_directory({ directory = project_root, add_dirs = true })
|
||||
files = vim.iter(files):map(function(filepath) return Path:new(filepath):make_relative(project_root) end):totable()
|
||||
files = vim.iter(files):map(function(filepath) return Utils.make_relative_path(filepath, project_root) end):totable()
|
||||
|
||||
return vim.tbl_map(function(path)
|
||||
local rel_path = Path:new(path):make_relative(project_root)
|
||||
local rel_path = Utils.make_relative_path(path, project_root)
|
||||
local stat = vim.loop.fs_stat(path)
|
||||
if stat and stat.type == "directory" then rel_path = rel_path .. "/" end
|
||||
return rel_path
|
||||
|
||||
@@ -34,7 +34,7 @@ local function has_permission_to_access(abs_path)
|
||||
-- Specifically, it should not check the project root itself
|
||||
-- Otherwise if the binary is named the same as the project root (such as Go binary), any paths
|
||||
-- insde the project root will be ignored
|
||||
local rel_path = Path:new(abs_path):make_relative(project_root)
|
||||
local rel_path = Utils.make_relative_path(abs_path, project_root)
|
||||
return not Utils.is_ignored(rel_path, gitignore_patterns, gitignore_negate_patterns)
|
||||
end
|
||||
|
||||
|
||||
@@ -35,6 +35,14 @@ end
|
||||
|
||||
function M.is_win() return jit.os:find("Windows") ~= nil end
|
||||
|
||||
M.path_sep = (function()
|
||||
if M.is_win() then
|
||||
return "\\"
|
||||
else
|
||||
return "/"
|
||||
end
|
||||
end)()
|
||||
|
||||
---@return "linux" | "darwin" | "windows"
|
||||
function M.get_os_name()
|
||||
local os_name = vim.uv.os_uname().sysname
|
||||
@@ -765,7 +773,7 @@ function M.scan_directory(options)
|
||||
:filter(function(file)
|
||||
local base_dir = options.directory
|
||||
if base_dir:sub(-2) == "/." then base_dir = base_dir:sub(1, -3) end
|
||||
local rel_path = tostring(Path:new(file):make_relative(base_dir))
|
||||
local rel_path = M.make_relative_path(file, base_dir)
|
||||
local pieces = vim.split(rel_path, "/")
|
||||
return #pieces <= options.max_depth
|
||||
end)
|
||||
@@ -776,8 +784,7 @@ function M.scan_directory(options)
|
||||
local dirs = {}
|
||||
local dirs_seen = {}
|
||||
for _, file in ipairs(files) do
|
||||
local dir = tostring(Path:new(file):parent())
|
||||
dir = dir .. "/"
|
||||
local dir = M.get_parent_path(file)
|
||||
if not dirs_seen[dir] then
|
||||
table.insert(dirs, dir)
|
||||
dirs_seen[dir] = true
|
||||
@@ -789,6 +796,64 @@ function M.scan_directory(options)
|
||||
return files
|
||||
end
|
||||
|
||||
function M.get_parent_path(filepath)
|
||||
if filepath == nil then error("filepath cannot be nil") end
|
||||
if filepath == "" then return "" end
|
||||
local is_abs = M.is_absolute_path(filepath)
|
||||
if filepath:sub(-1) == M.path_sep then filepath = filepath:sub(1, -2) end
|
||||
if filepath == "" then return "" end
|
||||
local parts = vim.split(filepath, M.path_sep)
|
||||
local parent_parts = vim.list_slice(parts, 1, #parts - 1)
|
||||
local res = table.concat(parent_parts, M.path_sep)
|
||||
if res == "" then
|
||||
if is_abs then return M.path_sep end
|
||||
return "."
|
||||
end
|
||||
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.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.join_paths(...)
|
||||
local paths = { ... }
|
||||
local result = paths[1] or ""
|
||||
for i = 2, #paths do
|
||||
local path = paths[i]
|
||||
if path == nil or path == "" then goto continue end
|
||||
|
||||
if M.is_absolute_path(path) then
|
||||
result = path
|
||||
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
|
||||
::continue::
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function M.is_first_letter_uppercase(str) return string.match(str, "^[A-Z]") ~= nil end
|
||||
|
||||
---@param content string
|
||||
@@ -954,8 +1019,8 @@ function M.uniform_path(path)
|
||||
if type(path) ~= "string" then path = tostring(path) end
|
||||
if not M.file.is_in_cwd(path) then return path end
|
||||
local project_root = M.get_project_root()
|
||||
local abs_path = Path:new(path):is_absolute() and path or Path:new(project_root):joinpath(path):absolute()
|
||||
local relative_path = Path:new(abs_path):make_relative(project_root)
|
||||
local abs_path = M.is_absolute_path(path) and path or M.join_paths(project_root, path)
|
||||
local relative_path = M.make_relative_path(abs_path, project_root)
|
||||
return relative_path
|
||||
end
|
||||
|
||||
|
||||
81
tests/utils/get_parent_path_spec.lua
Normal file
81
tests/utils/get_parent_path_spec.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
local utils = require("avante.utils")
|
||||
|
||||
describe("get_parent_path", function()
|
||||
-- Define path separator for our tests, using the same logic as in the utils module
|
||||
local path_sep = jit.os:find("Windows") ~= nil and "\\" or "/"
|
||||
|
||||
it("should return the parent directory of a file path", function()
|
||||
local filepath = "foo" .. path_sep .. "bar" .. path_sep .. "baz.txt"
|
||||
local expected = "foo" .. path_sep .. "bar"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
end)
|
||||
|
||||
it("should return the parent directory of a directory path", function()
|
||||
local dirpath = "foo" .. path_sep .. "bar" .. path_sep .. "baz"
|
||||
local expected = "foo" .. path_sep .. "bar"
|
||||
assert.are.equal(expected, utils.get_parent_path(dirpath))
|
||||
end)
|
||||
|
||||
it("should handle trailing separators", function()
|
||||
local dirpath = "foo" .. path_sep .. "bar" .. path_sep .. "baz" .. path_sep
|
||||
local expected = "foo" .. path_sep .. "bar"
|
||||
assert.are.equal(expected, utils.get_parent_path(dirpath))
|
||||
end)
|
||||
|
||||
it("should return '.' for a single file or directory", function()
|
||||
assert.are.equal(".", utils.get_parent_path("foo.txt"))
|
||||
assert.are.equal(".", utils.get_parent_path("dir"))
|
||||
end)
|
||||
|
||||
it("should handle paths with multiple levels", function()
|
||||
local filepath = "a" .. path_sep .. "b" .. path_sep .. "c" .. path_sep .. "d" .. path_sep .. "file.txt"
|
||||
local expected = "a" .. path_sep .. "b" .. path_sep .. "c" .. path_sep .. "d"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
end)
|
||||
|
||||
it("should return empty string for root directory", function()
|
||||
-- Root directory on Unix-like systems
|
||||
if path_sep == "/" then
|
||||
assert.are.equal("/", utils.get_parent_path("/foo"))
|
||||
else
|
||||
-- Windows uses drive letters, so parent of "C:\foo" is "C:"
|
||||
local winpath = "C:" .. path_sep .. "foo"
|
||||
assert.are.equal("C:", utils.get_parent_path(winpath))
|
||||
end
|
||||
end)
|
||||
|
||||
it("should return empty string for an empty string", function() assert.are.equal("", utils.get_parent_path("")) end)
|
||||
|
||||
it("should throw an error for nil input", function()
|
||||
assert.has_error(function() utils.get_parent_path(nil) end, "filepath cannot be nil")
|
||||
end)
|
||||
|
||||
it("should handle paths with spaces", function()
|
||||
local filepath = "path with spaces" .. path_sep .. "file name.txt"
|
||||
local expected = "path with spaces"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
end)
|
||||
|
||||
it("should handle special characters in paths", function()
|
||||
local filepath = "folder-name!" .. path_sep .. "file_#$%&.txt"
|
||||
local expected = "folder-name!"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
end)
|
||||
|
||||
it("should handle absolute paths", function()
|
||||
if path_sep == "/" then
|
||||
-- Unix-like paths
|
||||
local filepath = path_sep .. "home" .. path_sep .. "user" .. path_sep .. "file.txt"
|
||||
local expected = path_sep .. "home" .. path_sep .. "user"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
|
||||
-- Root directory edge case
|
||||
assert.are.equal("", utils.get_parent_path(path_sep))
|
||||
else
|
||||
-- Windows paths
|
||||
local filepath = "C:" .. path_sep .. "Users" .. path_sep .. "user" .. path_sep .. "file.txt"
|
||||
local expected = "C:" .. path_sep .. "Users" .. path_sep .. "user"
|
||||
assert.are.equal(expected, utils.get_parent_path(filepath))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
54
tests/utils/join_paths_spec.lua
Normal file
54
tests/utils/join_paths_spec.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
local assert = require("luassert")
|
||||
local utils = require("avante.utils")
|
||||
|
||||
describe("join_paths", function()
|
||||
it("should join multiple path segments with proper separator", function()
|
||||
local result = utils.join_paths("path", "to", "file.lua")
|
||||
assert.equals("path" .. utils.path_sep .. "to" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle empty path segments", function()
|
||||
local result = utils.join_paths("", "to", "file.lua")
|
||||
assert.equals("to" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle nil path segments", function()
|
||||
local result = utils.join_paths(nil, "to", "file.lua")
|
||||
assert.equals("to" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle empty path segments", function()
|
||||
local result = utils.join_paths("path", "", "file.lua")
|
||||
assert.equals("path" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should use absolute path when encountered", function()
|
||||
local absolute_path = utils.is_win() and "C:\\absolute\\path" or "/absolute/path"
|
||||
local result = utils.join_paths("relative", "path", absolute_path)
|
||||
assert.equals(absolute_path, result)
|
||||
end)
|
||||
|
||||
it("should handle paths with trailing separators", function()
|
||||
local path_with_sep = "path" .. utils.path_sep
|
||||
local result = utils.join_paths(path_with_sep, "file.lua")
|
||||
assert.equals("path" .. utils.path_sep .. "file.lua", result)
|
||||
end)
|
||||
|
||||
it("should return empty string when no paths provided", function()
|
||||
local result = utils.join_paths()
|
||||
assert.equals("", result)
|
||||
end)
|
||||
|
||||
it("should return first path when only one path provided", function()
|
||||
local result = utils.join_paths("path")
|
||||
assert.equals("path", result)
|
||||
end)
|
||||
|
||||
it("should handle path with mixed separators", function()
|
||||
-- This test is more relevant on Windows where both / and \ are valid separators
|
||||
local mixed_path = utils.is_win() and "path\\to/file" or "path/to/file"
|
||||
local result = utils.join_paths("base", mixed_path)
|
||||
-- The function should use utils.path_sep for joining
|
||||
assert.equals("base" .. utils.path_sep .. mixed_path, result)
|
||||
end)
|
||||
end)
|
||||
58
tests/utils/make_relative_path_spec.lua
Normal file
58
tests/utils/make_relative_path_spec.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
local assert = require("luassert")
|
||||
local utils = require("avante.utils")
|
||||
|
||||
describe("make_relative_path", function()
|
||||
it("should remove base directory from filepath", function()
|
||||
local test_filepath = "/path/to/project/src/file.lua"
|
||||
local test_base_dir = "/path/to/project"
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("src/file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle trailing dot-slash in base_dir", function()
|
||||
local test_filepath = "/path/to/project/src/file.lua"
|
||||
local test_base_dir = "/path/to/project/."
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("src/file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle trailing dot-slash in filepath", function()
|
||||
local test_filepath = "/path/to/project/src/."
|
||||
local test_base_dir = "/path/to/project"
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("src", result)
|
||||
end)
|
||||
|
||||
it("should handle both having trailing dot-slash", function()
|
||||
local test_filepath = "/path/to/project/src/."
|
||||
local test_base_dir = "/path/to/project/."
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("src", result)
|
||||
end)
|
||||
|
||||
it("should return the filepath when base_dir is not a prefix", function()
|
||||
local test_filepath = "/path/to/project/src/file.lua"
|
||||
local test_base_dir = "/different/path"
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("/path/to/project/src/file.lua", result)
|
||||
end)
|
||||
|
||||
it("should handle identical paths", function()
|
||||
local test_filepath = "/path/to/project"
|
||||
local test_base_dir = "/path/to/project"
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals(".", result)
|
||||
end)
|
||||
|
||||
it("should handle empty strings", function()
|
||||
local result = utils.make_relative_path("", "")
|
||||
assert.equals(".", result)
|
||||
end)
|
||||
|
||||
it("should preserve trailing slash in filepath", function()
|
||||
local test_filepath = "/path/to/project/src/"
|
||||
local test_base_dir = "/path/to/project"
|
||||
local result = utils.make_relative_path(test_filepath, test_base_dir)
|
||||
assert.equals("src/", result)
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user