diff --git a/lua/avante/config.lua b/lua/avante/config.lua index cddde2b..5026fbd 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -459,8 +459,7 @@ M._defaults = { }, prompt_logger = { -- logs prompts to disk (timestamped, for replay/debugging) enabled = true, -- toggle logging entirely - log_dir = Utils.join_paths(vim.fn.stdpath("cache"), "avante_prompts"), -- directory where logs are saved - fortune_cookie_on_success = false, -- shows a random fortune after each logged prompt (requires `fortune` installed) + log_dir = vim.fn.stdpath("cache"), -- directory where logs are saved next_prompt = { normal = "", -- load the next (newer) prompt log in normal mode insert = "", diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua index 80c03f6..2737de4 100644 --- a/lua/avante/sidebar.lua +++ b/lua/avante/sidebar.lua @@ -2674,6 +2674,7 @@ function Sidebar:create_input_container() self.handle_submit = handle_submit self.containers.input:mount() + PromptLogger.init() local function place_sign_at_first_line(bufnr) local group = "avante_input_prompt_group" @@ -2739,6 +2740,11 @@ function Sidebar:create_input_container() place_sign_at_first_line(self.containers.input.bufnr) end, }) + api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { + group = self.augroup, + buffer = self.containers.input.bufnr, + callback = function() PromptLogger.update_current_input() end, + }) api.nvim_create_autocmd("QuitPre", { group = self.augroup, diff --git a/lua/avante/utils/promptLogger.lua b/lua/avante/utils/promptLogger.lua index 813d448..6008854 100644 --- a/lua/avante/utils/promptLogger.lua +++ b/lua/avante/utils/promptLogger.lua @@ -1,98 +1,92 @@ local Config = require("avante.config") local Utils = require("avante.utils") +-- last one in entries is always to hold current input +local entries, idx = {}, 0 + ---@class avante.utils.promptLogger local M = {} +function M.init() + entries = {} + local dir = Config.prompt_logger.log_dir + local log_file = Utils.join_paths(dir, "avante_prompts.log") + local file = io.open(log_file, "r") + if file then + local content = file:read("*a"):gsub("\n$", "") + file:close() + + local lines = vim.split(content, "\n", { plain = true }) + for _, line in ipairs(lines) do + local ok, entry = pcall(vim.fn.json_decode, line) + if ok and entry and entry.time and entry.input then table.insert(entries, entry) end + end + end + table.insert(entries, { input = "" }) + idx = #entries - 1 +end + function M.log_prompt(request) local log_dir = Config.prompt_logger.log_dir - local log_file = Utils.join_paths(log_dir, "avante_prompt_" .. os.date("%Y%m%d_%H%M%S") .. ".log") + local log_file = Utils.join_paths(log_dir, "avante_prompts.log") if vim.fn.isdirectory(log_dir) == 0 then vim.fn.mkdir(log_dir, "p") end + local entry = { + time = Utils.get_timestamp(), + input = request, + } + + -- Remove any existing entries with the same input + if #entries > 1 then + for i = #entries - 1, 1, -1 do + if entries[i].input == entry.input then table.remove(entries, i) end + end + -- Add the new entry + table.insert(entries, #entries, entry) + idx = #entries - 1 + else + table.insert(entries, entry) + end + local file = io.open(log_file, "w") if file then - file:write(request) - file:close() - if Config.prompt_logger and Config.prompt_logger.fortune_cookie_on_success then - local handle = io.popen("fortune -s -n 100") - if handle then - local fortune_msg = handle:read("*a") - handle:close() - if fortune_msg and #fortune_msg > 0 then vim.notify(fortune_msg, vim.log.levels.INFO, { title = "" }) end - end + -- Write all entries to the log file, except the last one + for i = 1, #entries - 1, 1 do + file:write(vim.fn.json_encode(entries[i]) .. "\n") end + file:close() else vim.notify("Failed to log prompt", vim.log.levels.ERROR) end end --- Cache + helper -local logs, idx = {}, 0 - -local function refresh_logs() - local dir = Config.prompt_logger.log_dir - logs = vim.fn.glob(Utils.join_paths(dir, "avante_prompt_*.log"), false, true) - table.sort(logs, function(a, b) -- newest first - return a > b - end) -end - ----@param step integer 0 = keep | 1 = newer | -1 = older -local function load_log(step) - if #logs == 0 then refresh_logs() end - if #logs == 0 then - vim.notify("No prompt logs found 🤷", vim.log.levels.WARN) - return - end - idx = (idx + step) -- turn wheel - if idx < 1 then idx = #logs end -- wrap around - if idx > #logs then idx = 1 end - - local fp = io.open(logs[idx], "r") - if not fp then - vim.notify("Could not open " .. logs[idx], vim.log.levels.ERROR) - return - end - local content = fp:read("*a") - fp:close() - - local buf = vim.api.nvim_get_current_buf() - vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(content, "\n", { plain = true })) - vim.bo[buf].modifiable = true - vim.b[buf].avante_logpath = logs[idx] -end - -function M.next_log() load_log(1) end - -function M.prev_log() load_log(-1) end - local function _read_log(delta) - if #logs == 0 then refresh_logs() end - if #logs == 0 then return nil end + -- index of array starts from 1 in lua, while this idx starts from 0 + idx = ((idx - delta) % #entries + #entries) % #entries - local target = idx + delta - if target < 1 then target = 1 end - if target > #logs then target = #logs end - - idx = target - - local fp = io.open(logs[idx], "r") - if not fp then return nil end - local txt = fp:read("*a") - fp:close() - return { txt = txt, path = logs[idx] } + return entries[idx + 1] end function M.on_log_retrieve(delta) return function() local res = _read_log(delta) - if not res then - vim.notify("No logs available", vim.log.levels.WARN) + if not res or not res.input then + vim.notify("No log entry found.", vim.log.levels.WARN) return end - vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(res.txt, "\n", { plain = true })) - vim.api.nvim_win_set_cursor(0, { 1, 0 }) + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(res.input, "\n", { plain = true })) + vim.api.nvim_win_set_cursor( + 0, + { vim.api.nvim_buf_line_count(0), #vim.api.nvim_buf_get_lines(0, -2, -1, false)[1] } + ) + end +end + +function M.update_current_input() + if idx == #entries - 1 then + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + entries[#entries].input = table.concat(lines, "\n") end end