mirror of
https://github.com/neovim/neovim.git
synced 2026-05-23 21:30:11 +00:00
fix(float): :bufdelete in floatwin may change focus #39858
Problem: (Followup to 54f22a8f01c0feb27a531b52aedf5cdbd5e51b24.) Deleting another buffer from a floatwin could move focus into the holder window and fire BufEnter for the buffer being deleted. Solution: Use switch_win_noblock() instead of buf_jump_open_win() before recursing into do_buffer_ext().
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/eval/window.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
@@ -1462,11 +1463,21 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
||||
}
|
||||
|
||||
close_windows(buf, false);
|
||||
// close_windows keeps the last non-float window. If it still
|
||||
// shows buf, jump there and retry so curbuf gets replaced.
|
||||
if (bufref_valid(&bufref) && buf->b_nwindows > 0) {
|
||||
win_T *holder = buf_jump_open_win(buf);
|
||||
if (holder != NULL && !holder->w_floating) {
|
||||
// close_windows() refuses to close curtab's last non-float window.
|
||||
// If it still shows buf, retry the delete from there.
|
||||
win_T *holder = close_windows(buf, false);
|
||||
if (buf != curbuf && bufref_valid(&bufref) && holder != NULL) {
|
||||
if (curwin->w_floating) {
|
||||
// swap into holder without entering it: caller keeps focus,
|
||||
// BufEnter doesn't fire for the deleted buffer.
|
||||
switchwin_T switchwin;
|
||||
const int rv = switch_win_noblock(&switchwin, holder, curtab, true);
|
||||
assert(rv == OK);
|
||||
(void)rv;
|
||||
do_buffer_ext(action, start, dir, count, flags);
|
||||
restore_win_noblock(&switchwin, true);
|
||||
} else {
|
||||
buf_jump_open_win(buf);
|
||||
return do_buffer_ext(action, start, dir, count, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2593,7 +2593,8 @@ void curwin_init(void)
|
||||
/// Closes all windows for buffer `buf` unless there is only one non-floating window.
|
||||
///
|
||||
/// @param keep_curwin don't close `curwin`
|
||||
void close_windows(buf_T *buf, bool keep_curwin)
|
||||
/// @return the kept holder window, or NULL.
|
||||
win_T *close_windows(buf_T *buf, bool keep_curwin)
|
||||
{
|
||||
RedrawingDisabled++;
|
||||
|
||||
@@ -2646,6 +2647,12 @@ void close_windows(buf_T *buf, bool keep_curwin)
|
||||
|
||||
theend:
|
||||
RedrawingDisabled--;
|
||||
|
||||
// If buf is still shown in the last non-float window, let the caller retry there.
|
||||
if (firstwin->w_buffer == buf && !firstwin->w_floating) {
|
||||
return firstwin;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Check if "win" is the last non-floating window that exists.
|
||||
|
||||
@@ -764,6 +764,7 @@ describe('float window', function()
|
||||
it('if called from floating window with another buffer', function()
|
||||
api.nvim_set_current_win(other_buf_float)
|
||||
api.nvim_buf_delete(old_buf, { force = true })
|
||||
eq(other_buf_float, curwin())
|
||||
end)
|
||||
end)
|
||||
describe('creates an empty buffer when there is only one listed buffer', function()
|
||||
@@ -776,13 +777,11 @@ describe('float window', function()
|
||||
command('set nobuflisted')
|
||||
api.nvim_set_current_win(old_win)
|
||||
end)
|
||||
after_each(function()
|
||||
expect('')
|
||||
eq(2, #api.nvim_list_wins())
|
||||
end)
|
||||
it('if called from non-floating window', function()
|
||||
api.nvim_buf_delete(old_buf, { force = true })
|
||||
eq(old_win, curwin())
|
||||
expect('')
|
||||
eq(2, #api.nvim_list_wins())
|
||||
end)
|
||||
it('if called from floating window with the same buffer', function()
|
||||
api.nvim_set_current_win(same_buf_float)
|
||||
@@ -792,12 +791,36 @@ describe('float window', function()
|
||||
eq(same_buf_float, eval('g:win_leave'))
|
||||
eq(old_win, eval('g:win_enter'))
|
||||
eq(old_win, curwin())
|
||||
expect('')
|
||||
eq(2, #api.nvim_list_wins())
|
||||
end)
|
||||
it('if called from floating window with an unlisted buffer', function()
|
||||
api.nvim_set_current_win(unlisted_buf_float)
|
||||
api.nvim_buf_delete(old_buf, { force = true })
|
||||
eq(unlisted_buf_float, curwin())
|
||||
expect('unlisted')
|
||||
eq('', fn.bufname(api.nvim_win_get_buf(old_win)))
|
||||
eq(false, api.nvim_buf_is_valid(old_buf))
|
||||
eq(2, #api.nvim_list_wins())
|
||||
end)
|
||||
end)
|
||||
it('keeps focus in the floating window #39800', function()
|
||||
api.nvim_open_win(old_buf, false, float_opts)
|
||||
local other_float = api.nvim_open_win(api.nvim_create_buf(true, false), true, float_opts)
|
||||
api.nvim_buf_delete(old_buf, { force = true })
|
||||
eq(other_float, curwin())
|
||||
end)
|
||||
|
||||
it('does not trigger BufEnter for deleted buffer #39800', function()
|
||||
api.nvim_open_win(old_buf, false, float_opts)
|
||||
api.nvim_open_win(api.nvim_create_buf(true, false), true, float_opts)
|
||||
command('let g:abufs = []')
|
||||
command('autocmd BufEnter * call add(g:abufs, +expand("<abuf>"))')
|
||||
api.nvim_buf_delete(old_buf, { force = true })
|
||||
for _, b in ipairs(eval('g:abufs')) do
|
||||
neq(old_buf, b)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
describe('with splits, deleting the last listed buffer creates an empty buffer', function()
|
||||
describe('when a non-floating window has an unlisted buffer', function()
|
||||
|
||||
Reference in New Issue
Block a user