fix(window): use real last buffer of closed window

Problem: close_buffer autocmds may switch buffers at the last moment when
closing a window, causing terminal_check_size to prefer the size of a closed
window, or TabClosed to set an old <abuf>.

Solution: use the actual last buffer, similar to what TabClosed did before.

NOTE: If buffer was unloaded/deleted (not wiped), then TabClosed's <abuf> may
not use it. (w_buffer = NULL) Maybe odd, but it's how it worked before anyhow.

Relies on close_buffer reliably setting w_buffer to NULL if freed, otherwise
buf_valid is better. Only concern I see is if the window wasn't in the window
list after closing the buffer (close_buffer won't set it to NULL then), but then
win_close{_othertab} should've returned earlier.
This commit is contained in:
Sean Dewar
2026-02-07 17:02:54 +00:00
parent d945a93d69
commit da8de99d0b
3 changed files with 71 additions and 0 deletions

View File

@@ -2976,6 +2976,10 @@ int win_close(win_T *win, bool free_buf, bool force)
}
}
// About to free the window. Remember its final buffer for terminal_check_size,
// which may have changed since the last set_bufref. (e.g: close_buffer autocmds)
set_bufref(&bufref, win->w_buffer);
// Free the memory used for the window and get the window that received
// the screen space.
int dir;
@@ -3262,6 +3266,10 @@ bool win_close_othertab(win_T *win, int free_buf, tabpage_T *tp, bool force)
}
}
// About to free the window. Remember its final buffer for terminal_check_size/TabClosed,
// which may have changed since the last set_bufref. (e.g: close_buffer autocmds)
set_bufref(&bufref, win->w_buffer);
// Free the memory used for the window.
int dir;
win_free_mem(win, &dir, tp);

View File

@@ -80,6 +80,20 @@ describe('TabClosed', function()
]])
eq(true, eval('nvim_buf_is_valid(g:buf)'))
eq(eval('g:buf'), tonumber(eval('g:abuf')))
exec([[
tabnew
let s:win = win_getid()
tabfirst
let g:buf = nvim_create_buf(1, 1)
au BufHidden * ++once call nvim_win_set_buf(s:win, g:buf)
au TabClosed * ++once let g:abuf = expand('<abuf>')
call nvim_win_close(s:win, 1)
]])
-- BufHidden switched buffers at the last moment; TabClosed's <abuf> should show that.
eq(eval('g:buf'), tonumber(eval('g:abuf')))
end)
end)

View File

@@ -430,6 +430,8 @@ describe(':terminal window', function()
[1] = { reverse = true },
[2] = { background = 225, foreground = Screen.colors.Gray0 },
[3] = { bold = true },
[4] = { foreground = 12 },
[5] = { reverse = true, bold = true },
[17] = { background = 2, foreground = Screen.colors.Grey0 },
[18] = { background = 2, foreground = 8 },
[19] = { underline = true, foreground = Screen.colors.Grey0, background = 7 },
@@ -517,6 +519,53 @@ describe(':terminal window', function()
{17:foo [-] }{18:foo [-] }|
{3:-- TERMINAL --} |
]])
-- Sizing logic should only consider the final buffer shown in a window, even if autocommands
-- changed it at the last moment.
exec_lua(function()
vim.g.fired = 0
vim.api.nvim_create_autocmd('BufHidden', {
callback = function(ev)
vim.api.nvim_win_set_buf(vim.fn.win_findbuf(ev.buf)[1], vim.fn.bufnr('foo'))
vim.g.fired = vim.g.fired + 1
return vim.g.fired == 2
end,
})
end)
command('botright new')
screen:expect([[
rows: 2, cols: 25 │rows: 5, cols: 50 |
│rows: 2, cols: 50 |
{18:foo [-] foo [-] }|
^ |
{4:~ }|
{5:[No Name] }|
|
]])
command('quit')
eq(1, eval('g:fired'))
screen:expect([[
rows: 5, cols: 50 │rows: 5, cols: 25 |
rows: 5, cols: 25 │rows: 5, cols: 50 |
rows: 2, cols: 25 │rows: 2, cols: 50 |
rows: 5, cols: 25 │rows: 5, cols: 25 |
^ │rows: 5, cols: 40 |
{17:foo [-] }{18:foo [-] }|
|
]])
-- Check it doesn't use the size of the closed window in the other tab page; size should only
-- change via the :wincmd below. Hide tabline so it doesn't affect sizes.
command('set showtabline=0 | tabnew | tabprevious | wincmd > | tabonly')
eq(2, eval('g:fired'))
screen:expect([[
rows: 5, cols: 25 │rows: 5, cols: 25 |
rows: 2, cols: 25 │rows: 5, cols: 50 |
rows: 5, cols: 25 │rows: 2, cols: 50 |
rows: 5, cols: 26 │rows: 5, cols: 25 |
^ │rows: 5, cols: 40 |
{17:foo [-] }{18:foo [-] }|
|
]])
end)
it('restores window options when switching terminals', function()