From de8fb58bfab54f1494b1ba0089efd2c85e3beb9f Mon Sep 17 00:00:00 2001 From: Jiyeol Lee Date: Mon, 9 Jun 2025 07:59:43 -0400 Subject: [PATCH] feat: remove history command (#2063) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: remove history command * Address code review Co-authored-by: Michael Bøcker-Larsen <247048+mblarsen@users.noreply.github.com> --------- Co-authored-by: Michael Bøcker-Larsen <247048+mblarsen@users.noreply.github.com> --- lua/avante/history_selector.lua | 25 ++++++++++++--- lua/avante/path.lua | 33 +++++++++++++++++-- lua/avante/ui/selector/init.lua | 6 ++++ lua/avante/ui/selector/providers/native.lua | 35 +++++++++++++++++++-- 4 files changed, 89 insertions(+), 10 deletions(-) diff --git a/lua/avante/history_selector.lua b/lua/avante/history_selector.lua index b3d693a..18c6e0a 100644 --- a/lua/avante/history_selector.lua +++ b/lua/avante/history_selector.lua @@ -31,13 +31,15 @@ function M.open(bufnr, cb) end if #selector_items == 0 then - Utils.warn("No models available in config") + Utils.warn("No history items found.") return end - local selector = Selector:new({ - provider = Config.selector.provider, - title = "Select Avante History", + local current_selector -- To be able to close it from the keymap + + current_selector = Selector:new({ + provider = Config.selector.provider, -- This should be 'native' for the current setup + title = "Avante History (Select, then choose action)", -- Updated title items = vim .iter(selector_items) :map( @@ -60,8 +62,21 @@ function M.open(bufnr, cb) local content = Sidebar.render_history_content(history) return content, "markdown" end, + on_delete_item = function(item_id_to_delete) + if not item_id_to_delete then + Utils.warn("No item ID provided for deletion.") + return + end + Path.history.delete(bufnr, item_id_to_delete) -- bufnr from M.open's scope + -- The native provider handles the UI flow; we just need to refresh. + M.open(bufnr, cb) -- Re-open the selector to refresh the list + end, + on_action_cancel = function() + -- If the user cancels the open/delete prompt, re-open the history selector. + M.open(bufnr, cb) + end, }) - selector:open() + current_selector:open() end return M diff --git a/lua/avante/path.lua b/lua/avante/path.lua index 76c46cc..3541e0b 100644 --- a/lua/avante/path.lua +++ b/lua/avante/path.lua @@ -133,7 +133,7 @@ end ---@return avante.ChatHistory function History.load(bufnr, filename) local history_filepath = filename and History.get_filepath(bufnr, filename) - or History.get_latest_filepath(bufnr, false) + or History.get_latest_filepath(bufnr, false) if history_filepath:exists() then local content = history_filepath:read() if content ~= nil then @@ -154,6 +154,35 @@ History.save = function(bufnr, history) History.save_latest_filename(bufnr, history.filename) end +--- Deletes a specific chat history file. +---@param bufnr integer +---@param filename string +function History.delete(bufnr, filename) + local history_filepath = History.get_filepath(bufnr, filename) + if history_filepath:exists() then + local was_latest = (filename == History.get_latest_filename(bufnr, false)) + history_filepath:rm() + + if was_latest then + local remaining_histories = History.list(bufnr) -- This list is sorted by recency + if #remaining_histories > 0 then + History.save_latest_filename(bufnr, remaining_histories[1].filename) + else + -- No histories left, clear the latest_filename from metadata + local metadata_filepath = History.get_metadata_filepath(bufnr) + if metadata_filepath:exists() then + local metadata_content = metadata_filepath:read() + local metadata = vim.json.decode(metadata_content) + metadata.latest_filename = nil -- Or "", depending on desired behavior for an empty latest + metadata_filepath:write(vim.json.encode(metadata), "w") + end + end + end + else + Utils.warn("History file not found: " .. tostring(history_filepath)) + end +end + P.history = History -- Prompt path @@ -210,7 +239,7 @@ function Prompt.get_templates_dir(project_root) end Path:new(debug.getinfo(1).source:match("@?(.*/)"):gsub("/lua/avante/path.lua$", "") .. "templates") - :copy({ destination = cache_prompt_dir, recursive = true }) + :copy({ destination = cache_prompt_dir, recursive = true }) vim.iter(Prompt.custom_prompts_contents):filter(function(_, v) return v ~= nil end):each(function(k, v) local orig_file = cache_prompt_dir:joinpath(Prompt.get_builtin_prompts_filepath(k)) diff --git a/lua/avante/ui/selector/init.lua b/lua/avante/ui/selector/init.lua index e446744..0f269ff 100644 --- a/lua/avante/ui/selector/init.lua +++ b/lua/avante/ui/selector/init.lua @@ -13,6 +13,8 @@ local Utils = require("avante.utils") ---@field provider_opts table | nil ---@field on_select fun(item_ids: string[] | nil) ---@field get_preview_content fun(item_id: string): (string, string) | nil +---@field on_delete_item fun(item_id: string): (nil) | nil +---@field on_action_cancel fun(): (nil) | nil ---@class avante.ui.Selector ---@field provider avante.SelectorProvider @@ -23,6 +25,8 @@ local Utils = require("avante.utils") ---@field on_select fun(item_ids: string[] | nil) ---@field selected_item_ids string[] | nil ---@field get_preview_content fun(item_id: string): (string, string) | nil +---@field on_delete_item fun(item_id: string): (nil) | nil +---@field on_action_cancel fun(): (nil) | nil local Selector = {} Selector.__index = Selector @@ -45,6 +49,8 @@ function Selector:new(opts) o.on_select = opts.on_select o.selected_item_ids = opts.selected_item_ids or {} o.get_preview_content = opts.get_preview_content + o.on_delete_item = opts.on_delete_item + o.on_action_cancel = opts.on_action_cancel return o end diff --git a/lua/avante/ui/selector/providers/native.lua b/lua/avante/ui/selector/providers/native.lua index 7cbb86f..9ef3d42 100644 --- a/lua/avante/ui/selector/providers/native.lua +++ b/lua/avante/ui/selector/providers/native.lua @@ -10,10 +10,39 @@ function M.show(selector) prompt = selector.title, format_item = function(item) return item.title end, }, function(item) - if item then - selector.on_select({ item.id }) - else + if not item then selector.on_select(nil) + return + end + + -- If on_delete_item callback is provided, prompt for action + if type(selector.on_delete_item) == "function" then + vim.ui.input( + { prompt = "Action for '" .. item.title .. "': (o)pen, (d)elete, (c)ancel?", default = "" }, + function(input) + if not input then -- User cancelled input + selector.on_select(nil) -- Treat as cancellation of selection + return + end + local choice = input:lower() + if choice == "d" or choice == "delete" then + selector.on_delete_item(item.id) + elseif choice == "" or choice == "o" or choice == "open" then + selector.on_select({ item.id }) + elseif choice == "c" or choice == "cancel" then + if type(selector.on_action_cancel) == "function" then + selector.on_action_cancel() + else + selector.on_select(nil) -- Fallback if on_action_cancel is not defined + end + else -- c or any other input, treat as cancel + selector.on_select(nil) -- Fallback if on_action_cancel is not defined + end + end + ) + else + -- Default behavior: directly select the item + selector.on_select({ item.id }) end end) end