From e7113f183471eb5f77755b98556c0bd82fb32373 Mon Sep 17 00:00:00 2001 From: yetone Date: Tue, 24 Jun 2025 02:06:50 +0800 Subject: [PATCH] optimize: use cache to optimize the performance of streaming rendering (#2312) --- lua/avante/sidebar.lua | 32 +++++++++++++++++++++++++++++--- lua/avante/utils/init.lua | 28 +++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua index 12fde4e..6023da2 100644 --- a/lua/avante/sidebar.lua +++ b/lua/avante/sidebar.lua @@ -18,6 +18,7 @@ local FileSelector = require("avante.file_selector") local LLMTools = require("avante.llm_tools") local HistoryMessage = require("avante.history_message") local Line = require("avante.ui.line") +local LRUCache = require("avante.utils.lru_cache") local RESULT_BUF_NAME = "AVANTE_RESULT" local VIEW_BUFFER_UPDATED_PATTERN = "AvanteViewBufferUpdated" @@ -1489,6 +1490,11 @@ function Sidebar:should_auto_scroll() return is_scrolled_to_bottom end +Sidebar.throttled_update_content = Utils.throttle(function(self, ...) + local args = { ... } + self:update_content(unpack(args)) +end, 50) + ---@param content string concatenated content of the buffer ---@param opts? {focus?: boolean, scroll?: boolean, backspace?: integer, callback?: fun(): nil} whether to focus the result view function Sidebar:update_content(content, opts) @@ -1629,7 +1635,7 @@ end ---@param messages avante.HistoryMessage[] ---@param ctx table ---@return avante.ui.Line[] -local function get_message_lines(message, messages, ctx) +local function _get_message_lines(message, messages, ctx) if message.visible == false then return {} end local lines = Utils.message_to_lines(message, messages) if message.is_user_submission then @@ -1674,6 +1680,21 @@ local function get_message_lines(message, messages, ctx) return lines end +local _message_to_lines_lru_cache = LRUCache:new(100) + +---@param message avante.HistoryMessage +---@param messages avante.HistoryMessage[] +---@param ctx table +---@return avante.ui.Line[] +local function get_message_lines(message, messages, ctx) + if message.state == "generating" then return _get_message_lines(message, messages, ctx) end + local cached_lines = _message_to_lines_lru_cache:get(message.uuid) + if cached_lines then return cached_lines end + local lines = _get_message_lines(message, messages, ctx) + _message_to_lines_lru_cache:set(message.uuid, lines) + return lines +end + ---@param history avante.ChatHistory ---@return avante.ui.Line[] function Sidebar.get_history_lines(history) @@ -1922,7 +1943,12 @@ function Sidebar:new_chat(args, cb) vim.schedule(function() self:create_todos_container() end) end -function Sidebar:save_history() Path.history.save(self.code.bufnr, self.chat_history) end +local debounced_save_history = Utils.debounce( + function(self) Path.history.save(self.code.bufnr, self.chat_history) end, + 1000 +) + +function Sidebar:save_history() debounced_save_history(self) end ---@param uuids string[] function Sidebar:delete_history_messages(uuids) @@ -1993,7 +2019,7 @@ function Sidebar:add_history_messages(messages) self.current_state = "generating" end end - xpcall(function() self:update_content("") end, function(err) + xpcall(function() self:throttled_update_content("") end, function(err) Utils.debug("Failed to update content:", err) return nil end) diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua index 44e66cf..76af92d 100644 --- a/lua/avante/utils/init.lua +++ b/lua/avante/utils/init.lua @@ -2,6 +2,8 @@ local api = vim.api local fn = vim.fn local lsp = vim.lsp +local LRUCache = require("avante.utils.lru_cache") + ---@class avante.utils: LazyUtilCore ---@field tokens avante.utils.tokens ---@field root avante.utils.root @@ -689,6 +691,25 @@ function M.debounce(func, delay) end end +function M.throttle(func, delay) + local timer = nil + local args + + return function(...) + args = { ... } + + if timer then return end + + timer = vim.loop.new_timer() + if not timer then return end + timer:start(delay, 0, function() + vim.schedule(function() func(unpack(args)) end) + timer:close() + timer = nil + end) + end +end + function M.winline(winid) local current_win = api.nvim_get_current_win() api.nvim_set_current_win(winid) @@ -1012,7 +1033,7 @@ end ---@param new_lines avante.ui.Line[] ---@return { start_line: integer, end_line: integer, content: avante.ui.Line[] }[] local function get_lines_diff(old_lines, new_lines) - local remaining_lines = 30 + local remaining_lines = 100 local start_line = 0 if #new_lines >= #old_lines then start_line = math.max(#old_lines - remaining_lines, 0) @@ -1122,7 +1143,11 @@ function M.is_same_file(filepath_a, filepath_b) return M.uniform_path(filepath_a function M.trim_think_content(content) return content:gsub("^.-", "", 1) end +local _filetype_lru_cache = LRUCache:new(60) + function M.get_filetype(filepath) + local cached_filetype = _filetype_lru_cache:get(filepath) + if cached_filetype then return cached_filetype end -- Some files are sometimes not detected correctly when buffer is not included -- https://github.com/neovim/neovim/issues/27265 @@ -1131,6 +1156,7 @@ function M.get_filetype(filepath) vim.api.nvim_buf_delete(buf, { force = true }) -- Parse the first filetype from a multifiltype file filetype = filetype:gsub("%..*$", "") + _filetype_lru_cache:set(filepath, filetype) return filetype end