diffview.nvim icon indicating copy to clipboard operation
diffview.nvim copied to clipboard

[Bug] Diffview flashes current window's open buffer when showing file diff or history

Open charbelnicolas opened this issue 1 year ago • 7 comments

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:

ss_2024_06_07_10_47_17

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.

ss_2024_06_07_10_46_35

And it is then that the correct buffer will show:

ss_2024_06_07_10_46_54

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

  1. nvim --clean -u mini.lua
  2. Open a file in a git repository
  3. 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!")

charbelnicolas avatar Jun 07 '24 16:06 charbelnicolas

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).

tmillr avatar Jun 24 '24 18:06 tmillr

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?

charbelnicolas avatar Jun 24 '24 19:06 charbelnicolas

~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'.

tmillr avatar Jun 24 '24 20:06 tmillr

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:

ss_2024_08_22_23_57_01 ss_2024_08_22_23_56_49

Hopefully it will get fixed in the future ;)

charbelnicolas avatar Aug 23 '24 05:08 charbelnicolas

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).

charbelnicolas avatar Nov 13 '24 17:11 charbelnicolas

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 avatar Mar 03 '25 05:03 charbelnicolas

@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

mmirus avatar Sep 17 '25 15:09 mmirus