Skip to content

Commit 3eed8d8

Browse files
authored
some more bug fixes and a minor refactoring (#18)
* fix(git): guard LOCAL stat swap when numstat is unavailable * fix(layout): null COMMIT-side deletions in Diff2 * fix(file-entry): handle should_null errors without nulling files * fix(file): null missing merge stage blobs * fix(config,layouts): validate rename threshold and merge null rules * test(git): cover LOCAL..COMMIT nil-stats binary path * test(matrix): add merge conflict and null logic coverage * refactor(file-entry): extract try_should_null helper
1 parent 817d251 commit 3eed8d8

18 files changed

Lines changed: 491 additions & 10 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ require("diffview").setup({
188188
enhanced_diff_hl = false, -- See |diffview-config-enhanced_diff_hl|
189189
git_cmd = { "git" }, -- The git executable followed by default args.
190190
hg_cmd = { "hg" }, -- The hg executable followed by default args.
191-
rename_threshold = nil, -- Similarity threshold for rename detection (e.g. 40 for 40%). Nil uses git default (50%).
191+
rename_threshold = nil, -- Integer 0-100 for rename detection similarity. Nil uses git default (50%). Invalid values are ignored.
192192
use_icons = true, -- Requires nvim-web-devicons or mini.icons
193193
show_help_hints = true, -- Show hints for how to open the help panel
194194
watch_index = true, -- Update views and index buffers when the git index changes.

doc/diffview.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ rename_threshold *diffview-config-rename_threshol
449449

450450
Similarity threshold for rename detection (e.g. 40 for 40%). When
451451
nil, uses the git default (50%).
452+
Must be an integer in the range [0, 100]. Invalid values are ignored.
452453

453454
use_icons *diffview-config-use_icons*
454455
Type: `boolean`, Default: `true`

doc/diffview_defaults.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ DEFAULT CONFIG *diffview.defaults*
77
enhanced_diff_hl = false, -- See |diffview-config-enhanced_diff_hl|
88
git_cmd = { "git" }, -- The git executable followed by default args.
99
hg_cmd = { "hg" }, -- The hg executable followed by default args.
10-
rename_threshold = nil, -- Similarity threshold for rename detection (e.g. 40 for 40%). Nil uses git default (50%).
10+
rename_threshold = nil, -- Integer 0-100 for rename detection similarity. Nil uses git default (50%). Invalid values are ignored.
1111
use_icons = true, -- Requires nvim-web-devicons or mini.icons
1212
show_help_hints = true, -- Show hints for how to open the help panel
1313
watch_index = true, -- Update views and index buffers when the git index changes.

lua/diffview/config.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,23 @@ function M.setup(user_config)
619619
M._config.git_cmd = M.defaults.git_cmd
620620
end
621621

622+
do
623+
local rename_threshold = M._config.rename_threshold
624+
625+
if rename_threshold ~= nil then
626+
local n = tonumber(rename_threshold)
627+
628+
if not n or n < 0 or n > 100 or n % 1 ~= 0 then
629+
utils.warn(
630+
"Invalid value for 'rename_threshold'. Must be an integer between 0 and 100, or nil."
631+
)
632+
M._config.rename_threshold = M.defaults.rename_threshold
633+
else
634+
M._config.rename_threshold = n
635+
end
636+
end
637+
end
638+
622639
do
623640
-- Validate layouts
624641
local view = M._config.view

lua/diffview/scene/file_entry.lua

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ local M = {}
1414

1515
local fstat_cache = {}
1616

17+
---Safely evaluate a layout's should_null predicate. Returns false if the
18+
---call errors so that a broken predicate never accidentally nulls a file.
19+
---@param layout Layout (class)
20+
---@param rev Rev
21+
---@param status string
22+
---@param symbol string
23+
---@return boolean
24+
local function try_should_null(layout, rev, status, symbol)
25+
local ok, res = pcall(layout.should_null, rev, status, symbol)
26+
return ok and res or false
27+
end
28+
1729
---@class GitStats
1830
---@field additions integer
1931
---@field deletions integer
@@ -123,7 +135,7 @@ function FileEntry:convert_layout(target_layout)
123135
commit = self.commit,
124136
get_data = get_data,
125137
rev = rev,
126-
nulled = select(2, pcall(target_layout.should_null, rev, self.status, symbol)),
138+
nulled = try_should_null(target_layout, rev, self.status, symbol),
127139
}) --[[@as vcs.File ]]
128140
end
129141

@@ -319,7 +331,7 @@ function FileEntry.with_layout(layout_class, opt)
319331
rev = rev,
320332
nulled = utils.sate(
321333
opt.nulled,
322-
select(2, pcall(layout_class.should_null, rev, opt.status, symbol))
334+
try_should_null(layout_class, rev, opt.status, symbol)
323335
),
324336
}) --[[@as vcs.File ]]
325337
end

lua/diffview/scene/layouts/diff_2.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function Diff2.should_null(rev, status, sym)
7474
return vim.tbl_contains({ "?", "A" }, status)
7575
end
7676

77-
return false
77+
return status == "D"
7878

