avante.nvim/lua/avante/file_selector.lua

147 lines
3.8 KiB
Lua
Raw Normal View History

feat(context): add a ui for selecting and adding files to the sidebar as context (#912) * feat(sidebar): supports select files chore (context) update add type annotations to context functions chore (sidebar) remove unused notify function call refactor (sidebar) remove setting search file to file path chore (sidebar) remove nvim_notify debugging api call * feat (files) allow selecting a file by string via cmp suggestion menu * chore (context) refactor to allow context using @file with a context view * refactor (context) refactor seletected file types as an array of path and content * refactor (config) remove unused configuration options * refactor (sidebar) remove unused unbild key * refactor (context) remove unused imports * refactor (mentions) update mentions to support items with callback functions and removal of the underlying selection. * fix (sidebar) add file context as a window that is visitable via the tab key * refactor (file_content) remove file content as an input to llm * feat (sidebar) support suggesting and applying code in all languages that are in the context * feat (sidebar) configurable mapping for removing a file from the context. * feat (context_view) configure hints for the context view for adding and deleting a file. * feat (context) add hints for the context view. * fix (sidebar) type when scrolling the results buffer. * refactor (selected files) refactor llm stream to accept an array of selected file metadata * refactor: context => selected_files --------- Co-authored-by: yetone <yetoneful@gmail.com>
2024-12-12 03:29:10 +10:00
local Utils = require("avante.utils")
local Path = require("plenary.path")
local scan = require("plenary.scandir")
--- @class FileSelector
local FileSelector = {}
--- @class FileSelector
--- @field id integer
--- @field selected_filepaths string[]
--- @field file_cache string[]
--- @field event_handlers table<string, function[]>
---@param id integer
---@return FileSelector
function FileSelector:new(id)
return setmetatable({
id = id,
selected_files = {},
file_cache = {},
event_handlers = {},
}, { __index = self })
end
function FileSelector:reset()
self.selected_filepaths = {}
self.event_handlers = {}
end
function FileSelector:add_selected_file(filepath) table.insert(self.selected_filepaths, Utils.uniform_path(filepath)) end
function FileSelector:on(event, callback)
local handlers = self.event_handlers[event]
if not handlers then
handlers = {}
self.event_handlers[event] = handlers
end
table.insert(handlers, callback)
end
function FileSelector:emit(event, ...)
local handlers = self.event_handlers[event]
if not handlers then return end
for _, handler in ipairs(handlers) do
handler(...)
end
end
function FileSelector:off(event, callback)
if not callback then
self.event_handlers[event] = {}
return
end
local handlers = self.event_handlers[event]
if not handlers then return end
for i, handler in ipairs(handlers) do
if handler == callback then
table.remove(handlers, i)
break
end
end
end
---@return nil
function FileSelector:open()
self:update_file_cache()
self:show_select_ui()
end
---@return nil
function FileSelector:update_file_cache()
local project_root = Path:new(Utils.get_project_root()):absolute()
local filepaths = scan.scan_dir(project_root, {
respect_gitignore = true,
})
-- Sort buffer names alphabetically
table.sort(filepaths, function(a, b) return a < b end)
self.file_cache = vim
.iter(filepaths)
:map(function(filepath) return Path:new(filepath):make_relative(project_root) end)
:totable()
end
---@return nil
function FileSelector:show_select_ui()
vim.schedule(function()
local filepaths = vim
.iter(self.file_cache)
:filter(function(filepath) return not vim.tbl_contains(self.selected_filepaths, filepath) end)
:totable()
vim.ui.select(filepaths, {
prompt = "(Avante) Add a file:",
format_item = function(item) return item end,
}, function(filepath)
if not filepath then return end
table.insert(self.selected_filepaths, Utils.uniform_path(filepath))
self:emit("update")
end)
end)
-- unlist the current buffer as vim.ui.select will be listed
local winid = vim.api.nvim_get_current_win()
local bufnr = vim.api.nvim_win_get_buf(winid)
vim.api.nvim_set_option_value("buflisted", false, { buf = bufnr })
vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = bufnr })
end
---@param idx integer
---@return boolean
function FileSelector:remove_selected_filepaths(idx)
if idx > 0 and idx <= #self.selected_filepaths then
table.remove(self.selected_filepaths, idx)
self:emit("update")
return true
end
return false
end
---@return { path: string, content: string, file_type: string }[]
function FileSelector:get_selected_files_contents()
local contents = {}
for _, file_path in ipairs(self.selected_filepaths) do
local file = io.open(file_path, "r")
if file then
local content = file:read("*all")
file:close()
-- Detect the file type
local filetype = vim.filetype.match({ filename = file_path, contents = contents }) or "unknown"
table.insert(contents, { path = file_path, content = content, file_type = filetype })
end
end
return contents
end
function FileSelector:get_selected_filepaths() return vim.deepcopy(self.selected_filepaths) end
return FileSelector