mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 04:42:03 +00:00
fix(api): disallow moving window between tabpages in more cases
Problem: more cases where it may not be safe to move a window between tabpages. Solution: check them. Rather speculative... I haven't spend much time looking, but I didn't find existing code that sets these locks to skip checking win_valid. (what I did find called it anyway, like in win_close) Still, I think it's a good precaution for what future code might do. If the fact that nvim_win_set_config *actually* moves windows between tabpages causes unforeseen issues, "faking" it like ":wincmd T" may be an alternative: split a new window, close the old one, but instead also block autocmds, copy the old window's config, and give it its handle?
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval/window.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/highlight_group.h"
|
||||
#include "nvim/macros_defs.h"
|
||||
@@ -391,6 +393,20 @@ static bool win_can_move_tp(win_T *wp, tabpage_T *tp, Error *err)
|
||||
api_set_error(err, kErrorTypeException, "Cannot move last non-floating window");
|
||||
return false;
|
||||
}
|
||||
// Like closing, moving windows between tabpages makes win_valid return false. Helpful when e.g:
|
||||
// walking the window list, as w_next/w_prev can unexpectedly refer to windows in another tabpage!
|
||||
// Check related locks, in case they were set to avoid checking win_valid.
|
||||
if (win_locked(wp)) {
|
||||
api_set_error(err, kErrorTypeException, "Cannot move window to another tabpage whilst in use");
|
||||
return false;
|
||||
}
|
||||
if (window_layout_locked_err(CMD_SIZE, err)) {
|
||||
return false; // error already set
|
||||
}
|
||||
if (textlock || expr_map_locked()) {
|
||||
api_set_error(err, kErrorTypeException, "%s", e_textlock);
|
||||
return false;
|
||||
}
|
||||
if (is_aucmd_win(wp)) {
|
||||
api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage");
|
||||
return false;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "nvim/eval/window.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds2.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_eval.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
@@ -143,12 +142,26 @@ bool frames_locked(void)
|
||||
/// error message. When closing window(s) and the command isn't easy to know,
|
||||
/// passing CMD_SIZE will also work.
|
||||
bool window_layout_locked(cmdidx_T cmd)
|
||||
{
|
||||
Error err = ERROR_INIT;
|
||||
const bool locked = window_layout_locked_err(cmd, &err);
|
||||
if (ERROR_SET(&err)) {
|
||||
emsg(_(err.msg));
|
||||
api_clear_error(&err);
|
||||
}
|
||||
return locked;
|
||||
}
|
||||
|
||||
/// Like `window_layout_locked`, but set `err` to the (untranslated) error message when locked.
|
||||
/// @see window_layout_locked
|
||||
bool window_layout_locked_err(cmdidx_T cmd, Error *err)
|
||||
{
|
||||
if (split_disallowed > 0 || close_disallowed > 0) {
|
||||
if (close_disallowed == 0 && cmd == CMD_tabnew) {
|
||||
emsg(_(e_cannot_split_window_when_closing_buffer));
|
||||
api_set_error(err, kErrorTypeException, "%s", e_cannot_split_window_when_closing_buffer);
|
||||
} else {
|
||||
emsg(_(e_not_allowed_to_change_window_layout_in_this_autocmd));
|
||||
api_set_error(err, kErrorTypeException, "%s",
|
||||
e_not_allowed_to_change_window_layout_in_this_autocmd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3168,7 +3168,6 @@ describe('API/win', function()
|
||||
)
|
||||
command('quit!')
|
||||
|
||||
-- Can't switch away from window before moving it to a different tabpage during textlock.
|
||||
exec(([[
|
||||
new
|
||||
call setline(1, 'foo')
|
||||
@@ -3180,6 +3179,41 @@ describe('API/win', function()
|
||||
pcall_err(command, 'normal! ==')
|
||||
)
|
||||
eq(cur_win, api.nvim_get_current_win())
|
||||
exec(([[
|
||||
wincmd p
|
||||
call setline(1, 'bar')
|
||||
setlocal indentexpr=nvim_win_set_config(win_getid(winnr('#')),#{split:'left',win:%d})
|
||||
]]):format(t2_win))
|
||||
neq(cur_win, api.nvim_get_current_win())
|
||||
matches(
|
||||
'E565: Not allowed to change text or change window$',
|
||||
pcall_err(command, 'normal! ==')
|
||||
)
|
||||
-- expr_map_lock
|
||||
exec(([[
|
||||
nnoremap <expr> @ nvim_win_set_config(win_getid(winnr('#')),#{split:'left',win:%d})
|
||||
]]):format(t2_win))
|
||||
neq(cur_win, api.nvim_get_current_win())
|
||||
matches(
|
||||
'E565: Not allowed to change text or change window$',
|
||||
pcall_err(fn.feedkeys, '@', 'x')
|
||||
)
|
||||
|
||||
exec(([[
|
||||
wincmd p
|
||||
autocmd WinNewPre * ++once call nvim_win_set_config(0, #{relative:'editor', win:%d, row:0, col:0, width:1, height:1})
|
||||
]]):format(t2_win))
|
||||
matches(
|
||||
'E1312: Not allowed to change the window layout in this autocmd$',
|
||||
pcall_err(command, 'split')
|
||||
)
|
||||
eq(cur_win, api.nvim_get_current_win()) -- :split didn't enter new window due to error
|
||||
|
||||
exec(([[
|
||||
autocmd WinLeave * ++once call nvim_win_set_config(0, #{relative:'editor', win:%d, row:0, col:0, width:1, height:1})
|
||||
]]):format(t2_win))
|
||||
matches('Cannot move window to another tabpage whilst in use$', pcall_err(command, 'quit'))
|
||||
eq(cur_win, api.nvim_get_current_win()) -- :quit didn't close window due to error
|
||||
end)
|
||||
|
||||
it('updates statusline when moving bottom split', function()
|
||||
|
||||
Reference in New Issue
Block a user