7979
elseif rev.type == RevType.STAGE then
8080
if sym == "a" then

lua/diffview/scene/layouts/diff_3.lua

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,38 @@ function Diff3:to_diff4(layout)
106106
})
107107
end
108108

109-
---FIXME
110109
---@override
111110
---@param rev Rev
112111
---@param status string Git status symbol.
113112
---@param sym Diff3.WindowSymbol
114113
function Diff3.should_null(rev, status, sym)
115-
return false
114+
assert(sym == "a" or sym == "b" or sym == "c")
115+
116+
if rev.type == RevType.LOCAL then
117+
return status == "D"
118+
119+
elseif rev.type == RevType.COMMIT then
120+
if sym == "a" then
121+
return vim.tbl_contains({ "?", "A" }, status)
122+
end
123+
124+
return status == "D"
125+
126+
elseif rev.type == RevType.STAGE then
127+
if rev.stage == 0 then
128+
if sym == "a" then
129+
return vim.tbl_contains({ "?", "A" }, status)
130+
end
131+
132+
return status == "D"
133+
end
134+
135+
-- Merge stages (1..3) can be absent depending on conflict type.
136+
-- This is resolved at file load time by checking stage blob existence.
137+
return false
138+
end
139+
140+
error(("Unexpected state! %s, %s, %s"):format(rev, status, sym))
116141
end
117142

118143
M.Diff3 = Diff3

lua/diffview/scene/layouts/diff_4.lua

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local oop = require("diffview.oop")
66

77
local Diff1 = lazy.access("diffview.scene.layouts.diff_1", "Diff1") ---@type Diff1|LazyModule
88
local Diff3 = lazy.access("diffview.scene.layouts.diff_3", "Diff3") ---@type Diff3|LazyModule
9+
local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule
910

1011
local await = async.await
1112

@@ -103,13 +104,38 @@ function Diff4:to_diff3(layout)
103104
})
104105
end
105106

106-
---FIXME
107107
---@override
108108
---@param rev Rev
109109
---@param status string Git status symbol.
110110
---@param sym Diff4.WindowSymbol
111111
function Diff4.should_null(rev, status, sym)
112-
return false
112+
assert(sym == "a" or sym == "b" or sym == "c" or sym == "d")
113+
114+
if rev.type == RevType.LOCAL then
115+
return status == "D"
116+
117+
elseif rev.type == RevType.COMMIT then
118+
if sym == "a" then
119+
return vim.tbl_contains({ "?", "A" }, status)
120+
end
121+
122+
return status == "D"
123+
124+
elseif rev.type == RevType.STAGE then
125+
if rev.stage == 0 then
126+
if sym == "a" then
127+
return vim.tbl_contains({ "?", "A" }, status)
128+
end
129+
130+
return status == "D"
131+
end
132+
133+
-- Merge stages (1..3) can be absent depending on conflict type.
134+
-- This is resolved at file load time by checking stage blob existence.
135+
return false
136+
end
137+
138+
error(("Unexpected state! %s, %s, %s"):format(rev, status, sym))
113139
end
114140

115141
M.Diff4 = Diff4
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
local config = require("diffview.config")
2+
local utils = require("diffview.utils")
3+
4+
describe("diffview.config", function()
5+
it("validates rename_threshold", function()
6+
local original = vim.deepcopy(config.get_config())
7+
local old_warn = utils.warn
8+
utils.warn = function() end
9+
10+
local ok, err = pcall(function()
11+
config.setup({ rename_threshold = "40" })
12+
assert.equals(40, config.get_config().rename_threshold)
13+
14+
config.setup({ rename_threshold = 101 })
15+
assert.is_nil(config.get_config().rename_threshold)
16+
17+
config.setup({ rename_threshold = 12.5 })
18+
assert.is_nil(config.get_config().rename_threshold)
19+
end)
20+
21+
utils.warn = old_warn
22+
config.setup(original)
23+
24+
if not ok then error(err) end
25+
end)
26+
end)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
local FileEntry = require("diffview.scene.file_entry").FileEntry
2+
local RevType = require("diffview.vcs.rev").RevType
3+
local GitRev = require("diffview.vcs.adapters.git.rev").GitRev
4+
5+
describe("diffview.file_entry", function()
6+
it("does not treat should_null errors as truthy null markers", function()
7+
local captured
8+
local layout_class = setmetatable({
9+
should_null = function()
10+
error("boom")
11+
end,
12+
}, {
13+
__call = function(_, opt)
14+
captured = opt
15+
return {
16+
files = function()
17+
return { opt.b }
18+
end,
19+
}
20+
end,
21+
})
22+
23+
local rev = GitRev(RevType.STAGE, 0)
24+
local adapter = { ctx = { toplevel = vim.uv.cwd() } }
25+
26+
FileEntry.with_layout(layout_class, {
27+
adapter = adapter,
28+
path = "README.md",
29+
status = "M",
30+
kind = "working",
31+
revs = { a = rev, b = rev },
32+
})
33+
34+
assert.is_not_nil(captured)
35+
assert.False(captured.b.nulled)
36+
end)
37+
end)

0 commit comments

Comments
 (0)