mirror of
https://github.com/neovim/neovim.git
synced 2026-08-27 17:41:48 +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/drawscreen.h"
|
||||||
#include "nvim/errors.h"
|
#include "nvim/errors.h"
|
||||||
#include "nvim/eval/window.h"
|
#include "nvim/eval/window.h"
|
||||||
|
#include "nvim/ex_cmds_defs.h"
|
||||||
|
#include "nvim/ex_docmd.h"
|
||||||
#include "nvim/globals.h"
|
#include "nvim/globals.h"
|
||||||
#include "nvim/highlight_group.h"
|
#include "nvim/highlight_group.h"
|
||||||
#include "nvim/macros_defs.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");
|
api_set_error(err, kErrorTypeException, "Cannot move last non-floating window");
|
||||||
return false;
|
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)) {
|
if (is_aucmd_win(wp)) {
|
||||||
api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage");
|
api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include "nvim/eval/window.h"
|
#include "nvim/eval/window.h"
|
||||||
#include "nvim/ex_cmds.h"
|
#include "nvim/ex_cmds.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
#include "nvim/ex_cmds_defs.h"
|
|
||||||
#include "nvim/ex_docmd.h"
|
#include "nvim/ex_docmd.h"
|
||||||
#include "nvim/ex_eval.h"
|
#include "nvim/ex_eval.h"
|
||||||
#include "nvim/ex_getln.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,
|
/// error message. When closing window(s) and the command isn't easy to know,
|
||||||
/// passing CMD_SIZE will also work.
|
/// passing CMD_SIZE will also work.
|
||||||
bool window_layout_locked(cmdidx_T cmd)
|
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 (split_disallowed > 0 || close_disallowed > 0) {
|
||||||
if (close_disallowed == 0 && cmd == CMD_tabnew) {
|
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 {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3168,7 +3168,6 @@ describe('API/win', function()
|
|||||||
)
|
)
|
||||||
command('quit!')
|
command('quit!')
|
||||||
|
|
||||||
-- Can't switch away from window before moving it to a different tabpage during textlock.
|
|
||||||
exec(([[
|
exec(([[
|
||||||
new
|
new
|
||||||
call setline(1, 'foo')
|
call setline(1, 'foo')
|
||||||
@@ -3180,6 +3179,41 @@ describe('API/win', function()
|
|||||||
pcall_err(command, 'normal! ==')
|
pcall_err(command, 'normal! ==')
|
||||||
)
|
)
|
||||||
eq(cur_win, api.nvim_get_current_win())
|
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)
|
end)
|
||||||
|
|
||||||
it('updates statusline when moving bottom split', function()
|
it('updates statusline when moving bottom split', function()
|
||||||
|
|||||||
Reference in New Issue
Block a user