fix(float): do not switch window before deleting last listed buffer (#17840)

Just allow close_windows() to close the current window instead.
This fixes wrong working directory or autocommands not being triggered.
This commit is contained in:
zeertzjq
2022-03-24 14:53:20 +08:00
committed by GitHub
parent ff82b2785f
commit a72f338d76
2 changed files with 74 additions and 53 deletions

View File

@@ -1042,24 +1042,24 @@ static int empty_curbuf(int close_others, int forceit, int action)
set_bufref(&bufref, buf); set_bufref(&bufref, buf);
if (close_others) { if (close_others) {
bool can_close_all_others = true;
if (curwin->w_floating) { if (curwin->w_floating) {
bool can_close_all_others = false; // Closing all other windows with this buffer may leave only floating windows.
can_close_all_others = false;
for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) { for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) {
if (wp->w_buffer != curbuf) { if (wp->w_buffer != curbuf) {
// Found another non-floating window with a different (probably unlisted) buffer. // Found another non-floating window with a different (probably unlisted) buffer.
// Closing all other windows with the this buffer is fine in this case. // Closing all other windows with this buffer is fine in this case.
can_close_all_others = true; can_close_all_others = true;
break; break;
} }
} }
if (!can_close_all_others) {
// Closing all other windows with this buffer will close all non-floating windows.
// Move to a non-floating window then.
curwin = firstwin;
}
} }
// Close any other windows on this buffer, then make it empty. // If it is fine to close all other windows with this buffer, keep the current window and
close_windows(buf, true); // close any other windows with this buffer, then make it empty.
// Otherwise close_windows() will refuse to close the last non-floating window, so allow it
// to close the current window instead.
close_windows(buf, can_close_all_others);
} }
setpcmark(); setpcmark();

View File

