aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaehwang Jung <tomtomjhj@gmail.com>2023-12-25 02:31:47 +0900
committerLewis Russell <me@lewisr.dev>2024-01-04 11:37:00 +0000
commitdc48a98f9ac614dc94739637c967aa29e064807e (patch)
treeb53348803c82a132b51077eca3200f3aa36cc513
parent9b90657376754a492d19f1daaffb257bf87d09cf (diff)
downloadrneovim-dc48a98f9ac614dc94739637c967aa29e064807e.tar.gz
rneovim-dc48a98f9ac614dc94739637c967aa29e064807e.tar.bz2
rneovim-dc48a98f9ac614dc94739637c967aa29e064807e.zip
fix(decorations): validate botline for on_win
Problem: Many decoration providers (treesitter injection highlighting, semantic token highlighting, inlay hint) rely on the correctness of the `botline` argument of `on_win` callback. However, `botline` can be smaller than the actual line number of the last displayed line if some lines are folded. In such cases, some decorations will be missing in the lines not covered by `botline`. Solution: Validate `botline` when invoking `on_win`. NOTE: It seems that the old code was deliberately avoiding this presumably due to performance reasons. However, I haven't experienced noticeable lag after this change, and I believe the cost of botline computation would be much smaller than the cost of decoration providers.
-rw-r--r--runtime/doc/api.txt4
-rw-r--r--runtime/lua/vim/_meta/api.lua4
-rw-r--r--src/nvim/api/extmark.c6
-rw-r--r--src/nvim/decoration_provider.c10
-rw-r--r--test/functional/ui/decorations_spec.lua6
5 files changed, 12 insertions, 18 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index e3063b0591..07e4473ac2 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -2841,9 +2841,7 @@ nvim_set_decoration_provider({ns_id}, {*opts})
• on_buf: called for each buffer being redrawn (before window
callbacks) ["buf", bufnr, tick]
• on_win: called when starting to redraw a specific window.
- botline_guess is an approximation that does not exceed the
- last line number. ["win", winid, bufnr, topline,
- botline_guess]
+ ["win", winid, bufnr, topline, botline]
• on_line: called for each buffer line being redrawn. (The
interaction with fold lines is subject to change) ["win",
winid, bufnr, row]
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index c0dfa7635b..02128b66b9 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -1761,9 +1761,7 @@ function vim.api.nvim_set_current_win(window) end
--- • on_buf: called for each buffer being redrawn (before window
--- callbacks) ["buf", bufnr, tick]
--- • on_win: called when starting to redraw a specific window.
---- botline_guess is an approximation that does not exceed the
---- last line number. ["win", winid, bufnr, topline,
---- botline_guess]
+--- ["win", winid, bufnr, topline, botline]
--- • on_line: called for each buffer line being redrawn. (The
--- interaction with fold lines is subject to change) ["win",
--- winid, bufnr, row]
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 0cf16294b6..e66e25e527 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -1009,10 +1009,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// - on_buf: called for each buffer being redrawn (before
/// window callbacks)
/// ["buf", bufnr, tick]
-/// - on_win: called when starting to redraw a
-/// specific window. botline_guess is an approximation
-/// that does not exceed the last line number.
-/// ["win", winid, bufnr, topline, botline_guess]
+/// - on_win: called when starting to redraw a specific window.
+/// ["win", winid, bufnr, topline, botline]
/// - on_line: called for each buffer line being redrawn.
/// (The interaction with fold lines is subject to change)
/// ["win", winid, bufnr, row]
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 6a7ecf102d..9bb499fe4b 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -15,6 +15,7 @@
#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/message.h"
+#include "nvim/move.h"
#include "nvim/pos_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -121,10 +122,9 @@ void decor_providers_invoke_win(win_T *wp)
// then we would need decor_state.running_decor_provider just like "on_line" below
assert(kv_size(decor_state.active) == 0);
- linenr_T knownmax = MIN(wp->w_buffer->b_ml.ml_line_count,
- ((wp->w_valid & VALID_BOTLINE)
- ? wp->w_botline
- : MAX(wp->w_topline + wp->w_height_inner, wp->w_botline)));
+ if (kv_size(decor_providers) > 0) {
+ validate_botline(wp);
+ }
for (size_t i = 0; i < kv_size(decor_providers); i++) {
DecorProvider *p = &kv_A(decor_providers, i);
@@ -138,7 +138,7 @@ void decor_providers_invoke_win(win_T *wp)
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
// TODO(bfredl): we are not using this, but should be first drawn line?
ADD_C(args, INTEGER_OBJ(wp->w_topline - 1));
- ADD_C(args, INTEGER_OBJ(knownmax - 1));
+ ADD_C(args, INTEGER_OBJ(wp->w_botline - 1 - 1));
if (!decor_provider_invoke((int)i, "win", p->redraw_win, args, true)) {
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
}
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index d78d7d06cc..48df6b3295 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -615,9 +615,9 @@ describe('decorations providers', function()
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
]])
setup_provider([[
- local function on_do(kind, winid, bufnr, topline, botline_guess)
+ local function on_do(kind, winid, bufnr, topline, botline)
if kind == 'win' then
- if topline < 100 and botline_guess > 100 then
+ if topline < 100 and botline > 100 then
api.nvim_buf_set_extmark(bufnr, ns1, 99, -1, { sign_text = 'X' })
else
api.nvim_buf_clear_namespace(bufnr, ns1, 0, -1)
@@ -655,7 +655,7 @@ describe('decorations providers', function()
eok = true
]])
setup_provider([[
- local function on_do(kind, winid, bufnr, topline, botline_guess)
+ local function on_do(kind, winid, bufnr, topline, botline)
if kind == 'line' then
api.nvim_buf_set_extmark(bufnr, ns1, 1, -1, { sign_text = 'X' })
eok = pcall(api.nvim_buf_clear_namespace, bufnr, ns1, 0, -1)