fix(marks): don't use spell decorations from other lines (#39441)

Spell decorations from other lines aren't relevant to the current line.
Also, decor_redraw_col() can only go forward, while spell navigation
needs to go both forward and backward.
This commit is contained in:
zeertzjq
2026-04-28 07:00:23 +08:00
committed by GitHub
parent 3411c95828
commit 46c83ce321
2 changed files with 123 additions and 7 deletions

View File

@@ -1255,15 +1255,10 @@ bool no_spell_checking(win_T *wp)
return false;
}
static void decor_spell_nav_start(win_T *wp)
{
decor_state = (DecorState){ 0 };
decor_redraw_reset(wp, &decor_state);
}
static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col)
{
if (*decor_lnum != lnum) {
decor_redraw_reset(wp, &decor_state);
decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1);
decor_redraw_line(wp, lnum - 1, &decor_state);
*decor_lnum = lnum;
@@ -1331,7 +1326,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a
// temporary DecorState.
DecorState saved_decor_start = decor_state;
linenr_T decor_lnum = -1;
decor_spell_nav_start(wp);
decor_state = (DecorState){ 0 };
while (!got_int) {
char *line = ml_get_buf(wp->w_buffer, lnum);

View File

@@ -455,4 +455,125 @@ describe("'spell'", function()
|
]])
end)
local function test_spell_false_nav(ephemeral)
screen:try_resize(50, 8)
insert('Splel\nSplle\nSepll\nSpele\nSpeel')
feed('gg0')
exec('set shortmess+=s spell spelloptions=noplainbuffer')
n.exec_lua(function()
local ns = vim.api.nvim_create_namespace('spell')
if ephemeral then
local decors = {} --- @type [integer,integer,vim.api.keyset.set_extmark][]
local function on_do()
for _, decor in ipairs(decors) do
vim.api.nvim_buf_set_extmark(
0,
ns,
decor[1],
decor[2],
vim.tbl_deep_extend('error', decor[3], { ephemeral = true })
)
end
end
vim.api.nvim_set_decoration_provider(ns, {
on_win = on_do,
_on_spell_nav = on_do,
})
function _G.Update(new_decors)
decors = new_decors
vim.cmd('redraw!')
end
else
--- @param decors [integer,integer,vim.api.keyset.set_extmark][]
function _G.Update(decors)
vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
for _, decor in ipairs(decors) do
vim.api.nvim_buf_set_extmark(0, ns, decor[1], decor[2], decor[3])
end
vim.cmd('redraw!')
end
end
end)
n.exec_lua(function()
_G.Update({
{ 0, 0, { end_row = 5, spell = true, priority = 0 } },
{ 2, 0, { end_row = 3, spell = false, priority = 1 } },
})
end)
screen:expect([[
{1:^Splel} |
{1:Splle} |
Sepll |
{1:Spele} |
{1:Speel} |
{0:~ }|*2
|
]])
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 2, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 4, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 5, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 5, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 4, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 2, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
n.exec_lua(function()
_G.Update({
{ 0, 0, { end_row = 5, spell = true, priority = 0 } },
{ 3, 0, { end_row = 5, spell = false, priority = 1 } },
{ 3, 0, { end_row = 4, spell = true, priority = 2 } },
})
end)
screen:expect([[
{1:^Splel} |
{1:Splle} |
{1:Sepll} |
{1:Spele} |
Speel |
{0:~ }|*2
|
]])
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 2, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 3, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 4, 0 }, api.nvim_win_get_cursor(0))
feed(']s')
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 4, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 3, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 2, 0 }, api.nvim_win_get_cursor(0))
feed('[s')
t.eq({ 1, 0 }, api.nvim_win_get_cursor(0))
end
describe('spell=false decoration on line with spelling mistake #39441', function()
it('using ephemeral decorations', function()
test_spell_false_nav(true)
end)
it('using non-ephemeral extmarks', function()
test_spell_false_nav(false)
end)
end)
end)