mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 12:52:13 +00:00
feat(api): nvim_win_set_config can move floatwin to another tabpage
Problem: nvim_win_set_config can't move floating windows to different tab pages. Solution: allow it. Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com>
This commit is contained in:
@@ -32,6 +32,7 @@
|
|||||||
#include "nvim/syntax.h"
|
#include "nvim/syntax.h"
|
||||||
#include "nvim/types_defs.h"
|
#include "nvim/types_defs.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
|
#include "nvim/ui_compositor.h"
|
||||||
#include "nvim/ui_defs.h"
|
#include "nvim/ui_defs.h"
|
||||||
#include "nvim/vim_defs.h"
|
#include "nvim/vim_defs.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
@@ -381,6 +382,24 @@ static int win_split_flags(WinSplit split, bool toplevel)
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool can_move(win_T *wp, bool switch_tab, Error *err)
|
||||||
|
{
|
||||||
|
if (is_aucmd_win(wp) && switch_tab) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Can't move the cmdwin or its old curwin to a different tabpage.
|
||||||
|
if ((wp == cmdwin_win || wp == cmdwin_old_curwin) && switch_tab) {
|
||||||
|
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (wp->w_floating && wp->w_config.external) {
|
||||||
|
api_set_error(err, kErrorTypeException, "%s", "Cannot move external floating window");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool win_config_split(win_T *win, Dict(win_config) *config, WinConfig *fconfig, Error *err)
|
static bool win_config_split(win_T *win, Dict(win_config) *config, WinConfig *fconfig, Error *err)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
@@ -423,13 +442,7 @@ static bool win_config_split(win_T *win, Dict(win_config) *config, WinConfig *fc
|
|||||||
api_set_error(err, kErrorTypeException, "Cannot split a floating window");
|
api_set_error(err, kErrorTypeException, "Cannot split a floating window");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (is_aucmd_win(win) && win_tp != parent_tp) {
|
if (!can_move(win, win_tp != parent_tp, err)) {
|
||||||
api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Can't move the cmdwin or its old curwin to a different tabpage.
|
|
||||||
if ((win == cmdwin_win || win == cmdwin_old_curwin) && win_tp != parent_tp) {
|
|
||||||
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,6 +665,24 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
win_T *parent = NULL;
|
||||||
|
tabpage_T *parent_tp = NULL;
|
||||||
|
tabpage_T *win_tp = win_find_tabpage(win);
|
||||||
|
bool curwin_moving_tp = false;
|
||||||
|
if (config->win == 0) {
|
||||||
|
parent = curwin;
|
||||||
|
parent_tp = curtab;
|
||||||
|
} else if (config->win > 0) {
|
||||||
|
parent = find_window_by_handle(fconfig.window, err);
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent_tp = win_find_tabpage(parent);
|
||||||
|
if (!parent_tp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (was_split && !to_split) {
|
if (was_split && !to_split) {
|
||||||
if (!win_new_float(win, false, fconfig, err)) {
|
if (!win_new_float(win, false, fconfig, err)) {
|
||||||
return;
|
return;
|
||||||
@@ -662,6 +693,64 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (win->w_floating && HAS_KEY_X(config, win) && parent && parent_tp != win_tp) {
|
||||||
|
if (!can_move(win, win_tp != parent_tp, err)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (win == curwin) {
|
||||||
|
curwin_moving_tp = true;
|
||||||
|
win_goto(win_float_find_altwin(win, NULL));
|
||||||
|
|
||||||
|
// Autocommands may have been a real nuisance and messed things up...
|
||||||
|
if (curwin == win) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to switch away from window %d",
|
||||||
|
win->handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
win_tp = win_find_tabpage(win);
|
||||||
|
parent_tp = win_find_tabpage(parent);
|
||||||
|
if (!win_tp || !parent_tp) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Target windows were closed");
|
||||||
|
goto restore_curwin;
|
||||||
|
}
|
||||||
|
if (!win->w_floating) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Window %d was made non-floating",
|
||||||
|
win->handle);
|
||||||
|
goto restore_curwin;
|
||||||
|
}
|
||||||
|
if (!can_move(win, win_tp != parent_tp, err)) {
|
||||||
|
goto restore_curwin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win_tp != parent_tp) {
|
||||||
|
win_remove(win, win_tp == curtab ? NULL : win_tp);
|
||||||
|
win_T *target_after;
|
||||||
|
if (parent_tp == curtab) {
|
||||||
|
target_after = lastwin_nofloating();
|
||||||
|
} else {
|
||||||
|
target_after = parent_tp->tp_lastwin;
|
||||||
|
while (target_after->w_floating) {
|
||||||
|
target_after = target_after->w_prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
win_append(target_after, win, parent_tp == curtab ? NULL : parent_tp);
|
||||||
|
// If `win` was the curwin of its old tabpage, select a new curwin for it.
|
||||||
|
if (win_tp != curtab && win_tp->tp_curwin == win) {
|
||||||
|
win_tp->tp_curwin = win_float_find_altwin(win, win_tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent_tp != curtab) {
|
||||||
|
if (ui_has(kUIMultigrid)) {
|
||||||
|
ui_call_win_hide(win->w_grid_alloc.handle);
|
||||||
|
}
|
||||||
|
ui_comp_remove_grid(&win->w_grid_alloc);
|
||||||
|
} else {
|
||||||
|
win->w_hl_needs_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
win_config_float(win, fconfig);
|
win_config_float(win, fconfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,6 +764,14 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
} else if (win == cmdline_win && fconfig._cmdline_offset == INT_MAX) {
|
} else if (win == cmdline_win && fconfig._cmdline_offset == INT_MAX) {
|
||||||
cmdline_win = NULL;
|
cmdline_win = NULL;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
restore_curwin:
|
||||||
|
// If `win` was the original curwin, and autocommands didn't move it outside of curtab, be a
|
||||||
|
// good citizen and try to return to it.
|
||||||
|
if (curwin_moving_tp && win_valid(win)) {
|
||||||
|
win_goto(win);
|
||||||
|
}
|
||||||
#undef HAS_KEY_X
|
#undef HAS_KEY_X
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2561,60 +2561,61 @@ describe('API/win', function()
|
|||||||
eq(win, layout[2][2][2])
|
eq(win, layout[2][2][2])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('moves splits to other tabpages', function()
|
it('moves splits or floats to other tabpages', function()
|
||||||
local curtab = api.nvim_get_current_tabpage()
|
local first_tab = api.nvim_get_current_tabpage()
|
||||||
|
local first_win = api.nvim_get_current_win()
|
||||||
local win = api.nvim_open_win(0, false, { split = 'left' })
|
local win = api.nvim_open_win(0, false, { split = 'left' })
|
||||||
command('tabnew')
|
command('tabnew')
|
||||||
local tabnr = api.nvim_get_current_tabpage()
|
local new_tab = api.nvim_get_current_tabpage()
|
||||||
command('tabprev') -- return to the initial tab
|
local tab2_win = api.nvim_get_current_win()
|
||||||
|
api.nvim_set_current_tabpage(first_tab)
|
||||||
api.nvim_win_set_config(win, {
|
-- move new win to new tabpage
|
||||||
split = 'right',
|
api.nvim_win_set_config(win, { split = 'right', win = api.nvim_tabpage_get_win(new_tab) })
|
||||||
win = api.nvim_tabpage_get_win(tabnr),
|
eq(new_tab, api.nvim_win_get_tabpage(win))
|
||||||
})
|
|
||||||
|
|
||||||
eq(tabnr, api.nvim_win_get_tabpage(win))
|
|
||||||
-- we are changing the config, the current tabpage should not change
|
-- we are changing the config, the current tabpage should not change
|
||||||
eq(curtab, api.nvim_get_current_tabpage())
|
eq(first_tab, api.nvim_get_current_tabpage())
|
||||||
|
|
||||||
command('tabnext') -- switch to the new tabpage so we can get the layout
|
api.nvim_set_current_tabpage(new_tab)
|
||||||
local layout = fn.winlayout()
|
local layout = fn.winlayout()
|
||||||
|
|
||||||
eq({
|
eq({
|
||||||
'row',
|
'row',
|
||||||
{
|
{
|
||||||
{ 'leaf', api.nvim_tabpage_get_win(tabnr) },
|
{ 'leaf', api.nvim_tabpage_get_win(new_tab) },
|
||||||
{ 'leaf', win },
|
{ 'leaf', win },
|
||||||
},
|
},
|
||||||
}, layout)
|
}, layout)
|
||||||
|
|
||||||
|
-- convert new win to float window in new tabpage
|
||||||
|
api.nvim_win_set_config(win, { relative = 'editor', row = 2, col = 2, height = 2, width = 2 })
|
||||||
|
api.nvim_set_current_tabpage(first_tab)
|
||||||
|
-- move to another tabpage
|
||||||
|
api.nvim_win_set_config(win, { relative = 'win', win = first_win, row = 2, col = 2 })
|
||||||
|
eq(first_tab, api.nvim_win_get_tabpage(win))
|
||||||
|
eq({ first_win, win }, api.nvim_tabpage_list_wins(first_tab))
|
||||||
|
eq({ tab2_win }, api.nvim_tabpage_list_wins(new_tab))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('correctly moves curwin when moving curwin to a different tabpage', function()
|
it('correctly moves curwin when moving curwin to a different tabpage', function()
|
||||||
local curtab = api.nvim_get_current_tabpage()
|
local tab1 = api.nvim_get_current_tabpage()
|
||||||
|
local tab1_win = api.nvim_get_current_win()
|
||||||
command('tabnew')
|
command('tabnew')
|
||||||
local tab2 = api.nvim_get_current_tabpage()
|
local tab2 = api.nvim_get_current_tabpage()
|
||||||
local tab2_win = api.nvim_get_current_win()
|
local tab2_win = api.nvim_get_current_win()
|
||||||
|
api.nvim_set_current_tabpage(tab1) -- return to the initial tab
|
||||||
command('tabprev') -- return to the initial tab
|
|
||||||
|
|
||||||
local neighbor = api.nvim_get_current_win()
|
|
||||||
|
|
||||||
-- create and enter a new split
|
-- create and enter a new split
|
||||||
local win = api.nvim_open_win(0, true, {
|
local win = api.nvim_open_win(0, true, {
|
||||||
vertical = false,
|
vertical = false,
|
||||||
})
|
})
|
||||||
|
|
||||||
eq(curtab, api.nvim_win_get_tabpage(win))
|
eq(tab1, api.nvim_win_get_tabpage(win))
|
||||||
|
eq({ win, tab1_win }, api.nvim_tabpage_list_wins(tab1))
|
||||||
eq({ win, neighbor }, api.nvim_tabpage_list_wins(curtab))
|
|
||||||
|
|
||||||
-- move the current win to a different tabpage
|
-- move the current win to a different tabpage
|
||||||
api.nvim_win_set_config(win, {
|
api.nvim_win_set_config(win, {
|
||||||
split = 'right',
|
split = 'right',
|
||||||
win = api.nvim_tabpage_get_win(tab2),
|
win = api.nvim_tabpage_get_win(tab2),
|
||||||
})
|
})
|
||||||
|
eq(tab1, api.nvim_get_current_tabpage())
|
||||||
eq(curtab, api.nvim_get_current_tabpage())
|
|
||||||
|
|
||||||
-- win should have moved to tab2
|
-- win should have moved to tab2
|
||||||
eq(tab2, api.nvim_win_get_tabpage(win))
|
eq(tab2, api.nvim_win_get_tabpage(win))
|
||||||
@@ -2622,10 +2623,18 @@ describe('API/win', function()
|
|||||||
eq(tab2_win, api.nvim_tabpage_get_win(tab2))
|
eq(tab2_win, api.nvim_tabpage_get_win(tab2))
|
||||||
-- win lists should be correct
|
-- win lists should be correct
|
||||||
eq({ tab2_win, win }, api.nvim_tabpage_list_wins(tab2))
|
eq({ tab2_win, win }, api.nvim_tabpage_list_wins(tab2))
|
||||||
eq({ neighbor }, api.nvim_tabpage_list_wins(curtab))
|
eq({ tab1_win }, api.nvim_tabpage_list_wins(tab1))
|
||||||
|
|
||||||
-- current win should have moved to neighboring win
|
-- current win should have moved to neighboring win
|
||||||
eq(neighbor, api.nvim_tabpage_get_win(curtab))
|
eq(tab1_win, api.nvim_tabpage_get_win(tab1))
|
||||||
|
|
||||||
|
api.nvim_set_current_tabpage(tab2)
|
||||||
|
-- convert new win to float window
|
||||||
|
api.nvim_win_set_config(win, { relative = 'editor', row = 2, col = 2, height = 2, width = 2 })
|
||||||
|
api.nvim_set_current_win(win)
|
||||||
|
api.nvim_win_set_config(win, { relative = 'win', win = tab1_win, row = 3, col = 3 })
|
||||||
|
eq(tab1, api.nvim_win_get_tabpage(win))
|
||||||
|
eq(tab2, api.nvim_get_current_tabpage())
|
||||||
|
eq({ tab1_win, win }, api.nvim_tabpage_list_wins(tab1))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('splits windows in non-current tabpage', function()
|
it('splits windows in non-current tabpage', function()
|
||||||
@@ -2834,18 +2843,35 @@ describe('API/win', function()
|
|||||||
eq({ 'Leave', win, 'Enter', new_curwin }, eval('result'))
|
eq({ 'Leave', win, 'Enter', new_curwin }, eval('result'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('no autocmds when moving window within same tabpage', function()
|
it('no autocmds when moving window in same or other tabpage', function()
|
||||||
local parent = api.nvim_get_current_win()
|
local parent = api.nvim_get_current_win()
|
||||||
exec([[
|
exec([[
|
||||||
split
|
split
|
||||||
let result = []
|
let g:result = []
|
||||||
autocmd WinEnter * let result += ["Enter", win_getid()]
|
autocmd WinEnter * let g:result += ["Enter", win_getid()]
|
||||||
autocmd WinLeave * let result += ["Leave", win_getid()]
|
autocmd WinLeave * let g:result += ["Leave", win_getid()]
|
||||||
autocmd WinNew * let result += ["New", win_getid()]
|
autocmd WinNew * let g:result += ["New", win_getid()]
|
||||||
]])
|
]])
|
||||||
api.nvim_win_set_config(0, { win = parent, split = 'left' })
|
api.nvim_win_set_config(0, { win = parent, split = 'left' })
|
||||||
-- Shouldn't see any of those events, as we remain in the same window.
|
-- Shouldn't see any of those events, as we remain in the same window.
|
||||||
eq({}, eval('result'))
|
eq({}, eval('g:result'))
|
||||||
|
|
||||||
|
-- move float window from tab2 to tab1
|
||||||
|
command('tabdo only')
|
||||||
|
local tab1 = api.nvim_get_current_tabpage()
|
||||||
|
local tab1_win1 = api.nvim_get_current_win()
|
||||||
|
command('tabnew')
|
||||||
|
local fwin = api.nvim_open_win(0, false, {
|
||||||
|
relative = 'editor',
|
||||||
|
row = 2,
|
||||||
|
col = 2,
|
||||||
|
height = 2,
|
||||||
|
width = 2,
|
||||||
|
})
|
||||||
|
api.nvim_set_current_tabpage(tab1)
|
||||||
|
api.nvim_set_var('result', {})
|
||||||
|
api.nvim_win_set_config(fwin, { relative = 'win', win = tab1_win1, row = 4, col = 4 })
|
||||||
|
eq({}, eval('g:result'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('checks if splitting disallowed', function()
|
it('checks if splitting disallowed', function()
|
||||||
@@ -3656,5 +3682,49 @@ describe('API/win', function()
|
|||||||
api.nvim_open_win(0, true, { split = 'below', style = 'minimal' })
|
api.nvim_open_win(0, true, { split = 'below', style = 'minimal' })
|
||||||
command('quit')
|
command('quit')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('preserve current floating window when moving fails', function()
|
||||||
|
local buf = api.nvim_create_buf(false, true)
|
||||||
|
local tab1_win = api.nvim_get_current_win()
|
||||||
|
local float_win = api.nvim_open_win(buf, true, {
|
||||||
|
relative = 'editor',
|
||||||
|
row = 1,
|
||||||
|
col = 1,
|
||||||
|
width = 10,
|
||||||
|
height = 5,
|
||||||
|
})
|
||||||
|
command('tabnew')
|
||||||
|
local tab2_win = api.nvim_get_current_win()
|
||||||
|
command('tabprev')
|
||||||
|
api.nvim_set_current_win(float_win)
|
||||||
|
command('autocmd WinLeave * ++once call nvim_win_close(' .. tab2_win .. ', v:true)')
|
||||||
|
eq(
|
||||||
|
'Target windows were closed',
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
float_win,
|
||||||
|
{ relative = 'win', win = tab2_win, row = 0, col = 0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
eq(float_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
command('tabnew')
|
||||||
|
local tab3_win = api.nvim_get_current_win()
|
||||||
|
command('tabprev')
|
||||||
|
command(
|
||||||
|
('autocmd WinLeave * ++once call nvim_win_set_config(%d, {"split": "left", "win": %d})'):format(
|
||||||
|
float_win,
|
||||||
|
tab1_win
|
||||||
|
)
|
||||||
|
)
|
||||||
|
eq(
|
||||||
|
('Window %d was made non-floating'):format(float_win),
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
float_win,
|
||||||
|
{ relative = 'win', win = tab3_win, row = 0, col = 0 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user