feat(ui): expand tool use message (#2684)

This commit is contained in:
yetone
2025-09-04 00:17:32 +08:00
committed by GitHub
parent 20c4f44195
commit 4ac4c8ed3f
6 changed files with 243 additions and 84 deletions

View File

@@ -569,6 +569,7 @@ M._defaults = {
repomap = "<leader>aR", repomap = "<leader>aR",
}, },
sidebar = { sidebar = {
expand_tool_use = "<S-Tab>",
next_prompt = "]p", next_prompt = "]p",
prev_prompt = "[p", prev_prompt = "[p",
apply_all = "A", apply_all = "A",

View File

@@ -7,11 +7,11 @@ local rshift, band = bit.rshift, bit.band
local Highlights = { local Highlights = {
TITLE = { name = "AvanteTitle", fg = "#1e222a", bg = "#98c379" }, TITLE = { name = "AvanteTitle", fg = "#1e222a", bg = "#98c379" },
REVERSED_TITLE = { name = "AvanteReversedTitle", fg = "#98c379" }, REVERSED_TITLE = { name = "AvanteReversedTitle", fg = "#98c379", bg_link = "NormalFloat" },
SUBTITLE = { name = "AvanteSubtitle", fg = "#1e222a", bg = "#56b6c2" }, SUBTITLE = { name = "AvanteSubtitle", fg = "#1e222a", bg = "#56b6c2" },
REVERSED_SUBTITLE = { name = "AvanteReversedSubtitle", fg = "#56b6c2" }, REVERSED_SUBTITLE = { name = "AvanteReversedSubtitle", fg = "#56b6c2", bg_link = "NormalFloat" },
THIRD_TITLE = { name = "AvanteThirdTitle", fg = "#ABB2BF", bg = "#353B45" }, THIRD_TITLE = { name = "AvanteThirdTitle", fg = "#ABB2BF", bg = "#353B45" },
REVERSED_THIRD_TITLE = { name = "AvanteReversedThirdTitle", fg = "#353B45" }, REVERSED_THIRD_TITLE = { name = "AvanteReversedThirdTitle", fg = "#353B45", bg_link = "NormalFloat" },
SUGGESTION = { name = "AvanteSuggestion", link = "Comment" }, SUGGESTION = { name = "AvanteSuggestion", link = "Comment" },
ANNOTATION = { name = "AvanteAnnotation", link = "Comment" }, ANNOTATION = { name = "AvanteAnnotation", link = "Comment" },
POPUP_HINT = { name = "AvantePopupHint", link = "NormalFloat" }, POPUP_HINT = { name = "AvantePopupHint", link = "NormalFloat" },

View File

@@ -137,39 +137,45 @@ function M.get_diff_lines(old_str, new_str, decoration, truncate)
ctxlen = vim.o.scrolloff, ctxlen = vim.o.scrolloff,
}) })
local prev_start_a = 0 local prev_start_a = 0
for idx, hunk in ipairs(patch) do local truncated_lines = 0
if truncate and line_count > 10 then for _, hunk in ipairs(patch) do
table.insert(
lines,
Line:new({
{ decoration },
{
string.format("... (Result truncated, remaining %d hunks not shown)", #patch - idx + 1),
Highlights.AVANTE_COMMENT_FG,
},
})
)
break
end
local start_a, count_a, start_b, count_b = unpack(hunk) local start_a, count_a, start_b, count_b = unpack(hunk)
local no_change_lines = vim.list_slice(old_lines, prev_start_a, start_a - 1) local no_change_lines = vim.list_slice(old_lines, prev_start_a, start_a - 1)
local last_tree_no_change_lines = vim.list_slice(no_change_lines, #no_change_lines - 3) if truncate then
if #no_change_lines > 3 then table.insert(lines, Line:new({ { decoration }, { "..." } })) end local last_three_no_change_lines = vim.list_slice(no_change_lines, #no_change_lines - 3)
for _, line in ipairs(last_tree_no_change_lines) do truncated_lines = truncated_lines + #no_change_lines - #last_three_no_change_lines
if #no_change_lines > 4 then
table.insert(lines, Line:new({ { decoration }, { "...", Highlights.AVANTE_COMMENT_FG } }))
end
no_change_lines = last_three_no_change_lines
end
for idx, line in ipairs(no_change_lines) do
if truncate and line_count > 10 then
truncated_lines = truncated_lines + #no_change_lines - idx
break
end
line_count = line_count + 1 line_count = line_count + 1
table.insert(lines, Line:new({ { decoration }, { line } })) table.insert(lines, Line:new({ { decoration }, { line } }))
end end
prev_start_a = start_a + count_a prev_start_a = start_a + count_a
if count_a > 0 then if count_a > 0 then
local delete_lines = vim.list_slice(old_lines, start_a, start_a + count_a - 1) local delete_lines = vim.list_slice(old_lines, start_a, start_a + count_a - 1)
for _, line in ipairs(delete_lines) do for idx, line in ipairs(delete_lines) do
if truncate and line_count > 10 then
truncated_lines = truncated_lines + #delete_lines - idx
break
end
line_count = line_count + 1 line_count = line_count + 1
table.insert(lines, Line:new({ { decoration }, { line, Highlights.TO_BE_DELETED_WITHOUT_STRIKETHROUGH } })) table.insert(lines, Line:new({ { decoration }, { line, Highlights.TO_BE_DELETED_WITHOUT_STRIKETHROUGH } }))
end end
end end
if count_b > 0 then if count_b > 0 then
local create_lines = vim.list_slice(new_lines, start_b, start_b + count_b - 1) local create_lines = vim.list_slice(new_lines, start_b, start_b + count_b - 1)
for _, line in ipairs(create_lines) do for idx, line in ipairs(create_lines) do
if truncate and line_count > 10 then
truncated_lines = truncated_lines + #create_lines - idx
break
end
line_count = line_count + 1 line_count = line_count + 1
table.insert(lines, Line:new({ { decoration }, { line, Highlights.INCOMING } })) table.insert(lines, Line:new({ { decoration }, { line, Highlights.INCOMING } }))
end end
@@ -178,12 +184,27 @@ function M.get_diff_lines(old_str, new_str, decoration, truncate)
if prev_start_a < #old_lines then if prev_start_a < #old_lines then
-- Append remaining old_lines -- Append remaining old_lines
local no_change_lines = vim.list_slice(old_lines, prev_start_a, #old_lines) local no_change_lines = vim.list_slice(old_lines, prev_start_a, #old_lines)
local first_tree_no_change_lines = vim.list_slice(no_change_lines, 1, 3) local first_three_no_change_lines = vim.list_slice(no_change_lines, 1, 3)
for _, line in ipairs(first_tree_no_change_lines) do for idx, line in ipairs(first_three_no_change_lines) do
if truncate and line_count > 10 then
truncated_lines = truncated_lines + #first_three_no_change_lines - idx
break
end
line_count = line_count + 1 line_count = line_count + 1
table.insert(lines, Line:new({ { decoration }, { line } })) table.insert(lines, Line:new({ { decoration }, { line } }))
end end
if #no_change_lines > 3 then table.insert(lines, Line:new({ { decoration }, { "..." } })) end end
if truncate and truncated_lines > 0 then
table.insert(
lines,
Line:new({
{ decoration },
{
string.format("... (Result truncated, remaining %d lines not shown)", truncated_lines),
Highlights.AVANTE_COMMENT_FG,
},
})
)
end end
return lines return lines
end end
@@ -369,8 +390,9 @@ end
---@param item AvanteLLMMessageContentItem ---@param item AvanteLLMMessageContentItem
---@param message avante.HistoryMessage ---@param message avante.HistoryMessage
---@param messages avante.HistoryMessage[] ---@param messages avante.HistoryMessage[]
---@param expanded boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[]
local function tool_to_lines(item, message, messages) local function tool_to_lines(item, message, messages, expanded)
-- local logs = message.tool_use_logs -- local logs = message.tool_use_logs
local lines = {} local lines = {}
@@ -405,7 +427,7 @@ local function tool_to_lines(item, message, messages)
local lines_ = text_to_lines(table.concat(rest_input_text_lines, "\n"), decoration) local lines_ = text_to_lines(table.concat(rest_input_text_lines, "\n"), decoration)
local line_count = 0 local line_count = 0
for idx, line in ipairs(lines_) do for idx, line in ipairs(lines_) do
if line_count > 3 then if not expanded and line_count > 3 then
table.insert( table.insert(
lines, lines,
Line:new({ Line:new({
@@ -425,21 +447,21 @@ local function tool_to_lines(item, message, messages)
end end
if item.input and type(item.input) == "table" then if item.input and type(item.input) == "table" then
if type(item.input.old_str) == "string" and type(item.input.new_str) == "string" then if type(item.input.old_str) == "string" and type(item.input.new_str) == "string" then
local diff_lines = M.get_diff_lines(item.input.old_str, item.input.new_str, decoration, true) local diff_lines = M.get_diff_lines(item.input.old_str, item.input.new_str, decoration, not expanded)
vim.list_extend(lines, diff_lines) vim.list_extend(lines, diff_lines)
end end
end end
if message.acp_tool_call and message.acp_tool_call.content then if message.acp_tool_call and message.acp_tool_call.content then
local content = message.acp_tool_call.content local content = message.acp_tool_call.content
if content then if content then
local content_lines = M.get_content_lines(content, decoration, true) local content_lines = M.get_content_lines(content, decoration, not expanded)
vim.list_extend(lines, content_lines) vim.list_extend(lines, content_lines)
end end
else else
if result and result.content then if result and result.content then
local result_content = result.content local result_content = result.content
if result_content then if result_content then
local content_lines = M.get_content_lines(result_content, decoration, true) local content_lines = M.get_content_lines(result_content, decoration, not expanded)
vim.list_extend(lines, content_lines) vim.list_extend(lines, content_lines)
end end
end end
@@ -454,8 +476,9 @@ end
---@param item AvanteLLMMessageContentItem ---@param item AvanteLLMMessageContentItem
---@param message avante.HistoryMessage ---@param message avante.HistoryMessage
---@param messages avante.HistoryMessage[] ---@param messages avante.HistoryMessage[]
---@param expanded boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[]
local function message_content_item_to_lines(item, message, messages) local function message_content_item_to_lines(item, message, messages, expanded)
if type(item) == "string" then if type(item) == "string" then
return text_to_lines(item) return text_to_lines(item)
elseif type(item) == "table" then elseif type(item) == "table" then
@@ -480,7 +503,7 @@ local function message_content_item_to_lines(item, message, messages)
end end
end end
local lines = tool_to_lines(item, message, messages) local lines = tool_to_lines(item, message, messages, expanded)
if message.tool_use_log_lines then lines = vim.list_extend(lines, message.tool_use_log_lines) end if message.tool_use_log_lines then lines = vim.list_extend(lines, message.tool_use_log_lines) end
return lines return lines
end end
@@ -491,15 +514,16 @@ end
---Converts a message into representation suitable for UI ---Converts a message into representation suitable for UI
---@param message avante.HistoryMessage ---@param message avante.HistoryMessage
---@param messages avante.HistoryMessage[] ---@param messages avante.HistoryMessage[]
---@param expanded boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[]
function M.message_to_lines(message, messages) function M.message_to_lines(message, messages, expanded)
if message.displayed_content then return text_to_lines(message.displayed_content) end if message.displayed_content then return text_to_lines(message.displayed_content) end
local content = message.message.content local content = message.message.content
if type(content) == "string" then return text_to_lines(content) end if type(content) == "string" then return text_to_lines(content) end
if islist(content) then if islist(content) then
local lines = {} local lines = {}
for _, item in ipairs(content) do for _, item in ipairs(content) do
local item_lines = message_content_item_to_lines(item, message, messages) local item_lines = message_content_item_to_lines(item, message, messages, expanded)
lines = vim.list_extend(lines, item_lines) lines = vim.list_extend(lines, item_lines)
end end
return lines return lines

View File

@@ -25,6 +25,7 @@ local logo = require("avante.utils.logo")
local RESULT_BUF_NAME = "AVANTE_RESULT" local RESULT_BUF_NAME = "AVANTE_RESULT"
local VIEW_BUFFER_UPDATED_PATTERN = "AvanteViewBufferUpdated" local VIEW_BUFFER_UPDATED_PATTERN = "AvanteViewBufferUpdated"
local CODEBLOCK_KEYBINDING_NAMESPACE = api.nvim_create_namespace("AVANTE_CODEBLOCK_KEYBINDING") local CODEBLOCK_KEYBINDING_NAMESPACE = api.nvim_create_namespace("AVANTE_CODEBLOCK_KEYBINDING")
local TOOL_MESSAGE_KEYBINDING_NAMESPACE = api.nvim_create_namespace("AVANTE_TOOL_MESSAGE_KEYBINDING")
local USER_REQUEST_BLOCK_KEYBINDING_NAMESPACE = api.nvim_create_namespace("AVANTE_USER_REQUEST_BLOCK_KEYBINDING") local USER_REQUEST_BLOCK_KEYBINDING_NAMESPACE = api.nvim_create_namespace("AVANTE_USER_REQUEST_BLOCK_KEYBINDING")
local SELECTED_FILES_HINT_NAMESPACE = api.nvim_create_namespace("AVANTE_SELECTED_FILES_HINT") local SELECTED_FILES_HINT_NAMESPACE = api.nvim_create_namespace("AVANTE_SELECTED_FILES_HINT")
local SELECTED_FILES_ICON_NAMESPACE = api.nvim_create_namespace("AVANTE_SELECTED_FILES_ICON") local SELECTED_FILES_ICON_NAMESPACE = api.nvim_create_namespace("AVANTE_SELECTED_FILES_ICON")
@@ -78,6 +79,11 @@ Sidebar.__index = Sidebar
---@field acp_client avante.acp.ACPClient | nil ---@field acp_client avante.acp.ACPClient | nil
---@field acp_session_id string | nil ---@field acp_session_id string | nil
---@field post_render? fun(sidebar: avante.Sidebar) ---@field post_render? fun(sidebar: avante.Sidebar)
---@field message_button_handlers table<string, table<string, fun(arg: any)>>
---@field expanded_message_uuids table<string, boolean>
---@field tool_message_positions table<string, [integer, integer]>
---@field skip_line_count integer | nil
---@field current_tool_use_extmark_id integer | nil
---@param id integer the tabpage id retrieved from api.nvim_get_current_tabpage() ---@param id integer the tabpage id retrieved from api.nvim_get_current_tabpage()
function Sidebar:new(id) function Sidebar:new(id)
@@ -110,6 +116,10 @@ function Sidebar:new(id)
_cached_history_lines = nil, _cached_history_lines = nil,
_history_cache_invalidated = true, _history_cache_invalidated = true,
post_render = nil, post_render = nil,
message_handlers = {},
tool_message_positions = {},
expanded_message_ids = {},
current_tool_use_extmark_id = nil,
}, Sidebar) }, Sidebar)
end end
@@ -146,6 +156,10 @@ function Sidebar:reset()
self.scroll = true self.scroll = true
self.old_result_lines = {} self.old_result_lines = {}
self.token_count = nil self.token_count = nil
self.message_button_handlers = {}
self.tool_message_positions = {}
self.expanded_message_uuids = {}
self.current_tool_use_extmark_id = nil
end end
---@class SidebarOpenOptions: AskOptions ---@class SidebarOpenOptions: AskOptions
@@ -745,6 +759,16 @@ function Sidebar:is_cursor_in_user_request_block()
return cursor_line >= block.start_line and cursor_line <= block.end_line return cursor_line >= block.start_line and cursor_line <= block.end_line
end end
function Sidebar:get_current_tool_use_message_uuid()
local skip_line_count = self.skip_line_count or 0
local cursor_line = api.nvim_win_get_cursor(self.containers.result.winid)[1]
for message_uuid, positions in pairs(self.tool_message_positions) do
if skip_line_count + positions[1] + 1 <= cursor_line and cursor_line <= skip_line_count + positions[2] then
return message_uuid, positions
end
end
end
---@class AvanteCodeblock ---@class AvanteCodeblock
---@field start_line integer 1-indexed ---@field start_line integer 1-indexed
---@field end_line integer 1-indexed ---@field end_line integer 1-indexed
@@ -840,6 +864,13 @@ function Sidebar:retry_user_request()
self.handle_submit(block.content) self.handle_submit(block.content)
end end
function Sidebar:handle_expand_message(message_uuid, expanded)
Utils.debug("handle_expand_message", message_uuid, expanded)
self.expanded_message_uuids[message_uuid] = expanded
self._history_cache_invalidated = true
self:update_content("")
end
function Sidebar:edit_user_request() function Sidebar:edit_user_request()
local block = self:get_current_user_request_block() local block = self:get_current_user_request_block()
if not block then return end if not block then return end
@@ -1066,6 +1097,24 @@ function Sidebar:unbind_retry_user_request_key()
end end
end end
function Sidebar:bind_expand_tool_use_key(message_uuid)
if self.containers.result then
local expanded = self.expanded_message_uuids[message_uuid]
vim.keymap.set(
"n",
Config.mappings.sidebar.expand_tool_use,
function() self:handle_expand_message(message_uuid, not expanded) end,
{ buffer = self.containers.result.bufnr, noremap = true, silent = true }
)
end
end
function Sidebar:unbind_expand_tool_use_key()
if self.containers.result then
pcall(vim.keymap.del, "n", Config.mappings.sidebar.expand_tool_use, { buffer = self.containers.result.bufnr })
end
end
function Sidebar:bind_edit_user_request_key() function Sidebar:bind_edit_user_request_key()
if self.containers.result then if self.containers.result then
vim.keymap.set( vim.keymap.set(
@@ -1083,6 +1132,50 @@ function Sidebar:unbind_edit_user_request_key()
end end
end end
function Sidebar:render_tool_use_control_buttons()
local function show_current_tool_use_control_buttons()
if self.current_tool_use_extmark_id then
api.nvim_buf_del_extmark(
self.containers.result.bufnr,
TOOL_MESSAGE_KEYBINDING_NAMESPACE,
self.current_tool_use_extmark_id
)
end
local message_uuid, positions = self:get_current_tool_use_message_uuid()
if not message_uuid then return end
local expanded = self.expanded_message_uuids[message_uuid]
local skip_line_count = self.skip_line_count or 0
self.current_tool_use_extmark_id = api.nvim_buf_set_extmark(
self.containers.result.bufnr,
TOOL_MESSAGE_KEYBINDING_NAMESPACE,
skip_line_count + positions[1] + 2,
-1,
{
virt_text = {
{
string.format(" [%s: %s] ", Config.mappings.sidebar.expand_tool_use, expanded and "Collapse" or "Expand"),
"AvanteInlineHint",
},
},
virt_text_pos = "right_align",
hl_group = "AvanteInlineHint",
priority = PRIORITY,
}
)
end
local current_tool_use_message_uuid = self:get_current_tool_use_message_uuid()
if current_tool_use_message_uuid then
show_current_tool_use_control_buttons()
self:bind_expand_tool_use_key(current_tool_use_message_uuid)
else
api.nvim_buf_clear_namespace(self.containers.result.bufnr, TOOL_MESSAGE_KEYBINDING_NAMESPACE, 0, -1)
self:unbind_expand_tool_use_key()
end
end
function Sidebar:bind_sidebar_keys(codeblocks) function Sidebar:bind_sidebar_keys(codeblocks)
---@param direction "next" | "prev" ---@param direction "next" | "prev"
local function jump_to_codeblock(direction) local function jump_to_codeblock(direction)
@@ -1291,6 +1384,8 @@ function Sidebar:on_mount(opts)
group = self.augroup, group = self.augroup,
buffer = self.containers.result.bufnr, buffer = self.containers.result.bufnr,
callback = function(ev) callback = function(ev)
self:render_tool_use_control_buttons()
local in_codeblock = is_cursor_in_codeblock(codeblocks) local in_codeblock = is_cursor_in_codeblock(codeblocks)
if in_codeblock then if in_codeblock then
@@ -1628,8 +1723,10 @@ function Sidebar:update_content(content, opts)
) )
local history_lines local history_lines
local tool_message_positions
if not self._cached_history_lines or self._history_cache_invalidated then if not self._cached_history_lines or self._history_cache_invalidated then
history_lines = self.get_history_lines(self.chat_history, self.show_logo) history_lines, tool_message_positions = self:get_history_lines(self.chat_history, self.show_logo)
self.tool_message_positions = tool_message_positions
self._cached_history_lines = history_lines self._cached_history_lines = history_lines
self._history_cache_invalidated = false self._history_cache_invalidated = false
else else
@@ -1648,7 +1745,10 @@ function Sidebar:update_content(content, opts)
self:clear_state() self:clear_state()
local skip_line_count = 0 local skip_line_count = 0
if self.show_logo then skip_line_count = self:render_logo() end if self.show_logo then
skip_line_count = self:render_logo()
self.skip_line_count = skip_line_count
end
local bufnr = self.containers.result.bufnr local bufnr = self.containers.result.bufnr
Utils.unlock_buf(bufnr) Utils.unlock_buf(bufnr)
@@ -1673,6 +1773,7 @@ function Sidebar:update_content(content, opts)
vim.schedule(function() vim.schedule(function()
self:render_state() self:render_state()
self:render_tool_use_control_buttons()
vim.defer_fn(function() vim.cmd("redraw") end, 10) vim.defer_fn(function() vim.cmd("redraw") end, 10)
end) end)
@@ -1743,10 +1844,11 @@ end
---@param messages avante.HistoryMessage[] ---@param messages avante.HistoryMessage[]
---@param ctx table ---@param ctx table
---@param ignore_record_prefix boolean | nil ---@param ignore_record_prefix boolean | nil
---@param expanded boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[]
local function _get_message_lines(message, messages, ctx, ignore_record_prefix) local function _get_message_lines(message, messages, ctx, ignore_record_prefix, expanded)
if message.visible == false then return {} end if message.visible == false then return {} end
local lines = Render.message_to_lines(message, messages) local lines = Render.message_to_lines(message, messages, expanded)
if message.is_user_submission and not ignore_record_prefix then if message.is_user_submission and not ignore_record_prefix then
ctx.selected_filepaths = message.selected_filepaths ctx.selected_filepaths = message.selected_filepaths
local text = table.concat(vim.tbl_map(function(line) return tostring(line) end, lines), "\n") local text = table.concat(vim.tbl_map(function(line) return tostring(line) end, lines), "\n")
@@ -1794,10 +1896,11 @@ local _message_to_lines_lru_cache = LRUCache:new(100)
---@param messages avante.HistoryMessage[] ---@param messages avante.HistoryMessage[]
---@param ctx table ---@param ctx table
---@param ignore_record_prefix boolean | nil ---@param ignore_record_prefix boolean | nil
---@param expanded boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[]
local function get_message_lines(message, messages, ctx, ignore_record_prefix) local function get_message_lines(message, messages, ctx, ignore_record_prefix, expanded)
if message.state == "generating" or message.is_calling then if message.state == "generating" or message.is_calling then
return _get_message_lines(message, messages, ctx, ignore_record_prefix) return _get_message_lines(message, messages, ctx, ignore_record_prefix, expanded)
end end
local text_len = 0 local text_len = 0
local content = message.message.content local content = message.message.content
@@ -1814,52 +1917,51 @@ local function get_message_lines(message, messages, ctx, ignore_record_prefix)
elseif type(content) == "string" then elseif type(content) == "string" then
text_len = #content text_len = #content
end end
local cache_key = message.uuid .. ":" .. tostring(text_len) local cache_key = message.uuid .. ":" .. tostring(text_len) .. ":" .. tostring(expanded == true)
local cached_lines = _message_to_lines_lru_cache:get(cache_key) local cached_lines = _message_to_lines_lru_cache:get(cache_key)
if cached_lines then return cached_lines end if cached_lines then return cached_lines end
local lines = _get_message_lines(message, messages, ctx, ignore_record_prefix) local lines = _get_message_lines(message, messages, ctx, ignore_record_prefix, expanded)
_message_to_lines_lru_cache:set(cache_key, lines) _message_to_lines_lru_cache:set(cache_key, lines)
return lines return lines
end end
---@param history avante.ChatHistory ---@param history avante.ChatHistory
---@param ignore_record_prefix boolean | nil ---@param ignore_record_prefix boolean | nil
---@return avante.ui.Line[] ---@return avante.ui.Line[] history_lines
function Sidebar.get_history_lines(history, ignore_record_prefix) ---@return table<string, [integer, integer]> tool_message_positions
function Sidebar:get_history_lines(history, ignore_record_prefix)
local history_messages = History.get_history_messages(history) local history_messages = History.get_history_messages(history)
local ctx = {} local ctx = {}
---@type avante.ui.Line[][] ---@type avante.ui.Line[]
local group = {} local res = {}
local tool_message_positions = {}
local is_first_user_submission = true
for _, message in ipairs(history_messages) do for _, message in ipairs(history_messages) do
local lines = get_message_lines(message, history_messages, ctx, ignore_record_prefix) local expanded = self.expanded_message_uuids[message.uuid]
local lines = get_message_lines(message, history_messages, ctx, ignore_record_prefix, expanded)
if #lines == 0 then goto continue end if #lines == 0 then goto continue end
if message.is_user_submission then table.insert(group, {}) end if message.is_user_submission then
local last_item = group[#group] if not is_first_user_submission then
if last_item == nil then if ignore_record_prefix then
table.insert(group, {}) res = vim.list_extend(res, { Line:new({ { "" } }), Line:new({ { "" } }) })
last_item = group[#group] else
res = vim.list_extend(res, { Line:new({ { "" } }), Line:new({ { RESP_SEPARATOR } }), Line:new({ { "" } }) })
end
end
is_first_user_submission = false
end end
if message.message.role == "assistant" and not message.just_for_display and tostring(lines[1]) ~= "" then if message.message.role == "assistant" and not message.just_for_display and tostring(lines[1]) ~= "" then
table.insert(lines, 1, Line:new({ { "" } })) table.insert(lines, 1, Line:new({ { "" } }))
table.insert(lines, 1, Line:new({ { "" } })) table.insert(lines, 1, Line:new({ { "" } }))
end end
last_item = vim.list_extend(last_item, lines) if History.Helpers.is_tool_use_message(message) then
group[#group] = last_item tool_message_positions[message.uuid] = { #res, #res + #lines }
end
res = vim.list_extend(res, lines)
::continue:: ::continue::
end end
local res = {}
for idx, item in ipairs(group) do
if idx ~= 1 then
if ignore_record_prefix then
res = vim.list_extend(res, { Line:new({ { "" } }), Line:new({ { "" } }) })
else
res = vim.list_extend(res, { Line:new({ { "" } }), Line:new({ { RESP_SEPARATOR } }), Line:new({ { "" } }) })
end
end
res = vim.list_extend(res, item)
end
table.insert(res, Line:new({ { "" } })) table.insert(res, Line:new({ { "" } }))
return res return res, tool_message_positions
end end
---@param message avante.HistoryMessage ---@param message avante.HistoryMessage
@@ -2086,6 +2188,10 @@ function Sidebar:new_chat(args, cb)
self:reload_chat_history() self:reload_chat_history()
self.current_state = nil self.current_state = nil
self.acp_session_id = nil self.acp_session_id = nil
self.message_button_handlers = {}
self.expanded_message_uuids = {}
self.tool_message_positions = {}
self.current_tool_use_extmark_id = nil
self:update_content("New chat", { focus = false, scroll = false, callback = function() self:focus_input() end }) self:update_content("New chat", { focus = false, scroll = false, callback = function() self:focus_input() end })
--- goto first line then go to last line --- goto first line then go to last line
vim.schedule(function() vim.schedule(function()

View File

@@ -118,14 +118,16 @@ vim.g.avante_login = vim.g.avante_login
---@field is_calling boolean | nil ---@field is_calling boolean | nil
---@field original_content AvanteLLMMessageContent | nil ---@field original_content AvanteLLMMessageContent | nil
---@field acp_tool_call? avante.acp.ToolCall ---@field acp_tool_call? avante.acp.ToolCall
--- ---@field permission_options? avante.acp.PermissionOption[]
---@field is_permission_confirming? boolean
---@class AvanteLLMToolResult ---@class AvanteLLMToolResult
---@field tool_name string ---@field tool_name string
---@field tool_use_id string ---@field tool_use_id string
---@field content string ---@field content string
---@field is_error? boolean ---@field is_error? boolean
---@field is_user_declined? boolean ---@field is_user_declined? boolean
---
---@class AvantePromptOptions: table<[string], string> ---@class AvantePromptOptions: table<[string], string>
---@field system_prompt string ---@field system_prompt string
---@field messages AvanteLLMMessage[] ---@field messages AvanteLLMMessage[]

View File

@@ -1300,29 +1300,55 @@ end
---@param skip_line_count? integer ---@param skip_line_count? integer
function M.update_buffer_lines(ns_id, bufnr, old_lines, new_lines, skip_line_count) function M.update_buffer_lines(ns_id, bufnr, old_lines, new_lines, skip_line_count)
skip_line_count = skip_line_count or 0 skip_line_count = skip_line_count or 0
local diffs = get_lines_diff(old_lines, new_lines) local diff_start_idx = 0
if #diffs == 0 then return end for i, line in ipairs(new_lines) do
for _, diff in ipairs(diffs) do local old_line = old_lines[i]
local lines = diff.content if not old_line or old_line ~= line then
local text_lines = vim.tbl_map(function(line) return tostring(line) end, lines) diff_start_idx = i
--- rmeove newlines from text_lines break
local cleaned_lines = {}
for _, line in ipairs(text_lines) do
local lines_ = vim.split(line, "\n")
cleaned_lines = vim.list_extend(cleaned_lines, lines_)
end end
end
if diff_start_idx > 0 then
local changed_lines = vim.list_slice(new_lines, diff_start_idx)
local text_lines = vim.tbl_map(function(line) return tostring(line) end, changed_lines)
vim.api.nvim_buf_set_lines( vim.api.nvim_buf_set_lines(
bufnr, bufnr,
skip_line_count + diff.start_line - 1, skip_line_count + diff_start_idx - 1,
skip_line_count + diff.end_line - 1, skip_line_count + diff_start_idx + #changed_lines,
false, false,
cleaned_lines text_lines
) )
for i, line in ipairs(lines) do for i, line in ipairs(changed_lines) do
line:set_highlights(ns_id, bufnr, skip_line_count + diff.start_line + i - 2) line:set_highlights(ns_id, bufnr, skip_line_count + diff_start_idx + i - 2)
end end
vim.cmd("redraw")
end end
if #old_lines > #new_lines then
vim.api.nvim_buf_set_lines(bufnr, skip_line_count + #new_lines, skip_line_count + #old_lines, false, {})
end
vim.cmd("redraw")
-- local diffs = get_lines_diff(old_lines, new_lines)
-- if #diffs == 0 then return end
-- for _, diff in ipairs(diffs) do
-- local lines = diff.content
-- local text_lines = vim.tbl_map(function(line) return tostring(line) end, lines)
-- --- remove newlines from text_lines
-- local cleaned_lines = {}
-- for _, line in ipairs(text_lines) do
-- local lines_ = vim.split(line, "\n")
-- cleaned_lines = vim.list_extend(cleaned_lines, lines_)
-- end
-- vim.api.nvim_buf_set_lines(
-- bufnr,
-- skip_line_count + diff.start_line - 1,
-- skip_line_count + diff.end_line - 1,
-- false,
-- cleaned_lines
-- )
-- for i, line in ipairs(lines) do
-- line:set_highlights(ns_id, bufnr, skip_line_count + diff.start_line + i - 2)
-- end
-- vim.cmd("redraw")
-- end
end end
function M.uniform_path(path) function M.uniform_path(path)