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.
This commit is contained in:
Jaehwang Jung 2023-12-25 02:31:47 +09:00 committed by Lewis Russell
parent 9b90657376
commit dc48a98f9a
5 changed files with 12 additions and 18 deletions

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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;
}

View File

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