From 9578167e7776ce9882f1800bf0a9b71f469d1db7 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Sat, 13 Sep 2025 14:08:07 +0200 Subject: [PATCH] feat(diff): apply all code blocks for a file at once when showing diff Refactored diff preview logic to process all code blocks for a file in one pass, improving consistency and correctness. Updated diff utility functions to accept lines instead of buffer numbers, and adjusted context lengths for more accurate region detection. Signed-off-by: Tomas Slusny --- lua/CopilotChat/config/mappings.lua | 28 ++++++++++++++++++++++++---- lua/CopilotChat/utils/diff.lua | 23 +++++++++++------------ tests/diff_spec.lua | 2 +- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/lua/CopilotChat/config/mappings.lua b/lua/CopilotChat/config/mappings.lua index 05122044..74429e33 100644 --- a/lua/CopilotChat/config/mappings.lua +++ b/lua/CopilotChat/config/mappings.lua @@ -172,9 +172,10 @@ return { local path = block.header.filename local bufnr = prepare_diff_buffer(path, source) - local new_lines = diff.apply_diff(block, bufnr) + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local new_lines = diff.apply_diff(block, lines) vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, new_lines) - local first, last = diff.get_diff_region(block, bufnr) + local first, last = diff.get_diff_region(block, lines) if first and last then select.set(bufnr, source.winnr, first, last) select.highlight(bufnr) @@ -192,7 +193,8 @@ return { local path = block.header.filename local bufnr = prepare_diff_buffer(path, source) - local first, last = diff.get_diff_region(block, bufnr) + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local first, last = diff.get_diff_region(block, lines) if first and last and bufnr then select.set(bufnr, source.winnr, first, last) select.highlight(bufnr) @@ -223,7 +225,25 @@ return { local path = block.header.filename local bufnr = prepare_diff_buffer(path, source) - local new_lines = diff.apply_diff(block, bufnr) + + -- Collect all blocks for the same filename + local message = copilot.chat:get_message(constants.ROLE.ASSISTANT, true) + local blocks = {} + if message and message.section and message.section.blocks then + for _, b in ipairs(message.section.blocks) do + if b.header.filename == path then + table.insert(blocks, b) + end + end + else + blocks = { block } + end + + -- Apply all diffs for the filename + local new_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + for i = #blocks, 1, -1 do + new_lines = diff.apply_diff(blocks[i], new_lines) + end local opts = { filetype = vim.bo[bufnr].filetype, diff --git a/lua/CopilotChat/utils/diff.lua b/lua/CopilotChat/utils/diff.lua index 7e862c54..6ff56bac 100644 --- a/lua/CopilotChat/utils/diff.lua +++ b/lua/CopilotChat/utils/diff.lua @@ -120,8 +120,8 @@ function M.apply_unified_diff(diff_text, original_content) new_content = patched applied = applied or ok end - local original_lines = vim.split(original_content, '\n') - local new_lines = vim.split(new_content, '\n') + local original_lines = vim.split(original_content, '\n', { trimempty = true }) + local new_lines = vim.split(new_content, '\n', { trimempty = true }) local first, last local max_len = math.max(#original_lines, #new_lines) for i = 1, max_len do @@ -137,10 +137,9 @@ end --- Get diff from block content and buffer lines ---@param block CopilotChat.ui.chat.Block Block containing diff info ----@param bufnr integer Buffer number +---@param lines table table of lines ---@return string diff, string content -function M.get_diff(block, bufnr) - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) +function M.get_diff(block, lines) local content = table.concat(lines, '\n') if block.header.filetype == 'diff' then return block.content, content @@ -161,7 +160,7 @@ function M.get_diff(block, bufnr) vim.diff( table.concat(original_lines, '\n'), table.concat(patched_lines, '\n'), - { algorithm = 'myers', ctxlen = 20, interhunkctxlen = 50, ignore_whitespace_change = true } + { algorithm = 'myers', ctxlen = 10, interhunkctxlen = 10, ignore_whitespace_change = true } ) ), content @@ -169,10 +168,10 @@ end --- Apply a diff (unified or indices) to buffer lines ---@param block CopilotChat.ui.chat.Block Block containing diff info ----@param bufnr integer Buffer number +---@param lines table table of lines ---@return table new_lines -function M.apply_diff(block, bufnr) - local diff, content = M.get_diff(block, bufnr) +function M.apply_diff(block, lines) + local diff, content = M.get_diff(block, lines) local new_lines, applied, _, _ = M.apply_unified_diff(diff, content) if not applied then log.debug('Diff for ' .. block.header.filename .. ' failed to apply cleanly for:\n' .. diff) @@ -183,10 +182,10 @@ end --- Get changed region for diff (unified or indices) ---@param block CopilotChat.ui.chat.Block Block containing diff info ----@param bufnr integer Buffer number +---@param lines table table of lines ---@return number? first, number? last -function M.get_diff_region(block, bufnr) - local diff, content = M.get_diff(block, bufnr) +function M.get_diff_region(block, lines) + local diff, content = M.get_diff(block, lines) local _, _, first, last = M.apply_unified_diff(diff, content) return first, last end diff --git a/tests/diff_spec.lua b/tests/diff_spec.lua index cdea4c1b..58e2f4c9 100644 --- a/tests/diff_spec.lua +++ b/tests/diff_spec.lua @@ -179,7 +179,7 @@ describe('CopilotChat.utils.diff', function() local original_content = table.concat(original, '\n') local result, applied = diff.apply_unified_diff(diff_text, original_content) assert.is_true(applied) - assert.are.same({ '' }, result) + assert.are.same({}, result) end) it('applies unified diff with all lines added to empty file', function()