From 4ac4c8ed3fb2e86e06d430d6001d7db62ced90a1 Mon Sep 17 00:00:00 2001 From: yetone Date: Thu, 4 Sep 2025 00:17:32 +0800 Subject: [PATCH] feat(ui): expand tool use message (#2684) --- lua/avante/config.lua | 1 + lua/avante/highlights.lua | 6 +- lua/avante/history/render.lua | 86 ++++++++++------- lua/avante/sidebar.lua | 170 +++++++++++++++++++++++++++------- lua/avante/types.lua | 6 +- lua/avante/utils/init.lua | 58 ++++++++---- 6 files changed, 243 insertions(+), 84 deletions(-) diff --git a/lua/avante/config.lua b/lua/avante/config.lua index efccda2..4fd0fb7 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -569,6 +569,7 @@ M._defaults = { repomap = "aR", }, sidebar = { + expand_tool_use = "", next_prompt = "]p", prev_prompt = "[p", apply_all = "A", diff --git a/lua/avante/highlights.lua b/lua/avante/highlights.lua index 246a535d..b061f6c 100644 --- a/lua/avante/highlights.lua +++ b/lua/avante/highlights.lua @@ -7,11 +7,11 @@ local rshift, band = bit.rshift, bit.band local Highlights = { 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" }, - REVERSED_SUBTITLE = { name = "AvanteReversedSubtitle", fg = "#56b6c2" }, + REVERSED_SUBTITLE = { name = "AvanteReversedSubtitle", fg = "#56b6c2", bg_link = "NormalFloat" }, 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" }, ANNOTATION = { name = "AvanteAnnotation", link = "Comment" }, POPUP_HINT = { name = "AvantePopupHint", link = "NormalFloat" }, diff --git a/lua/avante/history/render.lua b/lua/avante/history/render.lua index c9a97d4..8d863d2 100644 --- a/lua/avante/history/render.lua +++ b/lua/avante/history/render.lua @@ -137,39 +137,45 @@ function M.get_diff_lines(old_str, new_str, decoration, truncate) ctxlen = vim.o.scrolloff, }) local prev_start_a = 0 - for idx, hunk in ipairs(patch) do - if truncate and line_count > 10 then - 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 truncated_lines = 0 + for _, hunk in ipairs(patch) do 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 last_tree_no_change_lines = vim.list_slice(no_change_lines, #no_change_lines - 3) - if #no_change_lines > 3 then table.insert(lines, Line:new({ { decoration }, { "..." } })) end - for _, line in ipairs(last_tree_no_change_lines) do + if truncate then + local last_three_no_change_lines = vim.list_slice(no_change_lines, #no_change_lines - 3) + 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 table.insert(lines, Line:new({ { decoration }, { line } })) end prev_start_a = start_a + count_a if count_a > 0 then 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 table.insert(lines, Line:new({ { decoration }, { line, Highlights.TO_BE_DELETED_WITHOUT_STRIKETHROUGH } })) end end if count_b > 0 then 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 table.insert(lines, Line:new({ { decoration }, { line, Highlights.INCOMING } })) end @@ -178,12 +184,27 @@ function M.get_diff_lines(old_str, new_str, decoration, truncate) if prev_start_a < #old_lines then -- Append remaining 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) - for _, line in ipairs(first_tree_no_change_lines) do + local first_three_no_change_lines = vim.list_slice(no_change_lines, 1, 3) + 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 table.insert(lines, Line:new({ { decoration }, { line } })) 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 return lines end @@ -369,8 +390,9 @@ end ---@param item AvanteLLMMessageContentItem ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] +---@param expanded boolean | nil ---@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 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 line_count = 0 for idx, line in ipairs(lines_) do - if line_count > 3 then + if not expanded and line_count > 3 then table.insert( lines, Line:new({ @@ -425,21 +447,21 @@ local function tool_to_lines(item, message, messages) end if item.input and type(item.input) == "table" 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) end end if message.acp_tool_call and message.acp_tool_call.content then local content = message.acp_tool_call.content 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) end else if result and result.content then local result_content = result.content 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) end end @@ -454,8 +476,9 @@ end ---@param item AvanteLLMMessageContentItem ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] +---@param expanded boolean | nil ---@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 return text_to_lines(item) elseif type(item) == "table" then @@ -480,7 +503,7 @@ local function message_content_item_to_lines(item, message, messages) 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 return lines end @@ -491,15 +514,16 @@ end ---Converts a message into representation suitable for UI ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] +---@param expanded boolean | nil ---@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 local content = message.message.content if type(content) == "string" then return text_to_lines(content) end if islist(content) then local lines = {} 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) end return lines diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua index f98a1e5..128c293 100644 --- a/lua/avante/sidebar.lua +++ b/lua/avante/sidebar.lua @@ -25,6 +25,7 @@ local logo = require("avante.utils.logo") local RESULT_BUF_NAME = "AVANTE_RESULT" local VIEW_BUFFER_UPDATED_PATTERN = "AvanteViewBufferUpdated" 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 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") @@ -78,6 +79,11 @@ Sidebar.__index = Sidebar ---@field acp_client avante.acp.ACPClient | nil ---@field acp_session_id string | nil ---@field post_render? fun(sidebar: avante.Sidebar) +---@field message_button_handlers table> +---@field expanded_message_uuids table +---@field tool_message_positions table +---@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() function Sidebar:new(id) @@ -110,6 +116,10 @@ function Sidebar:new(id) _cached_history_lines = nil, _history_cache_invalidated = true, post_render = nil, + message_handlers = {}, + tool_message_positions = {}, + expanded_message_ids = {}, + current_tool_use_extmark_id = nil, }, Sidebar) end @@ -146,6 +156,10 @@ function Sidebar:reset() self.scroll = true self.old_result_lines = {} self.token_count = nil + self.message_button_handlers = {} + self.tool_message_positions = {} + self.expanded_message_uuids = {} + self.current_tool_use_extmark_id = nil end ---@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 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 ---@field start_line integer 1-indexed ---@field end_line integer 1-indexed @@ -840,6 +864,13 @@ function Sidebar:retry_user_request() self.handle_submit(block.content) 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() local block = self:get_current_user_request_block() if not block then return end @@ -1066,6 +1097,24 @@ function Sidebar:unbind_retry_user_request_key() 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() if self.containers.result then vim.keymap.set( @@ -1083,6 +1132,50 @@ function Sidebar:unbind_edit_user_request_key() 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) ---@param direction "next" | "prev" local function jump_to_codeblock(direction) @@ -1291,6 +1384,8 @@ function Sidebar:on_mount(opts) group = self.augroup, buffer = self.containers.result.bufnr, callback = function(ev) + self:render_tool_use_control_buttons() + local in_codeblock = is_cursor_in_codeblock(codeblocks) if in_codeblock then @@ -1628,8 +1723,10 @@ function Sidebar:update_content(content, opts) ) local history_lines + local tool_message_positions 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._history_cache_invalidated = false else @@ -1648,7 +1745,10 @@ function Sidebar:update_content(content, opts) self:clear_state() 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 Utils.unlock_buf(bufnr) @@ -1673,6 +1773,7 @@ function Sidebar:update_content(content, opts) vim.schedule(function() self:render_state() + self:render_tool_use_control_buttons() vim.defer_fn(function() vim.cmd("redraw") end, 10) end) @@ -1743,10 +1844,11 @@ end ---@param messages avante.HistoryMessage[] ---@param ctx table ---@param ignore_record_prefix boolean | nil +---@param expanded boolean | nil ---@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 - 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 ctx.selected_filepaths = message.selected_filepaths 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 ctx table ---@param ignore_record_prefix boolean | nil +---@param expanded boolean | nil ---@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 - return _get_message_lines(message, messages, ctx, ignore_record_prefix) + return _get_message_lines(message, messages, ctx, ignore_record_prefix, expanded) end local text_len = 0 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 text_len = #content 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) 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) return lines end ---@param history avante.ChatHistory ---@param ignore_record_prefix boolean | nil ----@return avante.ui.Line[] -function Sidebar.get_history_lines(history, ignore_record_prefix) +---@return avante.ui.Line[] history_lines +---@return table tool_message_positions +function Sidebar:get_history_lines(history, ignore_record_prefix) local history_messages = History.get_history_messages(history) local ctx = {} - ---@type avante.ui.Line[][] - local group = {} + ---@type avante.ui.Line[] + local res = {} + local tool_message_positions = {} + local is_first_user_submission = true 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 message.is_user_submission then table.insert(group, {}) end - local last_item = group[#group] - if last_item == nil then - table.insert(group, {}) - last_item = group[#group] + if message.is_user_submission then + if not is_first_user_submission 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 + is_first_user_submission = false end 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({ { "" } })) end - last_item = vim.list_extend(last_item, lines) - group[#group] = last_item + if History.Helpers.is_tool_use_message(message) then + tool_message_positions[message.uuid] = { #res, #res + #lines } + end + res = vim.list_extend(res, lines) ::continue:: 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({ { "" } })) - return res + return res, tool_message_positions end ---@param message avante.HistoryMessage @@ -2086,6 +2188,10 @@ function Sidebar:new_chat(args, cb) self:reload_chat_history() self.current_state = 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 }) --- goto first line then go to last line vim.schedule(function() diff --git a/lua/avante/types.lua b/lua/avante/types.lua index 05e87f3..0f5d473 100644 --- a/lua/avante/types.lua +++ b/lua/avante/types.lua @@ -118,14 +118,16 @@ vim.g.avante_login = vim.g.avante_login ---@field is_calling boolean | nil ---@field original_content AvanteLLMMessageContent | nil ---@field acp_tool_call? avante.acp.ToolCall ---- +---@field permission_options? avante.acp.PermissionOption[] +---@field is_permission_confirming? boolean + ---@class AvanteLLMToolResult ---@field tool_name string ---@field tool_use_id string ---@field content string ---@field is_error? boolean ---@field is_user_declined? boolean ---- + ---@class AvantePromptOptions: table<[string], string> ---@field system_prompt string ---@field messages AvanteLLMMessage[] diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua index 1497f58..e972b2c 100644 --- a/lua/avante/utils/init.lua +++ b/lua/avante/utils/init.lua @@ -1300,29 +1300,55 @@ end ---@param skip_line_count? integer function M.update_buffer_lines(ns_id, bufnr, old_lines, new_lines, skip_line_count) skip_line_count = skip_line_count or 0 - 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) - --- rmeove 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_) + local diff_start_idx = 0 + for i, line in ipairs(new_lines) do + local old_line = old_lines[i] + if not old_line or old_line ~= line then + diff_start_idx = i + break 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( bufnr, - skip_line_count + diff.start_line - 1, - skip_line_count + diff.end_line - 1, + skip_line_count + diff_start_idx - 1, + skip_line_count + diff_start_idx + #changed_lines, false, - cleaned_lines + text_lines ) - for i, line in ipairs(lines) do - line:set_highlights(ns_id, bufnr, skip_line_count + diff.start_line + i - 2) + for i, line in ipairs(changed_lines) do + line:set_highlights(ns_id, bufnr, skip_line_count + diff_start_idx + i - 2) end - vim.cmd("redraw") 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 function M.uniform_path(path)