folke / noice.nvim

💥 Highly experimental plugin that completely replaces the UI for messages, cmdline and the popupmenu.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

bug: scrolling gets stuck when using mini.animate

aeddi opened this issue · comments

Did you check docs and existing issues?

  • I have read all the noice.nvim docs
  • I have searched the existing issues of noice.nvim
  • I have searched the existing issues of plugins related to this issue

Neovim version (nvim -v)

0.9.5

Operating system/version

macOS 14.0 (23A344)

Describe the bug

If the mini.animate plugin is loaded and the scroll animation is enabled (which is the default config), the hover/signature window scrolling will get stuck if you try to scroll up/down when the window is already displaying the top/bottom of the buffer.

See the attached video.

stuck.mp4

Steps To Reproduce

  1. Create a minimal configuration file based on the one in the documentation.
  2. Install mini.animate plugin and call its setup method without any argument. Add and configure an LSP server (in this example lua_lsp) and a few keymaps to interact with the hover window (see details below).
  3. Display a hover window (by pressing the K key in normal mode in my case) then use require("noice.lsp").scroll with a negative number as argument (by pressing <C-b> in my case). The scrolling is now stuck on this window, you can't scroll down anymore (by pressing <C-f> in my case).
  4. Display a hover window (by pressing the K key in normal mode in my case) then use require("noice.lsp").scroll until the last line of the buffer is displayed (by pressing <C-f> several times in my case) then press <C-f> one more time. The scrolling is now stuck on this window, you can't scroll up anymore (by pressing <C-b> in my case).

Expected Behavior

The hover/signature window should not get stuck.

Repro

local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
  vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, })
end
vim.opt.runtimepath:prepend(lazypath)

-- install plugins
local plugins = {
  "folke/tokyonight.nvim",
  {
    "folke/noice.nvim",
    dependencies = {
      "MunifTanjim/nui.nvim",
      "rcarriga/nvim-notify",
    },
  },
  -- add any other plugins here
  "neovim/nvim-lspconfig",
  "echasnovski/mini.animate",
}
require("lazy").setup(plugins, {
  root = root .. "/plugins",
})

vim.cmd.colorscheme("tokyonight")

-- add anything else here
require("noice").setup()
require("lspconfig").lua_ls.setup({
  settings = {
    Lua = {
      workspace = {
        library = {
          vim.env.VIMRUNTIME,
        },
      },
    },
  },
})
require("mini.animate").setup()

-- keymap for lsp hover interactions
vim.keymap.set("n", "K", vim.lsp.buf.hover)

vim.keymap.set({ "n", "i", "s" }, "<c-f>", function()
  if not require("noice.lsp").scroll(4) then
    return "<c-f>"
  end
end, { silent = true, expr = true })

vim.keymap.set({ "n", "i", "s" }, "<c-b>", function()
  if not require("noice.lsp").scroll(-4) then
    return "<c-b>"
  end
end, { silent = true, expr = true })

After a bit of investigation and debugging on both noice and mini.animate plugins, I found this part of the code to be problematic (although I'm not sure why) :

vim.api.nvim_command("noautocmd silent! normal! " .. top .. "zt")
vim.api.nvim_exec_autocmds("WinScrolled", { modeline = false })

1) When scrolling is not yet stuck, each of these lines emits a WinSrolled event, except that each of them occurs with a different window handle and buffer handle (???).

This can be easily tested by displaying the return of vim.api.nvim_get_current_buf() and vim.api.nvim_get_current_win(), or by adding debugging in the get_scroll_state() function of mini.animate (here).

2) When you try to scroll up/down when you're already at the top/bottom of the buffer, only the second line emits a WinScrolled event.

So if the top value is identical to the vim.fn.getwininfo(win)[1].topline value, the command "noautocmd silent! normal! " .. top .. "zt" emits nothing.

3) It's the same thing once scrolling gets stuck and you try to scroll in the "right" direction (scroll down when on top / scroll up when on bottom): only the second line still emits an event.
The first line emits nothing, even though the top and vim.fn.getwininfo(win)[1].topline values are now different.

Potential FIxes

1) I don't understand the point of the second line, which manually triggers a WinScrolled event, or why the noautocmd on the first line doesn't seem to work.
But by deleting the second line, the scrolling no longer gets stuck and everything seems to work fine.

@@ -246,7 +246,6 @@ function M.scroll(win, delta)
   vim.defer_fn(function()
     vim.api.nvim_buf_call(buf, function()
       vim.api.nvim_command("noautocmd silent! normal! " .. top .. "zt")
-      vim.api.nvim_exec_autocmds("WinScrolled", { modeline = false })
     end)
   end, 0)
 end

2) By skipping the execution of the two lines if the top and vim.fn.getwininfo(win)[1].topline values are identical, once again : the scrolling no longer gets stuck and everything seems to work fine.

@@ -243,12 +243,14 @@ function M.scroll(win, delta)
   top = math.max(top, 1)
   top = math.min(top, M.win_buf_height(win) - info.height + 1)

-  vim.defer_fn(function()
-    vim.api.nvim_buf_call(buf, function()
-      vim.api.nvim_command("noautocmd silent! normal! " .. top .. "zt")
-      vim.api.nvim_exec_autocmds("WinScrolled", { modeline = false })
-    end)
-  end, 0)
+  if info.topline ~= top then
+    vim.defer_fn(function()
+      vim.api.nvim_buf_call(buf, function()
+        vim.api.nvim_command("noautocmd silent! normal! " .. top .. "zt")
+        vim.api.nvim_exec_autocmds("WinScrolled", { modeline = false })
+      end)
+    end, 0)
+  end
 end

 return M