From be4e7cfd6a6eb772c223bbb21b0aa4610b64ef9d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 6 Jun 2026 06:26:12 -0400 Subject: [PATCH] =?UTF-8?q?fix(vim.hl):=20range(0,=E2=80=A6)=20highlight?= =?UTF-8?q?=20not=20cleared=20after=20buffer-switch=20#40130?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: When `vim.hl.range(0, …, { timeout = N })` is called, the deferred `range_hl_clear` captures `buf=0`, which resolves to an arbitrary "current buffer" at timeout. This may cause a stale highlight that never gets cleared. Solution: Resolve `buf=0` explicitly, before `range_hl_clear` captures it. (cherry picked from commit ec7dab077bf6bf53794f2bc42010c977687094a6) --- runtime/lua/vim/hl.lua | 2 ++ test/functional/lua/hl_spec.lua | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua index 3a3e3b4edb..b1d08fa811 100644 --- a/runtime/lua/vim/hl.lua +++ b/runtime/lua/vim/hl.lua @@ -49,6 +49,8 @@ M.priorities = { --- @return fun()? range_clear A function which allows clearing the highlight manually. --- nil is returned if timeout is not specified function M.range(buf, ns, higroup, start, finish, opts) + -- Resolve buf=0 so the deferred `range_hl_clear` works correctly even after buffer-switch. + buf = vim._resolve_bufnr(buf) opts = opts or {} local regtype = opts.regtype or 'v' local inclusive = opts.inclusive or false diff --git a/test/functional/lua/hl_spec.lua b/test/functional/lua/hl_spec.lua index a63b004456..ab9dda99cb 100644 --- a/test/functional/lua/hl_spec.lua +++ b/test/functional/lua/hl_spec.lua @@ -113,7 +113,7 @@ describe('vim.hl.range', function() ]]) end) - it('removes highlight after given `timeout`', function() + it('removes highlight after `timeout`', function() local timeout = 300 exec_lua(function() local ns = vim.api.nvim_create_namespace('') @@ -130,6 +130,13 @@ describe('vim.hl.range', function() ]], timeout = timeout / 3, }) + -- The deferred `range_hl_clear` works even if a different buffer was switched-to. + exec_lua(function() + local orig = vim.api.nvim_get_current_buf() + vim.api.nvim_set_current_buf(vim.api.nvim_create_buf(false, true)) + vim.wait(timeout * 2) + vim.api.nvim_set_current_buf(orig) + end) screen:expect([[ ^asdfghjkl{1:$} | «口=口»{1:$} | @@ -180,7 +187,7 @@ describe('vim.hl.range', function() ]]) end) - it('allows cancelling a highlight that has not timed out', function() + it('allows cancelling a highlight before timeout', function() exec_lua(function() local timeout = 3000 local range_timer @@ -220,7 +227,7 @@ describe('vim.hl.on_yank', function() clear() end) - it('does not show errors even if buffer is wiped before timeout', function() + it('no errors even if buffer is wiped before timeout', function() command('new') n.feed('ifoo') -- set '[, '] exec_lua(function() @@ -265,7 +272,7 @@ describe('vim.hl.on_yank', function() end) end) - it('removes old highlight if new one is created before old one times out', function() + it('removes old highlight if new one is created before old timeout', function() command('vnew') exec_lua(function() vim.api.nvim_buf_set_mark(0, '[', 1, 1, {})