Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inccommand: nvim_buf_set_text causes cursor flickering #30696

Closed
smjonas opened this issue Oct 6, 2024 · 4 comments · Fixed by #31040
Closed

inccommand: nvim_buf_set_text causes cursor flickering #30696

smjonas opened this issue Oct 6, 2024 · 4 comments · Fixed by #31040
Assignees
Labels
api libnvim, Nvim RPC API bug-regression wrong behavior that was introduced in a previous commit (please bisect) display redraw, layout, presentation performance performance, latency, cpu/memory usage
Milestone

Comments

@smjonas
Copy link
Member

smjonas commented Oct 6, 2024

Problem

When typing an argument of a previewable command in command line mode (only on some key presses), the cursor is briefly moved to the end of the text. This only occurs when the buffer text is changed using vim.api.nvim_buf_set_text in the preview callback.

Initially reported here (the recording might be useful): smjonas/inc-rename.nvim#63

Steps to reproduce

  • nvim --clean
  • :e test.lua

Paste the following Lua code:

local function replace_first_line_preview(opts, preview_ns, preview_buf)
  local replacement = opts.args
  vim.api.nvim_buf_set_text(0, 0, 0, 1, -1, { replacement })
  return 2
end

vim.api.nvim_create_user_command(
  "ReplaceFirstLine",
  function() end,
  { nargs = 1, preview = replace_first_line_preview }
)
  • :so %
  • start typing :ReplaceFirstLine a
  • move cursor to the left so that it is no longer at the end of the command argument in the command line
  • type multiple a's (hold the a key if the flicker is not immediately visible)

Observe that while typing some a's, the cursor is briefly moved to the right of the last a before being reset to the current position. position.

Expected behavior

Cursor should not flicker.

Nvim version (nvim -v)

NVIM v0.11.0-dev-911+ga2008118a

Vim (not Nvim) behaves the same?

n/a

Operating system/version

Linux Mint 21.2

Terminal name/version

Kitty 0.21.2

$TERM environment variable

xterm-kitty

Installation

appimage

@smjonas smjonas added the bug issues reporting wrong behavior label Oct 6, 2024
@justinmk justinmk added display redraw, layout, presentation api libnvim, Nvim RPC API labels Oct 6, 2024
@justinmk justinmk changed the title Typing inccommand command that calls nvim_buf_set_text causes cursor flickering inccommand: nvim_buf_set_text causes cursor flickering Oct 6, 2024
@justinmk justinmk added this to the needs-owner milestone Oct 6, 2024
@justinmk justinmk added performance performance, latency, cpu/memory usage channels-rpc channels, RPC, msgpack labels Oct 6, 2024
@justinmk
Copy link
Member

justinmk commented Oct 6, 2024

Flicker caused by non-trivial functions, especially RPC/API (though this case is not RPC), is kind of a general, known topic, but I couldn't find tracking issue.

@zeertzjq
Copy link
Member

zeertzjq commented Oct 7, 2024

This only happens with treesitter because treesitter uses nvim__redraw. Related: #28668

@zeertzjq zeertzjq added bug-regression wrong behavior that was introduced in a previous commit (please bisect) and removed channels-rpc channels, RPC, msgpack bug issues reporting wrong behavior labels Oct 7, 2024
@zeertzjq zeertzjq modified the milestones: needs-owner, 0.10.3 Oct 7, 2024
@zeertzjq
Copy link
Member

zeertzjq commented Oct 7, 2024

Hmm, there is a difference between different options of nvim__redraw regarding how the redraw can be reflected immediately:

  • For range, both update_screen() and ui_flush() are needed to reflect the redraw immediately, so calling ui_flush() without update_screen() won't work (and leads to the flicker here).
  • For cursor, statuscolumn, statusline and winbar, only a ui_flush() is needed to reflect the redraw immediately.

@justinmk justinmk modified the milestones: 0.10.3, backlog Oct 7, 2024
@luukvbaal
Copy link
Member

luukvbaal commented Nov 2, 2024

This only happens with treesitter because treesitter uses nvim__redraw. Related: #28668

Unrelated but I found that it still happens even after vim.treesitter.stop(). This is because stop() does not actually stop the highlighter's on_bytes callback.

The issue also happens when calling anything else that flushes the cursor position at msg_row/col still at the end of the newly changed cmdline. I.e. the following reproduces the flickering also without treesitter enabled:

local function replace_first_line_preview(opts, preview_ns, preview_buf)
  local replacement = opts.args
  vim.api.nvim_buf_set_text(0, 0, 0, 1, -1, { replacement })
  vim.cmd.sleep("1m")
  -- vim.cmd.redrawtabline()
  return 2
end

vim.treesitter.stop()
vim.api.nvim_create_user_command(
  "ReplaceFirstLine",
  function() end,
  { nargs = 1, preview = replace_first_line_preview }
)

Unlikely that any of those commands make sense to call inside the cmdpreview callback but I think it does indicate that a proper solution would be to ensure that the cursor is at the correct position before doing cmdpreview: #31040.

Hmm, there is a difference between different options of nvim__redraw regarding how the redraw can be reflected immediately:

True, do you think that presents a problem intrinsically? Currently nvim__redraw tries to not bother the caller with this and do what's needed implicitly.

For cursor, statuscolumn, statusline and winbar, only a ui_flush() is needed to reflect the redraw immediately.

True except for statuscolumn which does require update_screen().

For range, both update_screen() and ui_flush() are needed to reflect the redraw immediately, so calling ui_flush() without update_screen() won't work (and leads to the flicker here).

Yeah nvim__redraw->range() currently does more than what the old nvim__buf_redraw_range() did, and that the ui_flush() doesn't actually reflect what's intended to be redrawn for the TS changedtree callback (updated highlights). I raised this previously in the original PR:#28101 (comment) (without response). Currently it basically serves as a "flush pressed keys because TS callback may take a long time" (unintended).

We can either keep the ui_flush() for range (which was established to have improved editor responsiveness when added to the TS highlighter path), in which case it should probably also update_screen() (one doesn't make sense without the other as you say although I don't think it's what leads to the flicker per se).

Or we can special case range and omit it the ui_flush() to keep it working like in 0.9 if there is a valid reason for doing so.

Further findings:

On top of the on_bytes callback still being active after vim.treesitter.stop(), it seems entirely redundant in the first place? AFAIU an on_bytes callback should mean that the buffer range is already marked for redraw by change.c. Removing the callback altogether seems to confirm that, which doesn't yield any failed tests. I raised #31041 to do away with it.

So I think calling update_screen() and ui_flush() implicitly for range would make sense: #31042. But only if we get rid of the on_bytes callback as there it would just yield double redraws since the usual pattern is extmark_splice (emits on_bytes) -> changed_() marking the buffer to be redrawn again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api libnvim, Nvim RPC API bug-regression wrong behavior that was introduced in a previous commit (please bisect) display redraw, layout, presentation performance performance, latency, cpu/memory usage
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants