mirror of
https://github.com/neovim/neovim.git
synced 2026-04-01 05:12:02 +00:00
fix(api): relax config validation for "win"
Problem: only possible to move floats between tabpages if relative=win, which has the restrictive effect of also anchoring it to the target window. Solution: allow "win" without "relative" or "split"/"vertical". Only assume missing "win" is 0 if relative=win is given to maintain that behaviour. (or when configuring a new window) Also add an error when attempting to change a split into a float that's in another tabpage, as this isn't actually supported yet. (until the next commit) Maybe this could do with some bikeshedding. Unclear if "win" should require "relative" to be given, like with "row"/"col"; this can be annoying though as specifying "relative" requires other fields to be given too.
This commit is contained in:
@@ -3921,9 +3921,11 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
|
|||||||
Default is `"left"`.
|
Default is `"left"`.
|
||||||
• vertical: Split vertically |:vertical|.
|
• vertical: Split vertically |:vertical|.
|
||||||
• width: Window width (in character cells). Minimum of 1.
|
• width: Window width (in character cells). Minimum of 1.
|
||||||
• win: |window-ID| window to split, or relative window when
|
• win: |window-ID| target window. Can be in a different tab
|
||||||
creating a float (relative="win"). When splitting,
|
page. Determines the window to split (negative values act
|
||||||
negative value works like |:topleft|, |:botright|.
|
like |:topleft|, |:botright|), the relative window for a
|
||||||
|
`relative="win"` float, or just the target tab page
|
||||||
|
(inferred from the window) for others.
|
||||||
• zindex: Stacking order. floats with higher `zindex` go on
|
• zindex: Stacking order. floats with higher `zindex` go on
|
||||||
top on floats with lower indices. Must be larger than
|
top on floats with lower indices. Must be larger than
|
||||||
zero. The following screen elements have hard-coded
|
zero. The following screen elements have hard-coded
|
||||||
|
|||||||
5
runtime/lua/vim/_meta/api.lua
generated
5
runtime/lua/vim/_meta/api.lua
generated
@@ -1848,8 +1848,9 @@ function vim.api.nvim_open_term(buffer, opts) end
|
|||||||
--- Default is `"left"`.
|
--- Default is `"left"`.
|
||||||
--- - vertical: Split vertically `:vertical`.
|
--- - vertical: Split vertically `:vertical`.
|
||||||
--- - width: Window width (in character cells). Minimum of 1.
|
--- - width: Window width (in character cells). Minimum of 1.
|
||||||
--- - win: `window-ID` window to split, or relative window when creating a float (relative="win").
|
--- - win: `window-ID` target window. Can be in a different tab page. Determines the window to
|
||||||
--- When splitting, negative value works like `:topleft`, `:botright`.
|
--- split (negative values act like `:topleft`, `:botright`), the relative window for a
|
||||||
|
--- `relative="win"` float, or just the target tab page (inferred from the window) for others.
|
||||||
--- - zindex: Stacking order. floats with higher `zindex` go on top on
|
--- - zindex: Stacking order. floats with higher `zindex` go on top on
|
||||||
--- floats with lower indices. Must be larger than zero. The
|
--- floats with lower indices. Must be larger than zero. The
|
||||||
--- following screen elements have hard-coded z-indices:
|
--- following screen elements have hard-coded z-indices:
|
||||||
|
|||||||
@@ -180,8 +180,9 @@
|
|||||||
/// Default is `"left"`.
|
/// Default is `"left"`.
|
||||||
/// - vertical: Split vertically |:vertical|.
|
/// - vertical: Split vertically |:vertical|.
|
||||||
/// - width: Window width (in character cells). Minimum of 1.
|
/// - width: Window width (in character cells). Minimum of 1.
|
||||||
/// - win: |window-ID| window to split, or relative window when creating a float (relative="win").
|
/// - win: |window-ID| target window. Can be in a different tab page. Determines the window to
|
||||||
/// When splitting, negative value works like |:topleft|, |:botright|.
|
/// split (negative values act like |:topleft|, |:botright|), the relative window for a
|
||||||
|
/// `relative="win"` float, or just the target tab page (inferred from the window) for others.
|
||||||
/// - zindex: Stacking order. floats with higher `zindex` go on top on
|
/// - zindex: Stacking order. floats with higher `zindex` go on top on
|
||||||
/// floats with lower indices. Must be larger than zero. The
|
/// floats with lower indices. Must be larger than zero. The
|
||||||
/// following screen elements have hard-coded z-indices:
|
/// following screen elements have hard-coded z-indices:
|
||||||
@@ -745,6 +746,16 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (was_split && !to_split) {
|
if (was_split && !to_split) {
|
||||||
|
win_T *parent = find_window_by_handle(fconfig.window, err);
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO(seandewar): support this, preferably via win_config_float_tp.
|
||||||
|
if (!win_valid(parent)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation,
|
||||||
|
"Cannot configure split into float in another tabpage");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!win_new_float(win, false, fconfig, err)) {
|
if (!win_new_float(win, false, fconfig, err)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1351,33 +1362,31 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relative_is_win || is_split) {
|
if (relative_is_win || (HAS_KEY_X(config, win) && !is_split && wp && wp->w_floating
|
||||||
if (wp && relative_is_win) {
|
&& fconfig->relative == kFloatRelativeWindow)) {
|
||||||
win_T *target_win = find_window_by_handle(config->win, err);
|
// When relative=win is given, missing win field means win=0.
|
||||||
if (!target_win) {
|
win_T *target_win = find_window_by_handle(config->win, err);
|
||||||
goto fail;
|
if (!target_win) {
|
||||||
}
|
goto fail;
|
||||||
|
|
||||||
if (target_win == wp) {
|
|
||||||
api_set_error(err, kErrorTypeException, "floating window cannot be relative to itself");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fconfig->window = curwin->handle;
|
if (target_win == wp) {
|
||||||
|
api_set_error(err, kErrorTypeException, "floating window cannot be relative to itself");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
fconfig->window = target_win->handle;
|
||||||
|
} else {
|
||||||
|
// Handle is not validated here, as win_config_split can accept negative values.
|
||||||
if (HAS_KEY_X(config, win)) {
|
if (HAS_KEY_X(config, win)) {
|
||||||
if (config->win > 0) {
|
if (!is_split && !has_relative && (!wp || !wp->w_floating)) {
|
||||||
fconfig->window = config->win;
|
api_set_error(err, kErrorTypeValidation,
|
||||||
|
"non-float with 'win' requires at least 'split' or 'vertical'");
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
fconfig->window = config->win;
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY_X(config, win)) {
|
// Resolve, but skip validating. E.g: win_config_split accepts negative "win".
|
||||||
if (has_relative) {
|
if (fconfig->window == 0) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
fconfig->window = curwin->handle;
|
||||||
"'win' key is only valid with relative='win' and relative=''");
|
|
||||||
goto fail;
|
|
||||||
} else if (!is_split) {
|
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"non-float with 'win' requires at least 'split' or 'vertical'");
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2585,19 +2585,26 @@ describe('API/win', function()
|
|||||||
},
|
},
|
||||||
}, layout)
|
}, layout)
|
||||||
|
|
||||||
|
-- converting split into a float for a different tabpage is not yet supported
|
||||||
|
eq(
|
||||||
|
'Cannot configure split into float in another tabpage',
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
win,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 1, height = 1, win = first_win }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
-- convert new win to float in new tabpage
|
-- convert new win to float in new tabpage
|
||||||
api.nvim_win_set_config(win, { relative = 'editor', row = 2, col = 2, height = 2, width = 2 })
|
api.nvim_win_set_config(win, { relative = 'editor', row = 2, col = 2, height = 2, width = 2 })
|
||||||
api.nvim_set_current_tabpage(first_tab)
|
api.nvim_set_current_tabpage(first_tab)
|
||||||
-- move to other tabpage
|
-- move to other tabpage
|
||||||
api.nvim_win_set_config(win, { relative = 'win', win = first_win, row = 2, col = 2 })
|
api.nvim_win_set_config(win, { win = first_win })
|
||||||
eq(first_tab, api.nvim_win_get_tabpage(win))
|
eq(first_tab, api.nvim_win_get_tabpage(win))
|
||||||
eq({ first_win, win }, api.nvim_tabpage_list_wins(first_tab))
|
eq({ first_win, win }, api.nvim_tabpage_list_wins(first_tab))
|
||||||
eq({ tab2_win }, api.nvim_tabpage_list_wins(new_tab))
|
eq({ tab2_win }, api.nvim_tabpage_list_wins(new_tab))
|
||||||
-- unlike splits, negative win is invalid
|
-- unlike splits, negative win is invalid
|
||||||
eq(
|
eq('Invalid window id: -1', pcall_err(api.nvim_win_set_config, win, { win = -1 }))
|
||||||
'Invalid window id: -1',
|
|
||||||
pcall_err(api.nvim_win_set_config, win, { relative = 'win', win = -1, row = 2, col = 2 })
|
|
||||||
)
|
|
||||||
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()
|
||||||
@@ -2875,7 +2882,7 @@ describe('API/win', function()
|
|||||||
})
|
})
|
||||||
api.nvim_set_current_tabpage(tab1)
|
api.nvim_set_current_tabpage(tab1)
|
||||||
api.nvim_set_var('result', {})
|
api.nvim_set_var('result', {})
|
||||||
api.nvim_win_set_config(fwin, { relative = 'win', win = tab1_win1, row = 4, col = 4 })
|
api.nvim_win_set_config(fwin, { win = tab1_win1 })
|
||||||
eq({}, eval('g:result'))
|
eq({}, eval('g:result'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -3584,11 +3591,7 @@ describe('API/win', function()
|
|||||||
split_ok, split_err =
|
split_ok, split_err =
|
||||||
pcall(vim.api.nvim_win_set_config, 0, { win = other_tp_win, split = 'right' })
|
pcall(vim.api.nvim_win_set_config, 0, { win = other_tp_win, split = 'right' })
|
||||||
|
|
||||||
float_ok, float_err = pcall(
|
float_ok, float_err = pcall(vim.api.nvim_win_set_config, 0, { win = other_tp_win })
|
||||||
vim.api.nvim_win_set_config,
|
|
||||||
0,
|
|
||||||
{ relative = 'win', win = other_tp_win, row = 0, col = 0 }
|
|
||||||
)
|
|
||||||
end)
|
end)
|
||||||
return win_type, split_ok, split_err, float_ok, float_err
|
return win_type, split_ok, split_err, float_ok, float_err
|
||||||
end)
|
end)
|
||||||
@@ -3714,11 +3717,7 @@ describe('API/win', function()
|
|||||||
command('autocmd WinLeave * ++once call nvim_win_close(' .. tab2_win .. ', v:true)')
|
command('autocmd WinLeave * ++once call nvim_win_close(' .. tab2_win .. ', v:true)')
|
||||||
eq(
|
eq(
|
||||||
'Target windows were closed',
|
'Target windows were closed',
|
||||||
pcall_err(
|
pcall_err(api.nvim_win_set_config, float_win, { win = tab2_win })
|
||||||
api.nvim_win_set_config,
|
|
||||||
float_win,
|
|
||||||
{ relative = 'win', win = tab2_win, row = 0, col = 0 }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
eq(float_win, api.nvim_get_current_win())
|
eq(float_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
@@ -3727,11 +3726,7 @@ describe('API/win', function()
|
|||||||
command('tabprev | autocmd WinEnter * ++once wincmd p')
|
command('tabprev | autocmd WinEnter * ++once wincmd p')
|
||||||
eq(
|
eq(
|
||||||
('Failed to switch away from window %d'):format(float_win),
|
('Failed to switch away from window %d'):format(float_win),
|
||||||
pcall_err(
|
pcall_err(api.nvim_win_set_config, float_win, { win = tab3_win })
|
||||||
api.nvim_win_set_config,
|
|
||||||
float_win,
|
|
||||||
{ relative = 'win', win = tab3_win, row = 0, col = 0 }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
eq(float_win, api.nvim_get_current_win())
|
eq(float_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
@@ -3743,11 +3738,7 @@ describe('API/win', function()
|
|||||||
)
|
)
|
||||||
eq(
|
eq(
|
||||||
('Window %d was made non-floating'):format(float_win),
|
('Window %d was made non-floating'):format(float_win),
|
||||||
pcall_err(
|
pcall_err(api.nvim_win_set_config, float_win, { win = tab3_win })
|
||||||
api.nvim_win_set_config,
|
|
||||||
float_win,
|
|
||||||
{ relative = 'win', win = tab3_win, row = 0, col = 0 }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
eq(float_win, api.nvim_get_current_win())
|
eq(float_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
@@ -3776,11 +3767,7 @@ describe('API/win', function()
|
|||||||
)
|
)
|
||||||
eq(
|
eq(
|
||||||
('Cannot move external window to another tabpage'):format(float_win),
|
('Cannot move external window to another tabpage'):format(float_win),
|
||||||
pcall_err(
|
pcall_err(api.nvim_win_set_config, float_win, { win = tab3_win })
|
||||||
api.nvim_win_set_config,
|
|
||||||
float_win,
|
|
||||||
{ relative = 'win', win = tab3_win, row = 0, col = 0 }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
eq(float_win, api.nvim_get_current_win())
|
eq(float_win, api.nvim_get_current_win())
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -3783,8 +3783,8 @@ describe('float window', function()
|
|||||||
local buf = api.nvim_create_buf(false, false)
|
local buf = api.nvim_create_buf(false, false)
|
||||||
eq("Invalid key: 'bork'", pcall_err(api.nvim_open_win, buf, false, { width = 20, height = 2, bork = true }))
|
eq("Invalid key: 'bork'", pcall_err(api.nvim_open_win, buf, false, { width = 20, height = 2, bork = true }))
|
||||||
eq(
|
eq(
|
||||||
"'win' key is only valid with relative='win' and relative=''",
|
"Must specify 'relative' or 'external' when creating a float",
|
||||||
pcall_err(api.nvim_open_win, buf, false, { width = 20, height = 2, relative = 'editor', row = 0, col = 0, win = 0 })
|
pcall_err(api.nvim_open_win, buf, false, { win = 0 })
|
||||||
)
|
)
|
||||||
eq(
|
eq(
|
||||||
"floating windows cannot have 'vertical'",
|
"floating windows cannot have 'vertical'",
|
||||||
@@ -11191,7 +11191,17 @@ describe('float window', function()
|
|||||||
local winid = api.nvim_open_win(buf, false, config)
|
local winid = api.nvim_open_win(buf, false, config)
|
||||||
api.nvim_set_current_win(winid)
|
api.nvim_set_current_win(winid)
|
||||||
eq('floating window cannot be relative to itself', pcall_err(api.nvim_win_set_config, winid, config))
|
eq('floating window cannot be relative to itself', pcall_err(api.nvim_win_set_config, winid, config))
|
||||||
-- Also when configuring split into float.
|
eq('floating window cannot be relative to itself', pcall_err(api.nvim_win_set_config, winid, { win = winid }))
|
||||||
|
-- Don't assume win=0 if no win given for existing relative=win float; so no error.
|
||||||
|
api.nvim_win_set_config(winid, { width = 7 })
|
||||||
|
eq(7, api.nvim_win_get_config(winid).width)
|
||||||
|
-- Don't expect the error when configuring to something other than relative=win, as win=self
|
||||||
|
-- is fine in those cases. (though maybe pointless) Other errors might be expected, though.
|
||||||
|
eq('Cannot split a floating window', pcall_err(api.nvim_win_set_config, winid, { split = 'above', win = winid }))
|
||||||
|
eq('win', api.nvim_win_get_config(winid).relative)
|
||||||
|
api.nvim_win_set_config(winid, { relative = 'editor', win = winid, row = 3, col = 3 })
|
||||||
|
eq('editor', api.nvim_win_get_config(winid).relative)
|
||||||
|
-- An error when configuring split into relative=win float.
|
||||||
command('split')
|
command('split')
|
||||||
eq('floating window cannot be relative to itself', pcall_err(api.nvim_win_set_config, 0, config))
|
eq('floating window cannot be relative to itself', pcall_err(api.nvim_win_set_config, 0, config))
|
||||||
end)
|
end)
|
||||||
@@ -11881,9 +11891,9 @@ describe('float window', function()
|
|||||||
-- Schedule an UPD_NOT_VALID redraw, but in one event move the float out of curtab before it's
|
-- Schedule an UPD_NOT_VALID redraw, but in one event move the float out of curtab before it's
|
||||||
-- handled. Do not flush before then.
|
-- handled. Do not flush before then.
|
||||||
local float = exec_lua(function()
|
local float = exec_lua(function()
|
||||||
local float = vim.api.nvim_open_win(0, true, { relative = 'editor', width = 10, height = 5, row = 0, col = 0 })
|
local float = vim.api.nvim_open_win(0, true, { relative = 'editor', width = 10, height = 5, row = 1, col = 1 })
|
||||||
vim.api.nvim__redraw({ valid = false, flush = false })
|
vim.api.nvim__redraw({ valid = false, flush = false })
|
||||||
vim.api.nvim_win_set_config(float, { relative = 'win', win = tab1_win, row = 1, col = 1 })
|
vim.api.nvim_win_set_config(float, { win = tab1_win })
|
||||||
return float
|
return float
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -11914,7 +11924,7 @@ describe('float window', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Importantly, want tabline redrawn and float's hl attribs to be correct here.
|
-- Importantly, want tabline redrawn and float's hl attribs to be correct here.
|
||||||
api.nvim_win_set_config(float, { relative = 'win', win = 0, row = 1, col = 1 })
|
api.nvim_win_set_config(float, { win = 0 })
|
||||||
if multigrid then
|
if multigrid then
|
||||||
screen:expect({
|
screen:expect({
|
||||||
grid = [[
|
grid = [[
|
||||||
@@ -11935,7 +11945,7 @@ describe('float window', function()
|
|||||||
{2:~ }|*4
|
{2:~ }|*4
|
||||||
]],
|
]],
|
||||||
float_pos = {
|
float_pos = {
|
||||||
[5] = { 1002, 'NW', 4, 1, 1, true, 50, 1, 1, 1 },
|
[5] = { 1002, 'NW', 1, 1, 1, true, 50, 1, 1, 1 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
@@ -12053,7 +12063,7 @@ describe('float window', function()
|
|||||||
]])
|
]])
|
||||||
end
|
end
|
||||||
|
|
||||||
api.nvim_win_set_config(float, { relative = 'win', win = tab2_win, row = 1, col = 1 })
|
api.nvim_win_set_config(float, { win = tab2_win })
|
||||||
if multigrid then
|
if multigrid then
|
||||||
screen:expect({
|
screen:expect({
|
||||||
grid = [[
|
grid = [[
|
||||||
|
|||||||
Reference in New Issue
Block a user