From c4452903e37bce5bc80d2b582d8ba54fa1edf89b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Wed, 14 Oct 2026 11:43:58 -0400 Subject: fix(api): nvim_buf_call() from expression for window in non-current tabpage #42352 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: - nvim_buf_call() aborts (assert) when called from an expression evaluated for a window in another tabpage ('foldexpr', 'statusline'), because `curwin` is not in `curtab`. - After it returns, `curwin` is wrong if it switched windows. - `vim._with{win=…, keepcwd=true}` does not undo `:lcd` if `win` is in another tabpage. Solution: - Allow `curwin` outside `curtab`, and restore if it was outside `curtab` at `ctx_switch()`. - Find the target window in any tabpage when restoring its dirs. --- test/functional/api/buffer_spec.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 551bc4091f..714bc0f563 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -2619,6 +2619,30 @@ describe('api/buf', function() eq(other_buf, api.nvim_win_get_buf(other_win)) end) + it('restores curwin when called by expr eval for a window in non-current tabpage', function() + local buf1 = api.nvim_get_current_buf() + command('tabnew') + local t2win = api.nvim_get_current_win() + command('tabprevious') + local hidden = api.nvim_create_buf(false, true) + -- The expression for `t2win` makes it curwin, while curtab stays the same. + eq( + { t2win, t2win, t2win }, + exec_lua(function() + local wins = {} + _G.test_stl = function() + for _, buf in ipairs({ 0, buf1, hidden }) do + vim.api.nvim_buf_call(buf, function() end) + table.insert(wins, vim.api.nvim_get_current_win()) + end + return '' + end + vim.api.nvim_eval_statusline('%{v:lua.test_stl()}', { winid = t2win }) + return wins + end) + ) + end) + it('preserves visual-mode, unless the callback ended it', function() -- Same-buffer: Visual survives untouched. command('normal! v') -- cgit