158 lines
4.5 KiB
Lua
158 lines
4.5 KiB
Lua
local M = {}
|
|
|
|
local uv = vim.loop
|
|
|
|
local function load_gitignore_patterns(root, conf)
|
|
local gitignore_path = root .. "/.gitignore"
|
|
local fd = uv.fs_open(gitignore_path, "r", 438)
|
|
if not fd then
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] No .gitignore found.\n")
|
|
end
|
|
return {}
|
|
end
|
|
local stat = uv.fs_fstat(fd)
|
|
local data = uv.fs_read(fd, stat.size, 0)
|
|
uv.fs_close(fd)
|
|
if not data then return {} end
|
|
local patterns = {}
|
|
for line in data:gmatch("[^\r\n]+") do
|
|
line = line:match("^%s*(.-)%s*$")
|
|
if line ~= "" and not line:match("^#") then
|
|
patterns[#patterns+1] = line
|
|
end
|
|
end
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Loaded " .. #patterns .. " gitignore patterns.\n")
|
|
end
|
|
return patterns
|
|
end
|
|
|
|
local function should_ignore_file(file, ignore_patterns, conf)
|
|
for _, pattern in ipairs(ignore_patterns) do
|
|
if file:find(pattern, 1, true) then
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Ignoring file/dir: " .. file .. " (matched pattern: " .. pattern .. ")\n")
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function is_text_file(file, conf)
|
|
local fd = uv.fs_open(file, "r", 438)
|
|
if not fd then
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Could not open file: " .. file .. " for reading.\n")
|
|
end
|
|
return false
|
|
end
|
|
local chunk = uv.fs_read(fd, 1024, 0) or ""
|
|
uv.fs_close(fd)
|
|
if chunk:find("\0") then
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] File appears binary: " .. file .. "\n")
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function scandir(dir, ignore_patterns, files, conf)
|
|
local fd = uv.fs_opendir(dir, nil, 50)
|
|
if not fd then
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Could not open dir: " .. dir .. "\n")
|
|
end
|
|
return files
|
|
end
|
|
while true do
|
|
local ents = uv.fs_readdir(fd)
|
|
if not ents then break end
|
|
for _, ent in ipairs(ents) do
|
|
local fullpath = dir .. "/" .. ent.name
|
|
if not should_ignore_file(fullpath, ignore_patterns, conf) then
|
|
if ent.type == "file" and is_text_file(fullpath, conf) then
|
|
table.insert(files, fullpath)
|
|
elseif ent.type == "directory" and ent.name ~= ".git" then
|
|
scandir(fullpath, ignore_patterns, files, conf)
|
|
elseif ent.type == "link" then
|
|
local link_target = uv.fs_readlink(fullpath)
|
|
if link_target then
|
|
local st = uv.fs_stat(link_target)
|
|
if st and st.type == "directory" then
|
|
table.insert(files, fullpath .. " (symlink to directory " .. link_target .. ")")
|
|
else
|
|
table.insert(files, fullpath .. " (symlink to file " .. link_target .. ")")
|
|
end
|
|
else
|
|
table.insert(files, fullpath .. " (symlink)")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
uv.fs_closedir(fd)
|
|
return files
|
|
end
|
|
|
|
function M.get_project_files(directories, conf)
|
|
local root = vim.fn.getcwd()
|
|
local ignore_patterns = load_gitignore_patterns(root, conf)
|
|
local all_files = {}
|
|
for _, dir in ipairs(directories) do
|
|
local abs_dir = dir
|
|
if not abs_dir:match("^/") then
|
|
abs_dir = root .. "/" .. dir
|
|
end
|
|
scandir(abs_dir, ignore_patterns, all_files, conf)
|
|
end
|
|
|
|
local rel_files = {}
|
|
for _, f in ipairs(all_files) do
|
|
local rel = vim.fn.fnamemodify(f, ":.")
|
|
table.insert(rel_files, rel)
|
|
end
|
|
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Found " .. #rel_files .. " project files.\n")
|
|
end
|
|
|
|
return rel_files
|
|
end
|
|
|
|
function M.get_project_structure(directories, conf)
|
|
local files = M.get_project_files(directories, conf)
|
|
local structure = "Files:\n" .. table.concat(files, "\n")
|
|
return structure
|
|
end
|
|
|
|
function M.get_file_contents(files, conf)
|
|
local root = vim.fn.getcwd()
|
|
local sections = {}
|
|
for _, f in ipairs(files) do
|
|
local path = root .. "/" .. f
|
|
local fd = uv.fs_open(path, "r", 438)
|
|
if fd then
|
|
local stat = uv.fs_fstat(fd)
|
|
if stat then
|
|
local data = uv.fs_read(fd, stat.size, 0)
|
|
uv.fs_close(fd)
|
|
if data then
|
|
table.insert(sections, "\nFile: `" .. f .. "`\n```\n" .. data .. "\n```\n")
|
|
end
|
|
else
|
|
uv.fs_close(fd)
|
|
end
|
|
else
|
|
if conf.debug then
|
|
vim.api.nvim_out_write("[chatgpt_nvim:context] Could not open file for content: " .. f .. "\n")
|
|
end
|
|
end
|
|
end
|
|
return table.concat(sections, "\n")
|
|
end
|
|
|
|
return M
|