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
26 changes: 26 additions & 0 deletions doc/diffview.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ COMMANDS *diffview-commands*
• `dx`: Choose none of the versions of the conflict (delete the
conflict region).

For operating on the merged file as a whole (useful when the merge
driver did not emit conflict markers, e.g., modify/delete conflicts
or jj with `merge-tool-edits-conflict-markers = false`):
• `<leader>cso`: Replace the merged buffer with the entire OURS
side.
• `<leader>cst`: Replace the merged buffer with the entire THEIRS
side.
• `<leader>csb`: Replace the merged buffer with the entire BASE
side.

For more info on these actions, see
|diffview-actions-conflict_choose|.

Expand Down Expand Up @@ -1717,6 +1727,22 @@ conflict_choose_all({version}) *diffview-actions-conflict_choos

Parameters: ~
{version} `'ours'|'theirs'|'base'|'all'|'none'`
See |diffview-conflict-versions| to tell what versions of the
file these names correspond with.

conflict_choose_side({version}) *diffview-actions-conflict_choose_side*
Contexts: `merge_tool`, `file_panel`

Replace the entire merged buffer with the content of one whole side
(OURS, THEIRS, or BASE). Unlike |diffview-actions-conflict_choose_all|,
this does not parse conflict markers; it overwrites the buffer with
the side's lines verbatim. Useful when the merge driver did not emit
conflict markers (e.g., modify/delete conflicts, or jj with
`merge-tool-edits-conflict-markers = false`); accepting a side whose
path was empty effectively accepts the delete.

Parameters: ~
{version} `'ours'|'theirs'|'base'`
See |diffview-conflict-versions| to tell what versions of the
file these names correspond with.

Expand Down
50 changes: 50 additions & 0 deletions lua/diffview/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ local pl = lazy.access(utils, "path") --[[@as PathLib ]]
---@field unstage_all fun()
---@field conflict_choose fun(target: DiffviewConflictTarget): fun()
---@field conflict_choose_all fun(target: DiffviewConflictTarget): AsyncFunc
---@field conflict_choose_side fun(target: DiffviewConflictSideTarget): AsyncFunc
---@field cycle_layout fun()
---@field diff_against_default_branch fun()
---@field diffget fun(target: DiffviewDiffgetTarget): fun()
Expand Down Expand Up @@ -766,6 +767,55 @@ function M.conflict_choose(target)
end, "merge_only")
end

---Replace the entire MERGED buffer with the content of the OURS, THEIRS, or
---BASE side. Unlike `conflict_choose_all`, this does not parse conflict
---markers; it overwrites the whole buffer with the side's lines. Useful when
---the merge driver did not emit markers (e.g., modify/delete conflicts, or
---jj with `merge-tool-edits-conflict-markers = false`).
---@param target DiffviewConflictSideTarget
---@return AsyncFunc
function M.conflict_choose_side(target)
return tag(
async.void(function()
local view = lib.get_current_view() --[[@as DiffView ]]

if not (view and view:instanceof(DiffView.__get())) then
return
end
---@cast view DiffView

if view.panel:is_focused() then
local item = view:infer_cur_file(false) ---@cast item -DirData
if not item then
return
end

if not item.active then
await(view:set_file(item))
end
end

local main, bufnr = get_valid_main(view)
if not main then
return
end
---@cast bufnr integer

local src_bufnr = diff_copy_target(target)
if not (src_bufnr and api.nvim_buf_is_valid(src_bufnr)) then
return
end

local src_lines = api.nvim_buf_get_lines(src_bufnr, 0, -1, false)
api.nvim_buf_set_lines(bufnr, 0, -1, false, src_lines)

utils.set_cursor(main.id, 1, 0)
view.cur_layout:sync_scroll()
end),
"merge_only"
)
end

---@param target DiffviewDiffgetTarget
---@return fun()
function M.diffget(target)
Expand Down
4 changes: 4 additions & 0 deletions lua/diffview/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ end

-- Targets consumed by action factories in `actions.lua` (referenced from keymaps).
---@alias DiffviewConflictTarget "ours"|"theirs"|"base"|"all"|"none"
---@alias DiffviewConflictSideTarget "ours"|"theirs"|"base"
---@alias DiffviewDiffgetTarget "ours"|"theirs"|"base"|"local"

---@class DiffviewKeymapOpts
Expand Down Expand Up @@ -120,6 +121,9 @@ local conflict_keymaps = {
{ "n", "<leader>cB", actions.conflict_choose_all("base"), { desc = "Choose the BASE version of a conflict for the whole file" } },
{ "n", "<leader>cA", actions.conflict_choose_all("all"), { desc = "Choose all the versions of a conflict for the whole file" } },
{ "n", "dX", actions.conflict_choose_all("none"), { desc = "Delete the conflict region for the whole file" } },
{ "n", "<leader>cso", actions.conflict_choose_side("ours"), { desc = "Replace the MERGED buffer with the entire OURS side" } },
{ "n", "<leader>cst", actions.conflict_choose_side("theirs"), { desc = "Replace the MERGED buffer with the entire THEIRS side" } },
{ "n", "<leader>csb", actions.conflict_choose_side("base"), { desc = "Replace the MERGED buffer with the entire BASE side" } },
}

---@class DiffviewConfig
Expand Down
6 changes: 6 additions & 0 deletions lua/diffview/tests/functional/actions_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ describe("diffview.actions._is_applicable", function()
assert.is_true(actions._is_applicable(fn, make_view({ merge_ctx = true })))
end)

it("propagates `merge_only` tag through `conflict_choose_side` factory", function()
local fn = actions.conflict_choose_side("ours")
assert.is_false(actions._is_applicable(fn, make_view()))
assert.is_true(actions._is_applicable(fn, make_view({ merge_ctx = true })))
end)

it("returns false when view is nil and the action is `merge_only`", function()
assert.is_false(actions._is_applicable(actions.next_conflict, nil))
end)
Expand Down
Loading