diffview.nvim
diffview.nvim copied to clipboard
[Bug] Diffview flashes current window's open buffer when showing file diff or history
Description
I don't think I had noticed this in the previous months but now it's really hard to unsee. When opening a Diffview window (either DiffviewOpen or DiffviewFileHistory), unless it's the very first time opening it (it is possible then to fix and not flash a buffer!), it will flash the current focused window buffer on both Diffview editor windows.
Let's assume I have this window open:
After opening a Diffview window once, If I open Diffview again, every consequent DiffviewOpen will flash the current focused buffer first instead of showing the right contents.
And it is then that the correct buffer will show:
Here's a video:
https://drive.google.com/file/d/11-etwvc3RU5f5bLy2_YVllcG0jYz5CY3/view?usp=sharing
Expected behavior
I expect to transition correctly into the desired Diffview output instead of seeing a flash of my current focused window buffer.
Actual behavior
I see a flash of buffer content that is not correct.
Steps to reproduce
- nvim --clean -u mini.lua
- Open a file in a git repository
- Do DiffviewOpen at least twice (first always comes clean)
Health check
diffview: require("diffview.health").check()
Checking plugin dependencies ~
- WARNING Optional dependency 'nvim-web-devicons' not found.
Checking VCS tools ~
- The plugin requires at least one of the supported VCS tools to be valid.
- OK Git found.
- OK Git is up-to-date. (2.45.2)
- WARNING Configured `hg_cmd` is not executable: 'hg'
Log info
Relevant info from :DiffviewLog
############################
### PUT LOG CONTENT HERE ###
############################
Neovim version
NVIM v0.11.0-dev-196+g2ce4a4d91e
Build type: Release
LuaJIT 2.1.1716656478
Operating system and version
Linux 6.9.3-arch1-1 x86_64 GNU/Linux
Minimal config
-- #######################################
-- ### USAGE: nvim --clean -u mini.lua ###
-- #######################################
local root = vim.fn.stdpath("run") .. "/nvim/diffview.nvim"
local plugin_dir = root .. "/plugins"
vim.fn.mkdir(plugin_dir, "p")
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
local plugins = {
{ "nvim-web-devicons", url = "https://github.com/nvim-tree/nvim-web-devicons.git" },
{ "diffview.nvim", url = "https://github.com/sindrets/diffview.nvim.git" },
-- ##################################################################
-- ### ADD PLUGINS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE ###
-- ##################################################################
}
for _, spec in ipairs(plugins) do
local install_path = plugin_dir .. "/" .. spec[1]
if vim.fn.isdirectory(install_path) ~= 1 then
if spec.url then
print(string.format("Installing '%s'...", spec[1]))
vim.fn.system({ "git", "clone", "--depth=1", spec.url, install_path })
end
end
vim.opt.runtimepath:append(spec.path or install_path)
end
require("diffview").setup({
-- ##############################################################################
-- ### ADD DIFFVIEW.NVIM CONFIG THAT IS _NECESSARY_ FOR REPRODUCING THE ISSUE ###
-- ##############################################################################
})
vim.opt.termguicolors = true
vim.cmd("colorscheme " .. (vim.fn.has("nvim-0.8") == 1 and "habamax" or "slate"))
-- ############################################################################
-- ### ADD INIT.LUA SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE ###
-- ############################################################################
print("Ready!")
I could be wrong, but based on some debugging I did some time ago, I think this is just how neovim operates when you open a new tab (i.e. it more or less functions similarly to :split, except instead of copying the current win to a new win it copies the current tab to a new tab). Only after that (and after a redraw cycle apparently) does it change the windows of the new tab. So idk if there's a workaround or not (you'd probably need a way to open a new tab and configure it without entering it yet) and this is ultimately an issue with neovim (unless this plugin is the one forcing the aforementioned redraw).
I could be wrong, but based on some debugging I did some time ago, I think this is just how neovim operates when you open a new tab (i.e. it more or less functions similarly to
:split, except instead of copying the current win to a new win it copies the current tab to a new tab). Only after that (and after a redraw cycle apparently) does it change the windows of the new tab. So idk if there's a workaround or not (you'd probably need a way to open a new tab and configure it without entering it yet) and this is ultimately an issue with neovim (unless this plugin is the one forcing the aforementioned redraw).
It could be... but how come the very first time it opens it does so cleanly without flashing anything?
~On my end it does the same thing the first time~ (or maybe not, maybe I just have bad eyes lol). But you're right, doing A and B with tabnew existingfile, it seems that that doesn't have the flashing issue.
Maybe it's just from setting up the multiple windows after tabnew? Or maybe the plugin is simply forcing a :redraw (or, vim.schedule(), async func) somewhere upon loading a new tab (and then updating the view after), that can create flashes.
There's actually a vim option to slow down redrawing I believe (for debugging). That might help give some answers/insight into what is going on to cause the flashes. Check out 'redrawdebug' or 'lazyredraw'.
So today I was going through the diffview code and I noticed that there were these FIXME comments added after a refactor, so maybe they have something to do with it:
Hopefully it will get fixed in the future ;)
Hello @sindrets, I know working for free isn't fun, so please let me know if there is a one time sponsored payment I could make to fix this bug. I'm willing to spend up to $30 USD (no offense).
A simple fix suggested by Grok is to modify the file lua/diffview/scene/views/standard/standard_view.lua with this:
self.cur_layout.pivot_producer = function()
local was_open = self.panel:is_open()
self.panel:close()
-- Create a scratch buffer
local scratch_buf = api.nvim_create_buf(false, true) -- false: not listed, true: scratch
-- Open a new vertical split and set the scratch buffer
vim.cmd("aboveleft vsp")
local pivot = api.nvim_get_current_win()
api.nvim_win_set_buf(pivot, scratch_buf)
-- Restore panel state if it was open
if was_open then
self.panel:open()
end
return pivot
end
I don't know if it is the correct way to do it, but it works...
@charbelnicolas I think you only need to modify the original function to add two lines:
self.cur_layout.pivot_producer = function()
local was_open = self.panel:is_open()
local was_only_win = was_open and #utils.tabpage_list_normal_wins(self.tabpage) == 1
self.panel:close()
-- If the panel was the only window before closing, then a temp window was
-- already created by `Panel:close()`.
if not was_only_win then
vim.cmd("1windo aboveleft vsp")
end
local pivot = api.nvim_get_current_win()
-- ⭐ these two lines are new:
local scratch_buf = api.nvim_create_buf(false, true)
api.nvim_win_set_buf(pivot, scratch_buf)
if was_open then
self.panel:open()
end
return pivot
end