@@ -17,6 +17,7 @@ local funcs = helpers.funcs
local run = helpers.run local run = helpers.run
local pcall_err = helpers.pcall_err local pcall_err = helpers.pcall_err
local tbl_contains = global_helpers.tbl_contains local tbl_contains = global_helpers.tbl_contains
local curbuf, curwin, curtab = helpers.curbuf, helpers.curwin, helpers.curtab
describe('float window', function() describe('float window', function()
before_each(function() before_each(function()
@@ -423,8 +424,8 @@ describe('float window', function()
local old_buf, old_win local old_buf, old_win
before_each(function() before_each(function()
insert('foo') insert('foo')
old_buf = meths.get_current_buf() old_buf = curbuf().id
old_win = meths.get_current_win() old_win = curwin().id
end) end)
describe('closing the last non-floating window gives E444', function() describe('closing the last non-floating window gives E444', function()
before_each(function() before_each(function()
@@ -442,44 +443,53 @@ describe('float window', function()
end) end)
describe("deleting the last non-floating window's buffer", function() describe("deleting the last non-floating window's buffer", function()
describe('leaves one window with an empty buffer when there is only one buffer', function() describe('leaves one window with an empty buffer when there is only one buffer', function()
local same_buf_float
before_each(function() before_each(function()
meths.open_win(old_buf, true, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
end) end)
after_each(function() after_each(function()
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
expect('') expect('')
eq(1, #meths.list_wins()) eq(1, #meths.list_wins())
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.set_current_win(old_win)
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
end) end)
it('if called from floating window', function() it('if called from floating window', function()
meths.set_current_win(same_buf_float)
command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(same_buf_float, eval('g:win_leave'))
eq(old_win, eval('g:win_enter'))
end) end)
end) end)
describe('closes other windows with that buffer when there are other buffers', function() describe('closes other windows with that buffer when there are other buffers', function()
local same_buf_float, other_buf, other_buf_float local same_buf_float, other_buf, other_buf_float
before_each(function() before_each(function()
same_buf_float = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
other_buf = meths.create_buf(true, false) other_buf = meths.create_buf(true, false).id
other_buf_float = meths.open_win(other_buf, true, float_opts) other_buf_float = meths.open_win(other_buf, true, float_opts).id
insert('bar') insert('bar')
meths.set_current_win(old_win) meths.set_current_win(old_win)
end) end)
after_each(function() after_each(function()
eq(other_buf, meths.get_current_buf()) eq(other_buf, curbuf().id)
expect('bar') expect('bar')
eq(2, #meths.list_wins()) eq(2, #meths.list_wins())
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
end) end)
it('if called from floating window with the same buffer', function() it('if called from floating window with the same buffer', function()
meths.set_current_win(same_buf_float) meths.set_current_win(same_buf_float)
command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(same_buf_float, eval('g:win_leave'))
eq(old_win, eval('g:win_enter'))
eq(old_win, curwin().id)
end) end)
-- TODO: this case is too hard to deal with -- TODO: this case is too hard to deal with
pending('if called from floating window with another buffer', function() pending('if called from floating window with another buffer', function()
@@ -490,9 +500,9 @@ describe('float window', function()
describe('creates an empty buffer when there is only one listed buffer', function() describe('creates an empty buffer when there is only one listed buffer', function()
local same_buf_float, unlisted_buf_float local same_buf_float, unlisted_buf_float
before_each(function() before_each(function()
same_buf_float = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
local unlisted_buf = meths.create_buf(true, false) local unlisted_buf = meths.create_buf(true, false).id
unlisted_buf_float = meths.open_win(unlisted_buf, true, float_opts) unlisted_buf_float = meths.open_win(unlisted_buf, true, float_opts).id
insert('unlisted') insert('unlisted')
command('set nobuflisted') command('set nobuflisted')
meths.set_current_win(old_win) meths.set_current_win(old_win)
@@ -503,12 +513,16 @@ describe('float window', function()
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
end) end)
it('if called from floating window with the same buffer', function() it('if called from floating window with the same buffer', function()
meths.set_current_win(same_buf_float) meths.set_current_win(same_buf_float)
command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(same_buf_float, eval('g:win_leave'))
eq(old_win, eval('g:win_enter'))
eq(old_win, curwin().id)
end) end)
-- TODO: this case is too hard to deal with -- TODO: this case is too hard to deal with
pending('if called from floating window with an unlisted buffer', function() pending('if called from floating window with an unlisted buffer', function()
@@ -525,7 +539,7 @@ describe('float window', function()
insert('unlisted') insert('unlisted')
command('set nobuflisted') command('set nobuflisted')
meths.set_current_win(old_win) meths.set_current_win(old_win)
same_buf_float = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
end) end)
after_each(function() after_each(function()
expect('') expect('')
@@ -533,12 +547,12 @@ describe('float window', function()
end) end)
it('if called from non-floating window with the deleted buffer', function() it('if called from non-floating window with the deleted buffer', function()
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
end) end)
it('if called from floating window with the deleted buffer', function() it('if called from floating window with the deleted buffer', function()
meths.set_current_win(same_buf_float) meths.set_current_win(same_buf_float)
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(same_buf_float, meths.get_current_win()) eq(same_buf_float, curwin().id)
end) end)
end) end)
end) end)
@@ -550,17 +564,17 @@ describe('float window', function()
before_each(function() before_each(function()
insert('unlisted') insert('unlisted')
command('set nobuflisted') command('set nobuflisted')
unlisted_buf = meths.get_current_buf() unlisted_buf = curbuf().id
command('tabnew') command('tabnew')
insert('foo') insert('foo')
old_buf = meths.get_current_buf() old_buf = curbuf().id
old_win = meths.get_current_win() old_win = curwin().id
end) end)
describe('without splits, deleting the last listed buffer creates an empty buffer', function() describe('without splits, deleting the last listed buffer creates an empty buffer', function()
local same_buf_float local same_buf_float
before_each(function() before_each(function()
meths.set_current_win(old_win) meths.set_current_win(old_win)
same_buf_float = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
end) end)
after_each(function() after_each(function()
expect('') expect('')
@@ -569,12 +583,16 @@ describe('float window', function()
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
end) end)
it('if called from floating window with the same buffer', function() it('if called from floating window with the same buffer', function()
meths.set_current_win(same_buf_float) meths.set_current_win(same_buf_float)
command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()')
command('autocmd WinEnter * let g:win_enter = nvim_get_current_win()')
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(same_buf_float, eval('g:win_leave'))
eq(old_win, eval('g:win_enter'))
eq(old_win, curwin().id)
end) end)
end) end)
describe('with splits, deleting the last listed buffer creates an empty buffer', function() describe('with splits, deleting the last listed buffer creates an empty buffer', function()
@@ -583,7 +601,7 @@ describe('float window', function()
command('botright vsplit') command('botright vsplit')
meths.set_current_buf(unlisted_buf) meths.set_current_buf(unlisted_buf)
meths.set_current_win(old_win) meths.set_current_win(old_win)
same_buf_float = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
end) end)
after_each(function() after_each(function()
expect('') expect('')
@@ -592,12 +610,12 @@ describe('float window', function()
end) end)
it('if called from non-floating window with the deleted buffer', function() it('if called from non-floating window with the deleted buffer', function()
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(old_win, meths.get_current_win()) eq(old_win, curwin().id)
end) end)
it('if called from floating window with the deleted buffer', function() it('if called from floating window with the deleted buffer', function()
meths.set_current_win(same_buf_float) meths.set_current_win(same_buf_float)
meths.buf_delete(old_buf, {force = true}) meths.buf_delete(old_buf, {force = true})
eq(same_buf_float, meths.get_current_win()) eq(same_buf_float, curwin().id)
end) end)
end) end)
end) end)
@@ -606,43 +624,46 @@ describe('float window', function()
local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1} local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
local old_tabpage, old_buf, old_win local old_tabpage, old_buf, old_win
before_each(function() before_each(function()
old_tabpage = meths.get_current_tabpage() old_tabpage = curtab().id
insert('oldtab') insert('oldtab')
command('tabnew') command('tabnew')
old_buf = meths.get_current_buf() old_buf = curbuf().id
old_win = meths.get_current_win() old_win = curwin().id
end) end)
describe('closing the last non-floating window', function() describe('closing the last non-floating window', function()
describe('closes the tabpage when all floating windows are closeable', function() describe('closes the tabpage when all floating windows are closeable', function()
local same_buf_float
before_each(function() before_each(function()
meths.open_win(old_buf, true, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
end) end)
after_each(function() after_each(function()
eq(old_tabpage, meths.get_current_tabpage()) eq(old_tabpage, curtab().id)
expect('oldtab') expect('oldtab')
eq(1, #meths.list_tabpages()) eq(1, #meths.list_tabpages())
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.set_current_win(old_win)
meths.win_close(old_win, false) meths.win_close(old_win, false)
end) end)
it('if called from floating window', function() it('if called from floating window', function()
meths.set_current_win(same_buf_float)
meths.win_close(old_win, false) meths.win_close(old_win, false)
end) end)
end) end)
describe('gives E5601 when there are non-closeable floating windows', function() describe('gives E5601 when there are non-closeable floating windows', function()
local other_buf_float
before_each(function() before_each(function()
command('set nohidden') command('set nohidden')
local other_buf = meths.create_buf(true, false) local other_buf = meths.create_buf(true, false).id
meths.open_win(other_buf, true, float_opts) other_buf_float = meths.open_win(other_buf, true, float_opts).id
insert('foo') insert('foo')
meths.set_current_win(old_win)
end) end)
it('if called from non-floating window', function() it('if called from non-floating window', function()
meths.set_current_win(old_win)
eq('Vim:E5601: Cannot close window, only floating window would remain', eq('Vim:E5601: Cannot close window, only floating window would remain',
pcall_err(meths.win_close, old_win, false)) pcall_err(meths.win_close, old_win, false))
end) end)
it('if called from floating window', function() it('if called from floating window', function()
meths.set_current_win(other_buf_float)
eq('Vim:E5601: Cannot close window, only floating window would remain', eq('Vim:E5601: Cannot close window, only floating window would remain',
pcall_err(meths.win_close, old_win, false)) pcall_err(meths.win_close, old_win, false))
end) end)
@@ -650,15 +671,15 @@ describe('float window', function()
end) end)
describe("deleting the last non-floating window's buffer", function() describe("deleting the last non-floating window's buffer", function()
describe('closes the tabpage when all floating windows are closeable', function() describe('closes the tabpage when all floating windows are closeable', function()
local same_buf_win, other_buf, other_buf_win local same_buf_float, other_buf, other_buf_float
before_each(function() before_each(function()
same_buf_win = meths.open_win(old_buf, false, float_opts) same_buf_float = meths.open_win(old_buf, false, float_opts).id
other_buf = meths.create_buf(true, false) other_buf = meths.create_buf(true, false).id
other_buf_win = meths.open_win(other_buf, true, float_opts) other_buf_float = meths.open_win(other_buf, true, float_opts).id
meths.set_current_win(old_win) meths.set_current_win(old_win)
end) end)
after_each(function() after_each(function()
eq(old_tabpage, meths.get_current_tabpage()) eq(old_tabpage, curtab().id)
expect('oldtab') expect('oldtab')
eq(1, #meths.list_tabpages()) eq(1, #meths.list_tabpages())
end) end)
@@ -666,12 +687,12 @@ describe('float window', function()
meths.buf_delete(old_buf, {force = false}) meths.buf_delete(old_buf, {force = false})
end) end)
it('if called from floating window with the same buffer', function() it('if called from floating window with the same buffer', function()
meths.set_current_win(same_buf_win) meths.set_current_win(same_buf_float)
meths.buf_delete(old_buf, {force = false}) meths.buf_delete(old_buf, {force = false})
end) end)
-- TODO: this case is too hard to deal with -- TODO: this case is too hard to deal with
pending('if called from floating window with another buffer', function() pending('if called from floating window with another buffer', function()
meths.set_current_win(other_buf_win) meths.set_current_win(other_buf_float)
meths.buf_delete(old_buf, {force = false}) meths.buf_delete(old_buf, {force = false})
end) end)
end) end)