Skip to content

Commit 0867ce5

Browse files
committed
wip
1 parent 48755d2 commit 0867ce5

File tree

5 files changed

+92
-85
lines changed

5 files changed

+92
-85
lines changed

lua/codecompanion/diff/keymaps.lua

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local config = require("codecompanion.config")
22
local log = require("codecompanion.utils.log")
3+
local utils = require("codecompanion.utils")
34

45
local api = vim.api
56

@@ -18,28 +19,19 @@ local function clear_map(keymaps, bufnr)
1819
end
1920
end
2021

21-
---Fire diff decision event
22-
---@param diff_id number
23-
---@param accepted boolean
24-
---@param timeout? boolean
25-
---@return nil
26-
local function notify(diff_id, accepted, timeout)
27-
local event_name = accepted and "CodeCompanionDiffAccepted" or "CodeCompanionDiffRejected"
28-
api.nvim_exec_autocmds("User", {
29-
pattern = event_name,
30-
data = {
31-
diff_id = diff_id,
32-
accepted = accepted,
33-
timeout = timeout or false,
34-
},
35-
})
36-
end
37-
3822
M.always_accept = {
3923
desc = "Always accept changes from this chat buffer",
4024
callback = function(diff_ui)
25+
-- There might be an edge case where a user has made a decision on the diff
26+
-- and the window has then closed, causing `WinClosed` to fire. So we put
27+
-- a guard here to check for this and repeat across all the keymaps.
28+
if diff_ui.resolved then
29+
return
30+
end
31+
diff_ui.resolved = true
32+
4133
log:trace("[Diff] Accepting diff for id=%s", diff_ui.diff_id)
42-
notify(diff_ui.diff_id, true)
34+
utils.fire("DiffAccepted", { id = diff_ui.diff_id })
4335

4436
local approvals = require("codecompanion.interactions.chat.tools.approvals")
4537
approvals:always(diff_ui.chat_bufnr, diff_ui.tool_name)
@@ -55,8 +47,13 @@ M.always_accept = {
5547
M.accept_change = {
5648
desc = "Accept all changes",
5749
callback = function(diff_ui)
50+
if diff_ui.resolved then
51+
return
52+
end
53+
diff_ui.resolved = true
54+
5855
log:trace("[Diff] Accepting diff for id=%s", diff_ui.diff_id)
59-
notify(diff_ui.diff_id, true)
56+
utils.fire("DiffAccepted", { id = diff_ui.diff_id })
6057

6158
local Diff = require("codecompanion.diff")
6259
Diff.clear(diff_ui.diff)
@@ -69,8 +66,13 @@ M.accept_change = {
6966
M.reject_change = {
7067
desc = "Reject all changes",
7168
callback = function(diff_ui)
69+
if diff_ui.resolved then
70+
return
71+
end
72+
diff_ui.resolved = true
73+
7274
log:trace("[Diff] Rejecting diff for id=%s", diff_ui.diff_id)
73-
notify(diff_ui.diff_id, false)
75+
utils.fire("DiffRejected", { id = diff_ui.diff_id })
7476

7577
local Diff = require("codecompanion.diff")
7678
Diff.clear(diff_ui.diff)
@@ -83,8 +85,13 @@ M.reject_change = {
8385
M.close_window = {
8486
desc = "Close window and reject",
8587
callback = function(diff_ui)
88+
if diff_ui.resolved then
89+
return
90+
end
91+
diff_ui.resolved = true
92+
8693
log:trace("[Diff] Closing diff window for id=%s", diff_ui.diff_id)
87-
notify(diff_ui.diff_id, false, true)
94+
utils.fire("DiffRejected", { id = diff_ui.diff_id })
8895

8996
local Diff = require("codecompanion.diff")
9097
Diff.clear(diff_ui.diff)

lua/codecompanion/diff/ui.lua

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ local api = vim.api
77
local M = {}
88

99
---@class CodeCompanion.DiffUI
10+
---@field bufnr number
11+
---@field chat_bufnr? number If the diff has an associated chat buffer, pass in the chat buffer number
12+
---@field current_hunk number The current hunk index (1-based)
1013
---@field diff CC.Diff
1114
---@field diff_id number
1215
---@field hunks number The total number of hunks in the diff
13-
---@field current_hunk number The current hunk index (1-based)
14-
---@field bufnr number
15-
---@field winnr number
16-
---@field chat_bufnr? number If the diff has an associated chat buffer, pass in the chat buffer number
16+
---@field resolved boolean Whether the diff has been resolved (accepted/rejected)
1717
---@field tool_name? string If the diff is associated with a tool, pass in the tool name
18+
---@field winnr number
1819
local DiffUI = {}
1920
DiffUI.__index = DiffUI
2021

@@ -48,7 +49,7 @@ local function show_keymaps(bufnr, opts)
4849

4950
ui_utils.show_buffer_notification(bufnr, {
5051
text = string.format(
51-
"(%d/%d) [%s] Always Accept | [%s] Accept | [%s] Reject | [%s]/[%s] Next/Prev hunks | [q] Close",
52+
"[%d/%d] %s Always Accept | %s Accept | %s Reject | %s/%s Next/Prev hunks | q Close",
5253
opts.current_hunk or 1,
5354
opts.hunks or 1,
5455
always_accept,
@@ -66,6 +67,10 @@ end
6667
---Navigate to next hunk
6768
---@param line number
6869
function DiffUI:next_hunk(line)
70+
if self.hunks == 1 then
71+
return
72+
end
73+
6974
for index, hunk in ipairs(self.diff.hunks) do
7075
local hunk_line = hunk.pos[1] + 1
7176
if hunk_line > line then
@@ -100,6 +105,10 @@ end
100105
---@param line number
101106
---@return nil
102107
function DiffUI:previous_hunk(line)
108+
if self.hunks == 1 then
109+
return
110+
end
111+
103112
for i = #self.diff.hunks, 1, -1 do
104113
local hunk = self.diff.hunks[i]
105114
local hunk_line = hunk.pos[1] + 1
@@ -179,6 +188,8 @@ function M.show(diff, opts)
179188

180189
local cfg = vim.tbl_deep_extend("force", config.display.chat.floating_window, config.display.chat.diff_window or {})
181190

191+
local title = opts.title or get_buf_name(diff.bufnr)
192+
182193
local bufnr, winnr = ui_utils.create_float(diff.to.lines, {
183194
window = {
184195
width = cfg.width,
@@ -189,19 +200,20 @@ function M.show(diff, opts)
189200
col = cfg.col,
190201
filetype = diff.ft or "text",
191202
ignore_keymaps = true,
192-
title = opts.title or get_buf_name(diff.bufnr),
203+
title = " " .. title .. " ",
193204
})
194205

195206
---@type CodeCompanion.DiffUI
196207
local diff_ui = setmetatable({
208+
bufnr = bufnr,
209+
chat_bufnr = opts.chat_bufnr,
210+
current_hunk = 1,
197211
diff = diff,
198212
diff_id = opts.diff_id or math.random(10000000),
199213
hunks = #diff.hunks,
200-
current_hunk = 1,
201-
bufnr = bufnr,
202-
winnr = winnr,
203-
chat_bufnr = opts.chat_bufnr,
204214
tool_name = opts.tool_name,
215+
resolved = false,
216+
winnr = winnr,
205217
}, DiffUI)
206218

207219
-- Apply diff extmarks

lua/codecompanion/helpers.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ function M.show_diff(args)
3939

4040
local diff_ui = require("codecompanion.diff.ui")
4141
return diff_ui.show(diff_obj, {
42-
diff_id = args.diff_id,
4342
chat_bufnr = args.chat_bufnr,
43+
diff_id = args.diff_id,
44+
title = args.title,
4445
tool_name = args.tool_name,
4546
})
4647
end

lua/codecompanion/interactions/chat/tools/builtin/insert_edit_into_file/init.lua

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ local approvals = require("codecompanion.interactions.chat.tools.approvals")
4848
local codecompanion = require("codecompanion")
4949
local config = require("codecompanion.config")
5050
local constants = require("codecompanion.interactions.chat.tools.builtin.insert_edit_into_file.constants")
51-
local diff = require("codecompanion.interactions.chat.helpers.diff")
5251
local helpers = require("codecompanion.interactions.chat.helpers")
5352
local match_selector = require("codecompanion.interactions.chat.tools.builtin.insert_edit_into_file.match_selector")
5453
local strategies = require("codecompanion.interactions.chat.tools.builtin.insert_edit_into_file.strategies")
@@ -151,24 +150,19 @@ local function restore_file_content(path, content, file_info)
151150
end
152151

153152
---Handle user decision for edits with diff approval
154-
---@param opts table Options containing: diff_id, chat_bufnr, display_name, should_diff, success_response, output_handler, bufnr (optional), on_reject (optional for files)
153+
---@param opts table Options containing: diff_id, chat_bufnr, display_name, diff_ui, success_response, output_handler, bufnr (optional), on_reject (optional for files)
155154
---@return any Result of output_handler call
156155
local function handle_user_decision(opts)
157-
local accept = config.interactions.inline.keymaps.accept_change.modes.n
158-
local reject = config.interactions.inline.keymaps.reject_change.modes.n
159-
local is_buffer = opts.bufnr ~= nil
160-
161156
local wait_opts = {
162157
chat_bufnr = opts.chat_bufnr,
163-
notify = config.display.icons.warning
164-
.. (is_buffer and " Waiting for diff approval ..." or " Waiting for decision ..."),
165-
sub_text = fmt("`%s` - Accept edits / `%s` - Reject edits", accept, reject),
158+
notify = config.display.icons.warning .. " Waiting for diff approval ...",
159+
sub_text = "Review changes in the diff window",
166160
}
167161

168162
return wait.for_decision(opts.diff_id, { "CodeCompanionDiffAccepted", "CodeCompanionDiffRejected" }, function(result)
169163
local response
170164
if result.accepted then
171-
if is_buffer then
165+
if opts.bufnr then
172166
pcall(function()
173167
api.nvim_buf_call(opts.bufnr, function()
174168
vim.cmd("silent! w")
@@ -180,10 +174,7 @@ local function handle_user_decision(opts)
180174
return opts.output_handler(response)
181175
else
182176
get_rejection_reason(function(reason)
183-
if is_buffer then
184-
if result.timeout and opts.should_diff and opts.should_diff.reject then
185-
opts.should_diff:reject()
186-
end
177+
if opts.bufnr then
187178
response = error_response(
188179
result.timeout and "User failed to accept the edits in time"
189180
or fmt('User rejected the edits for `%s`, with the reason "%s"', opts.display_name, reason)
@@ -201,9 +192,6 @@ local function handle_user_decision(opts)
201192
)
202193
)
203194
else
204-
if result.timeout and opts.should_diff and opts.should_diff.reject then
205-
opts.should_diff:reject()
206-
end
207195
response = error_response(
208196
result.timeout and "User failed to accept the edits in time"
209197
or fmt('User rejected the edits for `%s`, with the reason "%s"', opts.display_name, reason)
@@ -793,20 +781,31 @@ local function edit_file(action, chat_bufnr, output_handler, opts)
793781
end
794782

795783
local diff_id = math.random(10000000)
796-
local should_diff = diff.create(path, diff_id, {
784+
local from_lines = vim.split(current_content, "\n", { plain = true })
785+
local to_lines = vim.split(dry_run_result.final_content, "\n", { plain = true })
786+
787+
-- Detect filetype from path
788+
local ft = vim.filetype.match({ filename = path }) or "text"
789+
790+
local diff_helpers = require("codecompanion.helpers")
791+
local diff_ui = diff_helpers.show_diff({
792+
from_lines = from_lines,
793+
to_lines = to_lines,
794+
ft = ft,
795+
title = action.filepath,
796+
diff_id = diff_id,
797797
chat_bufnr = chat_bufnr,
798-
original_content = vim.split(current_content, "\n", { plain = true }),
799798
tool_name = "insert_edit_into_file",
800799
})
801800

802801
local final_success = success_response(fmt("Edited `%s` file%s", action.filepath, extract_explanation(action)))
803802

804-
if should_diff and opts.require_confirmation_after then
803+
if opts.require_confirmation_after then
805804
return handle_user_decision({
806805
diff_id = diff_id,
807806
chat_bufnr = chat_bufnr,
808807
display_name = action.filepath,
809-
should_diff = should_diff,
808+
diff_ui = diff_ui,
810809
success_response = final_success,
811810
on_reject = function()
812811
return restore_file_content(path, current_content, file_info)
@@ -900,28 +899,26 @@ local function edit_buffer(bufnr, chat_bufnr, action, output_handler, opts)
900899
return output_handler(success)
901900
end
902901

903-
local should_diff = diff.create(bufnr, diff_id, {
902+
local ft = vim.bo[bufnr].filetype or "text"
903+
904+
local diff_helpers = require("codecompanion.helpers")
905+
local diff_ui = diff_helpers.show_diff({
904906
chat_bufnr = chat_bufnr,
905-
original_content = original_content,
907+
from_lines = original_content,
908+
to_lines = final_lines,
909+
ft = ft,
910+
title = display_name,
911+
diff_id = diff_id,
906912
tool_name = "insert_edit_into_file",
907913
})
908914

909-
local start_line = nil
910-
if dry_run_result.edit_results[1] and dry_run_result.edit_results[1].start_line then
911-
start_line = dry_run_result.edit_results[1].start_line
912-
end
913-
914-
if start_line then
915-
ui_utils.scroll_to_line(bufnr, start_line)
916-
end
917-
918-
if should_diff and opts.require_confirmation_after then
915+
if opts.require_confirmation_after then
919916
return handle_user_decision({
920917
diff_id = diff_id,
921918
chat_bufnr = chat_bufnr,
922919
display_name = display_name,
923920
bufnr = bufnr,
924-
should_diff = should_diff,
921+
diff_ui = diff_ui,
925922
success_response = success,
926923
output_handler = output_handler,
927924
})

lua/codecompanion/utils/diff_test.lua

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,17 @@ def process_data(data, max_items=100):
8686
name = "Line additions",
8787
filetype = "lua",
8888
before = [[
89-
local M = {}
90-
91-
function M.setup()
92-
print("setup")
93-
end
94-
95-
return M
89+
return {
90+
"CodeCompanion is amazing - Oli Morris"
91+
}
9692
]],
9793
after = [[
98-
local M = {}
99-
100-
function M.setup()
101-
print("setup")
102-
vim.notify("initialized")
103-
end
104-
105-
function M.teardown()
106-
print("teardown")
107-
end
108-
109-
return M
94+
return {
95+
"CodeCompanion is amazing - Oli Morris"
96+
"Lua and Neovim are amazing too - Oli Morris"
97+
"Happy coding!"
98+
"Hello world"
99+
}
110100
]],
111101
},
112102
{

0 commit comments

Comments
 (0)