Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.18.0
2.19.0
5 changes: 5 additions & 0 deletions lua/codediff/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ M.defaults = {
accept_current = "<leader>co", -- Accept current (ours/right) change
accept_both = "<leader>cb", -- Accept both changes (incoming first)
discard = "<leader>cx", -- Discard both, keep base
-- Accept all (whole file) - uppercase versions like diffview
accept_all_incoming = "<leader>cT", -- Accept ALL incoming changes
accept_all_current = "<leader>cO", -- Accept ALL current changes
accept_all_both = "<leader>cB", -- Accept ALL both changes
discard_all = "<leader>cX", -- Discard ALL, reset to base
next_conflict = "]x", -- Jump to next conflict
prev_conflict = "[x", -- Jump to previous conflict
-- Vimdiff-style numbered diffget (from result buffer)
Expand Down
212 changes: 212 additions & 0 deletions lua/codediff/ui/conflict/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,216 @@ function M.discard(tabpage)
return true
end

--- Accept ALL incoming (left/input1) for all active conflicts
--- @param tabpage number
--- @return boolean success
function M.accept_all_incoming(tabpage)
local session = lifecycle.get_session(tabpage)
if not session then
vim.notify("[codediff] No active session", vim.log.levels.WARN)
return false
end

if not session.conflict_blocks or #session.conflict_blocks == 0 then
vim.notify("[codediff] No conflicts in this session", vim.log.levels.WARN)
return false
end

local result_bufnr = session.result_bufnr
local base_lines = session.result_base_lines
if not result_bufnr or not base_lines then
vim.notify("[codediff] No result buffer or base lines", vim.log.levels.ERROR)
return false
end

local count = 0

-- Process blocks in REVERSE order (bottom-to-top) to avoid line offset issues
-- Wrap in undojoin for atomic undo
vim.api.nvim_buf_call(result_bufnr, function()
for i = #session.conflict_blocks, 1, -1 do
local block = session.conflict_blocks[i]
if tracking.is_block_active(session, block) then
if count > 0 then
pcall(vim.cmd, "undojoin")
end
local incoming_lines = tracking.get_lines_for_range(
session.original_bufnr,
block.output1_range.start_line,
block.output1_range.end_line
)
apply_to_result(result_bufnr, block, incoming_lines, base_lines)
count = count + 1
end
end
end)

signs.refresh_all_conflict_signs(session)
auto_refresh.refresh_result_now(result_bufnr)
vim.notify(string.format("[codediff] Accepted %d incoming change(s)", count), vim.log.levels.INFO)
return count > 0
end

--- Accept ALL current (right/input2) for all active conflicts
--- @param tabpage number
--- @return boolean success
function M.accept_all_current(tabpage)
local session = lifecycle.get_session(tabpage)
if not session then
vim.notify("[codediff] No active session", vim.log.levels.WARN)
return false
end

if not session.conflict_blocks or #session.conflict_blocks == 0 then
vim.notify("[codediff] No conflicts in this session", vim.log.levels.WARN)
return false
end

local result_bufnr = session.result_bufnr
local base_lines = session.result_base_lines
if not result_bufnr or not base_lines then
vim.notify("[codediff] No result buffer or base lines", vim.log.levels.ERROR)
return false
end

local count = 0

vim.api.nvim_buf_call(result_bufnr, function()
for i = #session.conflict_blocks, 1, -1 do
local block = session.conflict_blocks[i]
if tracking.is_block_active(session, block) then
if count > 0 then
pcall(vim.cmd, "undojoin")
end
local current_lines = tracking.get_lines_for_range(
session.modified_bufnr,
block.output2_range.start_line,
block.output2_range.end_line
)
apply_to_result(result_bufnr, block, current_lines, base_lines)
count = count + 1
end
end
end)

signs.refresh_all_conflict_signs(session)
auto_refresh.refresh_result_now(result_bufnr)
vim.notify(string.format("[codediff] Accepted %d current change(s)", count), vim.log.levels.INFO)
return count > 0
end

--- Accept ALL both sides for all active conflicts
--- @param tabpage number
--- @param first_input number|nil Which input comes first (1=incoming, 2=current). Default: 1
--- @return boolean success
function M.accept_all_both(tabpage, first_input)
first_input = first_input or 1

local session = lifecycle.get_session(tabpage)
if not session then
vim.notify("[codediff] No active session", vim.log.levels.WARN)
return false
end

if not session.conflict_blocks or #session.conflict_blocks == 0 then
vim.notify("[codediff] No conflicts in this session", vim.log.levels.WARN)
return false
end

local result_bufnr = session.result_bufnr
local base_lines = session.result_base_lines
if not result_bufnr or not base_lines then
vim.notify("[codediff] No result buffer or base lines", vim.log.levels.ERROR)
return false
end

local count = 0

vim.api.nvim_buf_call(result_bufnr, function()
for i = #session.conflict_blocks, 1, -1 do
local block = session.conflict_blocks[i]
if tracking.is_block_active(session, block) then
if count > 0 then
pcall(vim.cmd, "undojoin")
end

local incoming_lines = tracking.get_lines_for_range(
session.original_bufnr,
block.output1_range.start_line,
block.output1_range.end_line
)
local current_lines = tracking.get_lines_for_range(
session.modified_bufnr,
block.output2_range.start_line,
block.output2_range.end_line
)

-- Combine both sides
local combined
if first_input == 1 then
combined = vim.list_extend(vim.list_extend({}, incoming_lines), current_lines)
else
combined = vim.list_extend(vim.list_extend({}, current_lines), incoming_lines)
end

apply_to_result(result_bufnr, block, combined, base_lines)
count = count + 1
end
end
end)

signs.refresh_all_conflict_signs(session)
auto_refresh.refresh_result_now(result_bufnr)
vim.notify(string.format("[codediff] Accepted %d combined change(s)", count), vim.log.levels.INFO)
return count > 0
end

--- Discard ALL changes (reset all conflicts to base)
--- @param tabpage number
--- @return boolean success
function M.discard_all(tabpage)
local session = lifecycle.get_session(tabpage)
if not session then
vim.notify("[codediff] No active session", vim.log.levels.WARN)
return false
end

if not session.conflict_blocks or #session.conflict_blocks == 0 then
vim.notify("[codediff] No conflicts in this session", vim.log.levels.WARN)
return false
end

local result_bufnr = session.result_bufnr
local base_lines = session.result_base_lines
if not result_bufnr or not base_lines then
vim.notify("[codediff] No result buffer or base lines", vim.log.levels.ERROR)
return false
end

local count = 0

vim.api.nvim_buf_call(result_bufnr, function()
for i = #session.conflict_blocks, 1, -1 do
local block = session.conflict_blocks[i]
-- For discard, we reset even resolved conflicts back to base
if count > 0 then
pcall(vim.cmd, "undojoin")
end

local base_content = {}
for j = block.base_range.start_line, block.base_range.end_line - 1 do
table.insert(base_content, base_lines[j] or "")
end

apply_to_result(result_bufnr, block, base_content, base_lines)
count = count + 1
end
end)

signs.refresh_all_conflict_signs(session)
auto_refresh.refresh_result_now(result_bufnr)
vim.notify(string.format("[codediff] Reset %d conflict(s) to base", count), vim.log.levels.INFO)
return count > 0
end

return M
4 changes: 4 additions & 0 deletions lua/codediff/ui/conflict/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ M.accept_incoming = actions.accept_incoming
M.accept_current = actions.accept_current
M.accept_both = actions.accept_both
M.discard = actions.discard
M.accept_all_incoming = actions.accept_all_incoming
M.accept_all_current = actions.accept_all_current
M.accept_all_both = actions.accept_all_both
M.discard_all = actions.discard_all

-- Delegate to diffget module
M.diffget_incoming = diffget.diffget_incoming
Expand Down
28 changes: 28 additions & 0 deletions lua/codediff/ui/conflict/keymaps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,34 @@ function M.setup_keymaps(tabpage)
)
end

