Skip to content

Commit 89576eb

Browse files
committed
Calculate folds for buffers with inline diffs.
1 parent 352419a commit 89576eb

File tree

5 files changed

+96
-12
lines changed

5 files changed

+96
-12
lines changed

lua/diffview/init.lua

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ end
55
local hl = require("diffview.hl")
66
local lazy = require("diffview.lazy")
77

8+
local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule
89
local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser"
910
local config = lazy.require("diffview.config") ---@module "diffview.config"
1011
local lib = lazy.require("diffview.lib") ---@module "diffview.lib"
@@ -75,6 +76,18 @@ function M.init()
7576
M.emit("refresh_files")
7677
end,
7778
})
79+
au("User", {
80+
group = M.augroup,
81+
pattern = "GitSignsUpdate",
82+
callback = function()
83+
local view = lib.get_current_view()
84+
85+
if view and view:instanceof(StandardView.__get()) then
86+
---@cast view StandardView
87+
view.cur_layout:gs_update_folds()
88+
end
89+
end,
90+
})
7891

7992
-- Set up user autocommand emitters
8093
DiffviewGlobal.emitter:on("view_opened", function(_)

lua/diffview/scene/layout.lua

+2
Original file line numberDiff line numberDiff line change
@@ -323,5 +323,7 @@ function Layout:sync_scroll()
323323
end
324324
end
325325

326+
function Layout:gs_update_folds() end
327+
326328
M.Layout = Layout
327329
return M

lua/diffview/scene/layouts/diff_1.lua

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local lazy = require("diffview.lazy")
22
local Layout = require("diffview.scene.layout").Layout
33
local oop = require("diffview.oop")
44

5+
local GitAdapter = lazy.access("diffview.vcs.adapters.git", "GitAdapter") ---@type GitAdapter|LazyModule
56
local Diff3 = lazy.access("diffview.scene.layouts.diff_3", "Diff3") ---@type Diff3|LazyModule
67
local Diff4 = lazy.access("diffview.scene.layouts.diff_4", "Diff4") ---@type Diff4|LazyModule
78
local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule
@@ -149,6 +150,12 @@ function Diff1:to_diff4(layout)
149150
})
150151
end
151152

153+
function Diff1:gs_update_folds()
154+
if self.b:is_file_open() and self.b.file.adapter:instanceof(GitAdapter.__get()) and self.b.file.kind ~= "conflicting" then
155+
self.b:gs_update_folds()
156+
end
157+
end
158+
152159
---FIXME
153160
---@override
154161
---@param rev Rev

lua/diffview/scene/window.lua

+66-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModu
66
local GitAdapter = lazy.access("diffview.vcs.adapters.git", "GitAdapter") ---@type GitAdapter|LazyModule
77
local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule
88
local config = lazy.require("diffview.config") ---@module "diffview.config"
9+
local gs_actions = lazy.require("gitsigns.actions") ---@module "gitsigns.actions"
910
local lib = lazy.require("diffview.lib") ---@module "diffview.lib"
1011
local utils = lazy.require("diffview.utils") ---@module "diffview.utils"
1112

@@ -64,6 +65,12 @@ function Window:is_focused()
6465
return self:is_valid() and api.nvim_get_current_win() == self.id
6566
end
6667

68+
function Window:is_file_open()
69+
return self:is_valid()
70+
and self.file:is_valid()
71+
and api.nvim_win_get_buf(self.id) == self.file.bufnr
72+
end
73+
6774
---@param callback fun(file: vcs.File)
6875
function Window:load_file(callback)
6976
assert(self.file)
@@ -89,17 +96,23 @@ function Window:open_file(callback)
8996
self:_save_winopts()
9097
end
9198

99+
local winopt_overrides
92100
local base_rev = utils.tbl_access(self, "parent.parent.revs.a") --[[@as Rev? ]]
101+
local use_inline_diff = self.file.kind ~= "conflicting"
102+
and self.parent:instanceof(Diff1.__get())
103+
and self.file.adapter:instanceof(GitAdapter.__get())
93104

94-
self:apply_file_winopts()
105+
if use_inline_diff then
106+
winopt_overrides = { foldmethod = "manual", diff = false }
107+
end
108+
109+
self:apply_file_winopts(winopt_overrides)
95110
self.file:attach_buffer(false, {
96111
keymaps = config.get_layout_keymaps(self.parent),
97112
disable_diagnostics = self.file.kind == "conflicting"
98113
and config.get_config().view.merge_tool.disable_diagnostics,
99114
inline_diff = {
100-
enable = self.file.kind ~= "conflicting"
101-
and self.parent:instanceof(Diff1.__get())
102-
and self.file.adapter:instanceof(GitAdapter.__get()),
115+
enabled = use_inline_diff,
103116
base = base_rev and base_rev:object_name(),
104117
}
105118
})
@@ -173,10 +186,15 @@ function Window:_restore_winopts()
173186
end
174187
end
175188

