diff --git a/CHANGELOG.md b/CHANGELOG.md index 3159431..a493d68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Added + +- In the merge tool, `g?`, `]x` and `[x` also work from the ours, theirs and base panes + +## [0.2.3] — 2026-10-09 + ### Fixed - A file whose `.gitattributes` sets a clean filter, `ident` or `working-tree-encoding` showed changes `git diff` doesn't, and staging its hunks could stage different content from `git add`. Its worktree side now reads as git would store it diff --git a/lua/differ/git/init.lua b/lua/differ/git/init.lua index 199c86c..e17d15a 100644 --- a/lua/differ/git/init.lua +++ b/lua/differ/git/init.lua @@ -398,19 +398,6 @@ local function untracked_additions(root, relpath) return additions end --- whether `relpath` is currently conflicted ----@param root string ----@param relpath string ----@return boolean -function M.is_conflicted(root, relpath) - for _, p in ipairs(M.conflicted(root)) do - if p == relpath then - return true - end - end - return false -end - -- list changed files for a resolved source (used by the picker/panel). rev.source is -- pure and can't tell a real ref from a typo, so this is the first call that finds -- out: git's stderr rides along, else a typo reads as an empty change set diff --git a/lua/differ/merge/init.lua b/lua/differ/merge/init.lua index 200d208..4c5ee7f 100644 --- a/lua/differ/merge/init.lua +++ b/lua/differ/merge/init.lua @@ -783,8 +783,8 @@ function M.close() end end --- g?: a floating keymap cheatsheet for the merge result buffer, mirroring the diff --- view's. rows read the session's resolved keymaps so a configured lhs shows correctly +-- g?: a floating keymap cheatsheet, mirroring the diff view's. rows read the session's +-- resolved keymaps so a configured lhs shows correctly; an input pane lists only its keys local function show_help() if not session then return @@ -792,18 +792,22 @@ local function show_help() local km = session.keymaps local help = require("differ.ui.help") local fmt, pair = help.fmt, help.pair - local rows = { - { pair(km.next_conflict, km.prev_conflict), "next / previous conflict" }, - { pair(km.next_marker, km.prev_marker), "next / previous marker line" }, - { fmt(km.choose_ours), "take ours" }, - { fmt(km.choose_theirs), "take theirs" }, - { fmt(km.choose_base), "take base" }, - { fmt(km.choose_all), "take both (ours then theirs)" }, - { fmt(km.choose_none), "drop the conflict" }, - { ":w", "write the file (auto-stages once resolved)" }, + local rows = { { pair(km.next_conflict, km.prev_conflict), "next / previous conflict" } } + if vim.api.nvim_get_current_buf() == session.result_buf then + vim.list_extend(rows, { + { pair(km.next_marker, km.prev_marker), "next / previous marker line" }, + { fmt(km.choose_ours), "take ours" }, + { fmt(km.choose_theirs), "take theirs" }, + { fmt(km.choose_base), "take base" }, + { fmt(km.choose_all), "take both (ours then theirs)" }, + { fmt(km.choose_none), "drop the conflict" }, + { ":w", "write the file (auto-stages once resolved)" }, + }) + end + vim.list_extend(rows, { { ":Differ close", "close the merge tool" }, { fmt(km.help), "this help" }, - } + }) -- dismiss on the configured help key too, not just the hardcoded q/ local dismiss = { "q", "" } vim.list_extend(dismiss, type(km.help) == "table" and km.help or { km.help }) @@ -997,6 +1001,16 @@ function lay_out(root, relpath, model, layout) resolve_choice("none") end, "differ: drop the conflict") rb("help", show_help, "differ: keymap help") + -- the input panes are scratch buffers deleted on close, so these need no unbind + for _, buf in ipairs(bufs) do + bind(buf, km.next_conflict, function() + goto_conflict("next") + end, "differ: next conflict") + bind(buf, km.prev_conflict, function() + goto_conflict("prev") + end, "differ: previous conflict") + bind(buf, km.help, show_help, "differ: keymap help") + end -- no close key here: the result buffer is the real file and stays editable, so q is -- left to native macro recording. `:Differ close` ends the session diff --git a/test/nvim/mergetool_spec.lua b/test/nvim/mergetool_spec.lua index 40b77ba..726d9d8 100644 --- a/test/nvim/mergetool_spec.lua +++ b/test/nvim/mergetool_spec.lua @@ -328,6 +328,50 @@ describe(":Differ mergetool", function() vim.api.nvim_win_close(float, true) end) + it("binds conflict nav and g? on the input panes, with a pane-scoped cheatsheet", function() + local root = conflict_repo() + vim.cmd.edit(root .. "/f.txt") + merge.open({}) + local s = merge.current() + + local function callback(buf, desc) + for _, m in ipairs(vim.api.nvim_buf_get_keymap(buf, "n")) do + if m.desc == desc then + return m.callback + end + end + end + for _, buf in ipairs(s.bufs) do + assert.is_not_nil(callback(buf, "differ: next conflict")) + assert.is_not_nil(callback(buf, "differ: previous conflict")) + assert.is_not_nil(callback(buf, "differ: keymap help")) + end + + local inp = s.inputs[1] + vim.api.nvim_set_current_win(inp.win) + vim.api.nvim_win_set_cursor(s.result_win, { 1, 0 }) + callback(s.bufs[1], "differ: next conflict")() + assert.are.equal(s.regions[1].result_start, vim.api.nvim_win_get_cursor(s.result_win)[1]) + assert.are.equal(inp.win, vim.api.nvim_get_current_win()) -- focus stays on the input + + callback(s.bufs[1], "differ: keymap help")() + local float + for _, w in ipairs(vim.api.nvim_list_wins()) do + if vim.api.nvim_win_get_config(w).relative ~= "" then + float = w + end + end + assert.is_not_nil(float) + local txt = table.concat( + vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(float), 0, -1, false), + "\n" + ) + assert.is_true(txt:find("next / previous conflict", 1, true) ~= nil) + assert.is_nil(txt:find("take ours", 1, true)) + assert.is_nil(txt:find("marker line", 1, true)) + vim.api.nvim_win_close(float, true) + end) + it("opts the result buffer out of format-on-save and restores it on close", function() local root = conflict_repo() vim.cmd.edit(root .. "/f.txt")