-- Accept ALL incoming
if keymaps.accept_all_incoming then
vim.keymap.set("n", keymaps.accept_all_incoming, function()
actions.accept_all_incoming(tabpage)
end, vim.tbl_extend("force", base_opts, { buffer = bufnr, desc = "Accept ALL incoming changes" }))
end

-- Accept ALL current
if keymaps.accept_all_current then
vim.keymap.set("n", keymaps.accept_all_current, function()
actions.accept_all_current(tabpage)
end, vim.tbl_extend("force", base_opts, { buffer = bufnr, desc = "Accept ALL current changes" }))
end

-- Accept ALL both
if keymaps.accept_all_both then
vim.keymap.set("n", keymaps.accept_all_both, function()
actions.accept_all_both(tabpage)
end, vim.tbl_extend("force", base_opts, { buffer = bufnr, desc = "Accept ALL both changes" }))
end

-- Discard ALL
if keymaps.discard_all then
vim.keymap.set("n", keymaps.discard_all, function()
actions.discard_all(tabpage)
end, vim.tbl_extend("force", base_opts, { buffer = bufnr, desc = "Discard ALL, reset to base" }))
end

-- Navigation
if keymaps.next_conflict then
vim.keymap.set("n", keymaps.next_conflict, function()
Expand Down
Loading