176-
function Window:apply_file_winopts()
189+
---@param overrides WindowOptions?
190+
function Window:apply_file_winopts(overrides)
177191
assert(self.file)
178192
if self.file.winopts then
179-
utils.set_local(self.id, self.file.winopts)
193+
if overrides then
194+
utils.set_local(self.id, vim.tbl_extend("force", self.file.winopts, overrides))
195+
else
196+
utils.set_local(self.id, self.file.winopts)
197+
end
180198
end
181199
end
182200

@@ -194,5 +212,47 @@ function Window:set_file(file)
194212
self.file = file
195213
end
196214

215+
function Window:gs_update_folds()
216+
if self:is_file_open() and vim.wo[self.id].foldenable then
217+
api.nvim_win_call(self.id, function()
218+
pcall(vim.cmd, "norm! zE") -- Delete all folds in window
219+
local hunks = gs_actions.get_hunks(self.file.bufnr) or {}
220+
local context
221+
222+
for _, v in ipairs(vim.opt.diffopt:get()) do
223+
context = tonumber(v:match("^context:(%d+)"))
224+
if context then break end
225+
end
226+
227+
context = math.max(1, context or 6)
228+
229+
local prev_last = -context + 1
230+
local lcount = api.nvim_buf_line_count(self.file.bufnr)
231+
232+
for i = 1, #hunks + 1 do
233+
local hunk = hunks[i]
234+
local first, last
235+
236+
if hunk then
237+
first = hunk.added.start
238+
last = first + hunk.added.count - 1
239+
else
240+
first = lcount + context
241+
last = first
242+
end
243+
244+
-- print(prev_last, first, last, hunk and "hunk" or "nil")
245+
246+
if first - prev_last > context * 2 + 1 then
247+
-- print("FOLD:", prev_last + context, first - context)
248+
vim.cmd(("%d,%dfold"):format(prev_last + context, first - context))
249+
end
250+
251+
prev_last = last
252+
end
253+
end)
254+
end
255+
end
256+
197257
M.Window = Window
198258
return M

lua/diffview/vcs/file.lua

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce
1111

1212
local pl = lazy.access(utils, "path") ---@type PathLib|LazyModule
1313

14-
local gs_refresh = debounce.debounce_trailing(20, false, vim.schedule_wrap(function()
14+
local gs_refresh = debounce.debounce_trailing(20, false, vim.schedule_wrap(function(callback)
1515
gs_actions.refresh()
16+
if vim.is_callable(callback) then callback() end
1617
end))
1718

1819
local api = vim.api
@@ -266,8 +267,9 @@ local function prepare_attach_opt(t1, t2)
266267
keymaps = {},
267268
disable_diagnostics = false,
268269
inline_diff = {
269-
enable = false,
270+
enabled = false,
270271
base = nil --[[@as string? ]],
272+
update = nil --[[@as function? ]],
271273
}
272274
}
273275

@@ -318,7 +320,7 @@ function File:attach_buffer(force, opt)
318320
end
319321

320322
-- Inline diff
321-
if state.inline_diff.enable then
323+
if state.inline_diff.enabled then
322324
local gitsigns = require("gitsigns")
323325
local gs_config = require("gitsigns.config").config
324326
gitsigns.attach(self.bufnr, {
@@ -331,7 +333,7 @@ function File:attach_buffer(force, opt)
331333
gs_config.linehl = true
332334
gs_config.show_deleted = true
333335
gs_config.word_diff = true
334-
gs_refresh()
336+
gs_refresh(state.inline_diff.update)
335337
end
336338

337339
File.attached[self.bufnr] = state
@@ -362,12 +364,12 @@ function File:detach_buffer()
362364
end
363365

364366
-- Inline diff
365-
if state.inline_diff.enable then
367+
if state.inline_diff.enabled then
366368
local gs_config = require("gitsigns.config").config
367369
gs_config.linehl = false
368370
gs_config.show_deleted = false
369371
gs_config.word_diff = false
370-
gs_refresh()
372+
gs_refresh(state.inline_diff.update)
371373
end
372374

373375
File.attached[self.bufnr] = nil

0 commit comments

Comments
 (0)