From 01c15ee60aeecbec22a63c9f210b6806effd9bc5 Mon Sep 17 00:00:00 2001 From: David Yonge-Mallo Date: Wed, 8 Jul 2026 07:53:53 +0200 Subject: [PATCH] feat(merge)!: honour `view.merge_tool.layout` / `view.default.layout` in `DiffviewMergeFiles` and `DiffviewDiffDirs` `DiffviewMergeFiles ` on the default config now yields a 3-way view. Set `view.merge_tool.layout = "diff4_mixed"` to keep the 4-way rendering. --- .../scene/views/diff/file_dir_diff_view.lua | 14 ++++--- .../scene/views/diff/file_merge_view.lua | 15 +++---- .../tests/functional/file_merge_view_spec.lua | 39 ++++++++++++++++++- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/lua/diffview/scene/views/diff/file_dir_diff_view.lua b/lua/diffview/scene/views/diff/file_dir_diff_view.lua index 288f9ce3..c6984e2a 100644 --- a/lua/diffview/scene/views/diff/file_dir_diff_view.lua +++ b/lua/diffview/scene/views/diff/file_dir_diff_view.lua @@ -1,14 +1,14 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") -local Diff2Hor = lazy.access("diffview.scene.layouts.diff_2_hor", "Diff2Hor") ---@type Diff2Hor|LazyModule -local Diff3Hor = lazy.access("diffview.scene.layouts.diff_3_hor", "Diff3Hor") ---@type Diff3Hor|LazyModule +local Diff4Mixed = lazy.access("diffview.scene.layouts.diff_4_mixed", "Diff4Mixed") ---@type Diff4Mixed|LazyModule local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local NullDiffView = lazy.access("diffview.scene.views.diff.null_diff_view", "NullDiffView") ---@type NullDiffView|LazyModule local NullRev = lazy.access("diffview.vcs.adapters.null.rev", "NullRev") ---@type NullRev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule +local View = lazy.access("diffview.scene.view", "View") ---@type View|LazyModule local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local fmt = string.format @@ -214,12 +214,16 @@ function FileDirDiffView:_build_entry(rel, status, a_rev, b_rev, c_rev) b_file = make_file(b_abs, b_rev, nil, nil) end - local layout + local layout_class if three_pane then - layout = Diff3Hor.__get()({ a = a_file, b = b_file, c = c_file }) + layout_class = View.__get().get_default_merge_layout() + if layout_class == Diff4Mixed.__get() then + layout_class = View.__get().get_default_diff3() + end else - layout = Diff2Hor.__get()({ a = a_file, b = b_file }) + layout_class = View.__get().get_default_layout() end + local layout = layout_class({ a = a_file, b = b_file, c = c_file }) return FileEntry({ adapter = self.adapter, diff --git a/lua/diffview/scene/views/diff/file_merge_view.lua b/lua/diffview/scene/views/diff/file_merge_view.lua index ff4034c9..48c21d8d 100644 --- a/lua/diffview/scene/views/diff/file_merge_view.lua +++ b/lua/diffview/scene/views/diff/file_merge_view.lua @@ -1,13 +1,13 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") -local Diff3Hor = lazy.access("diffview.scene.layouts.diff_3_hor", "Diff3Hor") ---@type Diff3Hor|LazyModule local Diff4Mixed = lazy.access("diffview.scene.layouts.diff_4_mixed", "Diff4Mixed") ---@type Diff4Mixed|LazyModule local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local NullDiffView = lazy.access("diffview.scene.views.diff.null_diff_view", "NullDiffView") ---@type NullDiffView|LazyModule local NullRev = lazy.access("diffview.vcs.adapters.null.rev", "NullRev") ---@type NullRev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule +local View = lazy.access("diffview.scene.view", "View") ---@type View|LazyModule local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local fmt = string.format @@ -80,14 +80,15 @@ function FileMergeView:init(opt) local a_file = make_file(self.left_path, left_rev, "OURS (Current changes)", self.left_path) local b_file = make_file(self.output_path, output_rev, "MERGED (Output)", nil) local c_file = make_file(self.right_path, right_rev, "THEIRS (Incoming changes)", self.right_path) + local d_file = self.base_path + and make_file(self.base_path, base_rev, "BASE (Common ancestor)", self.base_path) + or nil - local layout - if self.base_path then - local d_file = make_file(self.base_path, base_rev, "BASE (Common ancestor)", self.base_path) - layout = Diff4Mixed.__get()({ a = a_file, b = b_file, c = c_file, d = d_file }) - else - layout = Diff3Hor.__get()({ a = a_file, b = b_file, c = c_file }) + local layout_class = View.__get().get_default_merge_layout() + if not d_file and layout_class == Diff4Mixed.__get() then + layout_class = View.__get().get_default_diff3() end + local layout = layout_class({ a = a_file, b = b_file, c = c_file, d = d_file }) local entry = FileEntry({ adapter = self.adapter, diff --git a/lua/diffview/tests/functional/file_merge_view_spec.lua b/lua/diffview/tests/functional/file_merge_view_spec.lua index 94dc2319..9a0139d8 100644 --- a/lua/diffview/tests/functional/file_merge_view_spec.lua +++ b/lua/diffview/tests/functional/file_merge_view_spec.lua @@ -1,4 +1,5 @@ local actions = require("diffview.actions") +local config = require("diffview.config") local helpers = require("diffview.tests.helpers") local lib = require("diffview.lib") local Diff3Hor = require("diffview.scene.layouts.diff_3_hor").Diff3Hor @@ -65,7 +66,8 @@ describe("diffview.scene.views.diff.file_merge_view", function() assert.True(view.files.conflicting[1].layout:instanceof(Diff3Hor)) end) - it("uses Diff4Mixed when base is provided", function() + it("uses Diff4Mixed when base is provided and config selects Diff4Mixed", function() + config.setup({ view = { merge_tool = { layout = "diff4_mixed" } } }) local adapter = NullAdapter.create({ toplevel = "/tmp" }) local view = FileMergeView({ adapter = adapter, @@ -76,6 +78,39 @@ describe("diffview.scene.views.diff.file_merge_view", function() }) assert.True(view.files.conflicting[1].layout:instanceof(Diff4Mixed)) + config.setup({}) + end) + + it("downgrades a configured Diff4Mixed to Diff3 default when no base is provided", function() + config.setup({ view = { merge_tool = { layout = "diff4_mixed" } } }) + local Diff3 = require("diffview.scene.layouts.diff_3").Diff3 + local adapter = NullAdapter.create({ toplevel = "/tmp" }) + local view = FileMergeView({ + adapter = adapter, + output_path = output_path, + left_path = left_path, + right_path = right_path, + }) + + assert.False(view.files.conflicting[1].layout:instanceof(Diff4Mixed)) + assert.True(view.files.conflicting[1].layout:instanceof(Diff3)) + config.setup({}) + end) + + it("honours a Diff3 config even when base is provided (base is dropped)", function() + config.setup({ view = { merge_tool = { layout = "diff3_mixed" } } }) + local Diff3Mixed = require("diffview.scene.layouts.diff_3_mixed").Diff3Mixed + local adapter = NullAdapter.create({ toplevel = "/tmp" }) + local view = FileMergeView({ + adapter = adapter, + output_path = output_path, + base_path = base_path, + left_path = left_path, + right_path = right_path, + }) + + assert.True(view.files.conflicting[1].layout:instanceof(Diff3Mixed)) + config.setup({}) end) it("populates conflicting (not working) with a single entry", function() @@ -126,6 +161,7 @@ describe("diffview.scene.views.diff.file_merge_view", function() end) it("binds each window's file to the matching on-disk path", function() + config.setup({ view = { merge_tool = { layout = "diff4_mixed" } } }) local adapter = NullAdapter.create({ toplevel = "/tmp" }) local view = FileMergeView({ adapter = adapter, @@ -140,6 +176,7 @@ describe("diffview.scene.views.diff.file_merge_view", function() eq(output_path, layout.b.file.path) -- OUTPUT (editable) eq(right_path, layout.c.file.path) -- THEIRS eq(base_path, layout.d.file.path) -- BASE + config.setup({}) end) it("populates `merge_ctx` so `merge_only` keymaps show in the help panel", function()