mirror of
https://github.com/neovim/neovim.git
synced 2025-10-04 17:06:30 +00:00
Merge pull request #27330 from seandewar/win_set_config-fixes
fix(api): various window-related function fixes This is a big one!
This commit is contained in:
@@ -3271,9 +3271,9 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
|
|||||||
• footer_pos: Footer position. Must be set with `footer`
|
• footer_pos: Footer position. Must be set with `footer`
|
||||||
option. Value can be one of "left", "center", or "right".
|
option. Value can be one of "left", "center", or "right".
|
||||||
Default is `"left"`.
|
Default is `"left"`.
|
||||||
• noautocmd: If true then no buffer-related autocommand
|
• noautocmd: If true then autocommands triggered from
|
||||||
events such as |BufEnter|, |BufLeave| or |BufWinEnter| may
|
setting the `buffer` to display are blocked (e.g:
|
||||||
fire from calling this function.
|
|BufEnter|, |BufLeave|, |BufWinEnter|).
|
||||||
• fixed: If true when anchor is NW or SW, the float window
|
• fixed: If true when anchor is NW or SW, the float window
|
||||||
would be kept fixed even if the window would be truncated.
|
would be kept fixed even if the window would be truncated.
|
||||||
• hide: If true the floating window will be hidden.
|
• hide: If true the floating window will be hidden.
|
||||||
@@ -3297,11 +3297,11 @@ nvim_win_get_config({window}) *nvim_win_get_config()*
|
|||||||
Map defining the window configuration, see |nvim_open_win()|
|
Map defining the window configuration, see |nvim_open_win()|
|
||||||
|
|
||||||
nvim_win_set_config({window}, {config}) *nvim_win_set_config()*
|
nvim_win_set_config({window}, {config}) *nvim_win_set_config()*
|
||||||
Configures window layout. Currently only for floating and external windows
|
Configures window layout. Cannot be used to move the last window in a
|
||||||
(including changing a split window to those layouts).
|
tabpage to a different one.
|
||||||
|
|
||||||
When reconfiguring a floating window, absent option keys will not be
|
When reconfiguring a window, absent option keys will not be changed.
|
||||||
changed. `row`/`col` and `relative` must be reconfigured together.
|
`row`/`col` and `relative` must be reconfigured together.
|
||||||
|
|
||||||
Parameters: ~
|
Parameters: ~
|
||||||
• {window} Window handle, or 0 for current window
|
• {window} Window handle, or 0 for current window
|
||||||
|
8
runtime/doc/builtin.txt
generated
8
runtime/doc/builtin.txt
generated
@@ -8901,10 +8901,10 @@ win_screenpos({nr}) *win_screenpos()*
|
|||||||
tabpage.
|
tabpage.
|
||||||
|
|
||||||
win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
|
win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
|
||||||
Move the window {nr} to a new split of the window {target}.
|
Temporarily switch to window {target}, then move window {nr}
|
||||||
This is similar to moving to {target}, creating a new window
|
to a new split adjacent to {target}.
|
||||||
using |:split| but having the same contents as window {nr}, and
|
Unlike commands such as |:split|, no new windows are created
|
||||||
then closing {nr}.
|
(the |window-ID| of window {nr} is unchanged after the move).
|
||||||
|
|
||||||
Both {nr} and {target} can be window numbers or |window-ID|s.
|
Both {nr} and {target} can be window numbers or |window-ID|s.
|
||||||
Both must be in the current tab page.
|
Both must be in the current tab page.
|
||||||
|
@@ -502,35 +502,33 @@ horizontally split windows. CTRL-W H does it the other way around.
|
|||||||
|
|
||||||
*CTRL-W_K*
|
*CTRL-W_K*
|
||||||
CTRL-W K Move the current window to be at the very top, using the full
|
CTRL-W K Move the current window to be at the very top, using the full
|
||||||
width of the screen. This works like closing the current
|
width of the screen. This works like `:topleft split`, except
|
||||||
window and then creating another one with ":topleft split",
|
it is applied to the current window and no new window is
|
||||||
except that the current window contents is used for the new
|
created.
|
||||||
window.
|
|
||||||
|
|
||||||
*CTRL-W_J*
|
*CTRL-W_J*
|
||||||
CTRL-W J Move the current window to be at the very bottom, using the
|
CTRL-W J Move the current window to be at the very bottom, using the
|
||||||
full width of the screen. This works like closing the current
|
full width of the screen. This works like `:botright split`,
|
||||||
window and then creating another one with ":botright split",
|
except it is applied to the current window and no new window
|
||||||
except that the current window contents is used for the new
|
is created.
|
||||||
window.
|
|
||||||
|
|
||||||
*CTRL-W_H*
|
*CTRL-W_H*
|
||||||
CTRL-W H Move the current window to be at the far left, using the
|
CTRL-W H Move the current window to be at the far left, using the
|
||||||
full height of the screen. This works like closing the
|
full height of the screen. This works like
|
||||||
current window and then creating another one with
|
`:vert topleft split`, except it is applied to the current
|
||||||
`:vert topleft split`, except that the current window contents
|
window and no new window is created.
|
||||||
is used for the new window.
|
|
||||||
|
|
||||||
*CTRL-W_L*
|
*CTRL-W_L*
|
||||||
CTRL-W L Move the current window to be at the far right, using the full
|
CTRL-W L Move the current window to be at the far right, using the full
|
||||||
height of the screen. This works like closing the
|
height of the screen. This works like `:vert botright split`,
|
||||||
current window and then creating another one with
|
except it is applied to the current window and no new window
|
||||||
`:vert botright split`, except that the current window
|
is created.
|
||||||
contents is used for the new window.
|
|
||||||
|
|
||||||
*CTRL-W_T*
|
*CTRL-W_T*
|
||||||
CTRL-W T Move the current window to a new tab page. This fails if
|
CTRL-W T Move the current window to a new tab page. This fails if
|
||||||
there is only one window in the current tab page.
|
there is only one window in the current tab page.
|
||||||
|
This works like `:tab split`, except the previous window is
|
||||||
|
closed.
|
||||||
When a count is specified the new tab page will be opened
|
When a count is specified the new tab page will be opened
|
||||||
before the tab page with this index. Otherwise it comes after
|
before the tab page with this index. Otherwise it comes after
|
||||||
the current tab page.
|
the current tab page.
|
||||||
|
14
runtime/lua/vim/_meta/api.lua
generated
14
runtime/lua/vim/_meta/api.lua
generated
@@ -1718,9 +1718,9 @@ function vim.api.nvim_open_term(buffer, opts) end
|
|||||||
--- • footer_pos: Footer position. Must be set with `footer`
|
--- • footer_pos: Footer position. Must be set with `footer`
|
||||||
--- option. Value can be one of "left", "center", or "right".
|
--- option. Value can be one of "left", "center", or "right".
|
||||||
--- Default is `"left"`.
|
--- Default is `"left"`.
|
||||||
--- • noautocmd: If true then no buffer-related autocommand
|
--- • noautocmd: If true then autocommands triggered from
|
||||||
--- events such as `BufEnter`, `BufLeave` or `BufWinEnter` may
|
--- setting the `buffer` to display are blocked (e.g:
|
||||||
--- fire from calling this function.
|
--- `BufEnter`, `BufLeave`, `BufWinEnter`).
|
||||||
--- • fixed: If true when anchor is NW or SW, the float window
|
--- • fixed: If true when anchor is NW or SW, the float window
|
||||||
--- would be kept fixed even if the window would be truncated.
|
--- would be kept fixed even if the window would be truncated.
|
||||||
--- • hide: If true the floating window will be hidden.
|
--- • hide: If true the floating window will be hidden.
|
||||||
@@ -2223,11 +2223,11 @@ function vim.api.nvim_win_remove_ns(window, ns_id) end
|
|||||||
--- @param buffer integer Buffer handle
|
--- @param buffer integer Buffer handle
|
||||||
function vim.api.nvim_win_set_buf(window, buffer) end
|
function vim.api.nvim_win_set_buf(window, buffer) end
|
||||||
|
|
||||||
--- Configures window layout. Currently only for floating and external windows
|
--- Configures window layout. Cannot be used to move the last window in a
|
||||||
--- (including changing a split window to those layouts).
|
--- tabpage to a different one.
|
||||||
---
|
---
|
||||||
--- When reconfiguring a floating window, absent option keys will not be
|
--- When reconfiguring a window, absent option keys will not be changed.
|
||||||
--- changed. `row`/`col` and `relative` must be reconfigured together.
|
--- `row`/`col` and `relative` must be reconfigured together.
|
||||||
---
|
---
|
||||||
--- @param window integer Window handle, or 0 for current window
|
--- @param window integer Window handle, or 0 for current window
|
||||||
--- @param config vim.api.keyset.win_config Map defining the window configuration, see `nvim_open_win()`
|
--- @param config vim.api.keyset.win_config Map defining the window configuration, see `nvim_open_win()`
|
||||||
|
8
runtime/lua/vim/_meta/vimfn.lua
generated
8
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -10598,10 +10598,10 @@ function vim.fn.win_move_statusline(nr, offset) end
|
|||||||
--- @return any
|
--- @return any
|
||||||
function vim.fn.win_screenpos(nr) end
|
function vim.fn.win_screenpos(nr) end
|
||||||
|
|
||||||
--- Move the window {nr} to a new split of the window {target}.
|
--- Temporarily switch to window {target}, then move window {nr}
|
||||||
--- This is similar to moving to {target}, creating a new window
|
--- to a new split adjacent to {target}.
|
||||||
--- using |:split| but having the same contents as window {nr}, and
|
--- Unlike commands such as |:split|, no new windows are created
|
||||||
--- then closing {nr}.
|
--- (the |window-ID| of window {nr} is unchanged after the move).
|
||||||
---
|
---
|
||||||
--- Both {nr} and {target} can be window numbers or |window-ID|s.
|
--- Both {nr} and {target} can be window numbers or |window-ID|s.
|
||||||
--- Both must be in the current tab page.
|
--- Both must be in the current tab page.
|
||||||
|
@@ -146,7 +146,11 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tp == curtab) {
|
if (tp == curtab) {
|
||||||
win_enter(wp, true);
|
try_start();
|
||||||
|
win_goto(wp);
|
||||||
|
if (!try_end(err) && curwin != wp) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win);
|
||||||
|
}
|
||||||
} else if (tp->tp_curwin != wp) {
|
} else if (tp->tp_curwin != wp) {
|
||||||
tp->tp_prevwin = tp->tp_curwin;
|
tp->tp_prevwin = tp->tp_curwin;
|
||||||
tp->tp_curwin = wp;
|
tp->tp_curwin = wp;
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include "nvim/ascii_defs.h"
|
#include "nvim/ascii_defs.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/autocmd_defs.h"
|
#include "nvim/autocmd_defs.h"
|
||||||
|
#include "nvim/buffer.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
#include "nvim/decoration.h"
|
#include "nvim/decoration.h"
|
||||||
#include "nvim/decoration_defs.h"
|
#include "nvim/decoration_defs.h"
|
||||||
@@ -198,9 +199,9 @@
|
|||||||
/// - footer_pos: Footer position. Must be set with `footer` option.
|
/// - footer_pos: Footer position. Must be set with `footer` option.
|
||||||
/// Value can be one of "left", "center", or "right".
|
/// Value can be one of "left", "center", or "right".
|
||||||
/// Default is `"left"`.
|
/// Default is `"left"`.
|
||||||
/// - noautocmd: If true then no buffer-related autocommand events such as
|
/// - noautocmd: If true then autocommands triggered from setting the
|
||||||
/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from
|
/// `buffer` to display are blocked (e.g: |BufEnter|, |BufLeave|,
|
||||||
/// calling this function.
|
/// |BufWinEnter|).
|
||||||
/// - fixed: If true when anchor is NW or SW, the float window
|
/// - fixed: If true when anchor is NW or SW, the float window
|
||||||
/// would be kept fixed even if the window would be truncated.
|
/// would be kept fixed even if the window would be truncated.
|
||||||
/// - hide: If true the floating window will be hidden.
|
/// - hide: If true the floating window will be hidden.
|
||||||
@@ -245,6 +246,10 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!check_split_disallowed_err(parent ? parent : curwin, err)) {
|
||||||
|
return 0; // error already set
|
||||||
|
}
|
||||||
|
|
||||||
if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) {
|
if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) {
|
||||||
if (config->vertical) {
|
if (config->vertical) {
|
||||||
fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft;
|
fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft;
|
||||||
@@ -254,18 +259,20 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
|
|||||||
}
|
}
|
||||||
int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER;
|
int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER;
|
||||||
|
|
||||||
if (parent == NULL) {
|
TRY_WRAP(err, {
|
||||||
wp = win_split_ins(0, flags, NULL, 0);
|
if (parent == NULL || parent == curwin) {
|
||||||
} else {
|
wp = win_split_ins(0, flags, NULL, 0, NULL);
|
||||||
tp = win_find_tabpage(parent);
|
} else {
|
||||||
switchwin_T switchwin;
|
tp = win_find_tabpage(parent);
|
||||||
// `parent` is valid in `tp`, so switch_win should not fail.
|
switchwin_T switchwin;
|
||||||
const int result = switch_win(&switchwin, parent, tp, true);
|
// `parent` is valid in `tp`, so switch_win should not fail.
|
||||||
(void)result;
|
const int result = switch_win(&switchwin, parent, tp, true);
|
||||||
assert(result == OK);
|
assert(result == OK);
|
||||||
wp = win_split_ins(0, flags, NULL, 0);
|
(void)result;
|
||||||
restore_win(&switchwin, true);
|
wp = win_split_ins(0, flags, NULL, 0, NULL);
|
||||||
}
|
restore_win(&switchwin, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
if (wp) {
|
if (wp) {
|
||||||
wp->w_config = fconfig;
|
wp->w_config = fconfig;
|
||||||
}
|
}
|
||||||
@@ -273,21 +280,49 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
|
|||||||
wp = win_new_float(NULL, false, fconfig, err);
|
wp = win_new_float(NULL, false, fconfig, err);
|
||||||
}
|
}
|
||||||
if (!wp) {
|
if (!wp) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to create window");
|
if (!ERROR_SET(err)) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to create window");
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Autocommands may close `wp` or move it to another tabpage, so update and check `tp` after each
|
||||||
|
// event. In each case, `wp` should already be valid in `tp`, so switch_win should not fail.
|
||||||
|
// Also, autocommands may free the `buf` to switch to, so store a bufref to check.
|
||||||
|
bufref_T bufref;
|
||||||
|
set_bufref(&bufref, buf);
|
||||||
switchwin_T switchwin;
|
switchwin_T switchwin;
|
||||||
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
|
{
|
||||||
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
|
const int result = switch_win_noblock(&switchwin, wp, tp, true);
|
||||||
|
assert(result == OK);
|
||||||
|
(void)result;
|
||||||
|
if (apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf)) {
|
||||||
|
tp = win_find_tabpage(wp);
|
||||||
|
}
|
||||||
|
restore_win_noblock(&switchwin, true);
|
||||||
}
|
}
|
||||||
restore_win_noblock(&switchwin, true);
|
if (tp && enter) {
|
||||||
if (enter) {
|
|
||||||
goto_tabpage_win(tp, wp);
|
goto_tabpage_win(tp, wp);
|
||||||
|
tp = win_find_tabpage(wp);
|
||||||
}
|
}
|
||||||
if (win_valid_any_tab(wp) && buf != wp->w_buffer) {
|
if (tp && bufref_valid(&bufref) && buf != wp->w_buffer) {
|
||||||
win_set_buf(wp, buf, !enter || fconfig.noautocmd, err);
|
// win_set_buf temporarily makes `wp` the curwin to set the buffer.
|
||||||
|
// If not entering `wp`, block Enter and Leave events. (cringe)
|
||||||
|
const bool au_no_enter_leave = curwin != wp && !fconfig.noautocmd;
|
||||||
|
if (au_no_enter_leave) {
|
||||||
|
autocmd_no_enter++;
|
||||||
|
autocmd_no_leave++;
|
||||||
|
}
|
||||||
|
win_set_buf(wp, buf, fconfig.noautocmd, err);
|
||||||
|
if (!fconfig.noautocmd) {
|
||||||
|
tp = win_find_tabpage(wp);
|
||||||
|
}
|
||||||
|
if (au_no_enter_leave) {
|
||||||
|
autocmd_no_enter--;
|
||||||
|
autocmd_no_leave--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!win_valid_any_tab(wp)) {
|
if (!tp) {
|
||||||
api_set_error(err, kErrorTypeException, "Window was closed immediately");
|
api_set_error(err, kErrorTypeException, "Window was closed immediately");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -330,11 +365,11 @@ static int win_split_flags(WinSplit split, bool toplevel)
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures window layout. Currently only for floating and external windows
|
/// Configures window layout. Cannot be used to move the last window in a
|
||||||
/// (including changing a split window to those layouts).
|
/// tabpage to a different one.
|
||||||
///
|
///
|
||||||
/// When reconfiguring a floating window, absent option keys will not be
|
/// When reconfiguring a window, absent option keys will not be changed.
|
||||||
/// changed. `row`/`col` and `relative` must be reconfigured together.
|
/// `row`/`col` and `relative` must be reconfigured together.
|
||||||
///
|
///
|
||||||
/// @see |nvim_open_win()|
|
/// @see |nvim_open_win()|
|
||||||
///
|
///
|
||||||
@@ -413,17 +448,59 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (was_split) {
|
if (!check_split_disallowed_err(win, err)) {
|
||||||
win_T *new_curwin = NULL;
|
return; // error already set
|
||||||
|
}
|
||||||
|
// Can't move the cmdwin or its old curwin to a different tabpage.
|
||||||
|
if ((win == cmdwin_win || win == cmdwin_old_curwin) && parent != NULL
|
||||||
|
&& win_find_tabpage(parent) != win_tp) {
|
||||||
|
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool to_split_ok = false;
|
||||||
|
// If we are moving curwin to another tabpage, switch windows *before* we remove it from the
|
||||||
|
// window list or remove its frame (if non-floating), so it's valid for autocommands.
|
||||||
|
const bool curwin_moving_tp
|
||||||
|
= win == curwin && parent != NULL && win_tp != win_find_tabpage(parent);
|
||||||
|
if (curwin_moving_tp) {
|
||||||
|
if (was_split) {
|
||||||
|
int dir;
|
||||||
|
win_goto(winframe_find_altwin(win, &dir, NULL, NULL));
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
if (!win_tp || !win_valid_any_tab(parent)) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Windows to split were closed");
|
||||||
|
goto restore_curwin;
|
||||||
|
}
|
||||||
|
if (was_split == win->w_floating || parent->w_floating) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Floating state of windows to split changed");
|
||||||
|
goto restore_curwin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int dir = 0;
|
||||||
|
frame_T *unflat_altfr = NULL;
|
||||||
|
win_T *altwin = NULL;
|
||||||
|
|
||||||
|
if (was_split) {
|
||||||
// If the window is the last in the tabpage or `fconfig.win` is
|
// If the window is the last in the tabpage or `fconfig.win` is
|
||||||
// a handle to itself, we can't split it.
|
// a handle to itself, we can't split it.
|
||||||
if (win->w_frame->fr_parent == NULL) {
|
if (win->w_frame->fr_parent == NULL) {
|
||||||
// FIXME(willothy): if the window is the last in the tabpage but there is another tabpage
|
// FIXME(willothy): if the window is the last in the tabpage but there is another tabpage
|
||||||
// and the target window is in that other tabpage, should we move the window to that
|
// and the target window is in that other tabpage, should we move the window to that
|
||||||
// tabpage and close the previous one, or just error?
|
// tabpage and close the previous one, or just error?
|
||||||
api_set_error(err, kErrorTypeValidation, "Cannot move last window");
|
api_set_error(err, kErrorTypeException, "Cannot move last window");
|
||||||
return;
|
goto restore_curwin;
|
||||||
} else if (parent != NULL && parent->handle == win->handle) {
|
} else if (parent != NULL && parent->handle == win->handle) {
|
||||||
int n_frames = 0;
|
int n_frames = 0;
|
||||||
for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) {
|
for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) {
|
||||||
@@ -459,83 +536,82 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
|
|||||||
}
|
}
|
||||||
// If the frame doesn't have a parent, the old frame
|
// If the frame doesn't have a parent, the old frame
|
||||||
// was the root frame and we need to create a top-level split.
|
// was the root frame and we need to create a top-level split.
|
||||||
int dir;
|
altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr);
|
||||||
new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
|
|
||||||
} else if (n_frames == 2) {
|
} else if (n_frames == 2) {
|
||||||
// There are two windows in the frame, we can just rotate it.
|
// There are two windows in the frame, we can just rotate it.
|
||||||
int dir;
|
altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr);
|
||||||
neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
|
neighbor = altwin;
|
||||||
new_curwin = neighbor;
|
|
||||||
} else {
|
} else {
|
||||||
// There is only one window in the frame, we can't split it.
|
// There is only one window in the frame, we can't split it.
|
||||||
api_set_error(err, kErrorTypeValidation, "Cannot split window into itself");
|
api_set_error(err, kErrorTypeException, "Cannot split window into itself");
|
||||||
return;
|
goto restore_curwin;
|
||||||
}
|
}
|
||||||
// Set the parent to whatever the correct
|
// Set the parent to whatever the correct neighbor window was determined to be.
|
||||||
// neighbor window was determined to be.
|
|
||||||
parent = neighbor;
|
parent = neighbor;
|
||||||
} else {
|
} else {
|
||||||
int dir;
|
altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr);
|
||||||
new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
|
|
||||||
}
|
|
||||||
// move to neighboring window if we're moving the current window to a new tabpage
|
|
||||||
if (curwin == win && parent != NULL && new_curwin != NULL
|
|
||||||
&& win_tp != win_find_tabpage(parent)) {
|
|
||||||
win_enter(new_curwin, true);
|
|
||||||
}
|
|
||||||
win_remove(win, win_tp == curtab ? NULL : win_tp);
|
|
||||||
} else {
|
|
||||||
win_remove(win, win_tp == curtab ? NULL : win_tp);
|
|
||||||
ui_comp_remove_grid(&win->w_grid_alloc);
|
|
||||||
if (win->w_config.external) {
|
|
||||||
for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
|
|
||||||
if (tp == curtab) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (tp->tp_curwin == win) {
|
|
||||||
tp->tp_curwin = tp->tp_firstwin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
win->w_pos_changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int flags = win_split_flags(fconfig.split, parent == NULL);
|
|
||||||
|
|
||||||
if (parent == NULL) {
|
|
||||||
if (!win_split_ins(0, flags, win, 0)) {
|
|
||||||
// TODO(willothy): What should this error message say?
|
|
||||||
api_set_error(err, kErrorTypeException, "Failed to split window");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
win_execute_T args;
|
altwin = win_float_find_altwin(win, win_tp == curtab ? NULL : win_tp);
|
||||||
|
|
||||||
tabpage_T *tp = win_find_tabpage(parent);
|
|
||||||
if (!win_execute_before(&args, parent, tp)) {
|
|
||||||
// TODO(willothy): how should we handle this / what should the message be?
|
|
||||||
api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle);
|
|
||||||
win_execute_after(&args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// This should return the same ptr to `win`, but we check for
|
|
||||||
// NULL to detect errors.
|
|
||||||
win_T *res = win_split_ins(0, flags, win, 0);
|
|
||||||
win_execute_after(&args);
|
|
||||||
if (!res) {
|
|
||||||
// TODO(willothy): What should this error message say?
|
|
||||||
api_set_error(err, kErrorTypeException, "Failed to split window");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
win_remove(win, win_tp == curtab ? NULL : win_tp);
|
||||||
|
if (win_tp == curtab) {
|
||||||
|
last_status(false); // may need to remove last status line
|
||||||
|
win_comp_pos(); // recompute window positions
|
||||||
|
}
|
||||||
|
|
||||||
|
int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER;
|
||||||
|
tabpage_T *const parent_tp = parent ? win_find_tabpage(parent) : curtab;
|
||||||
|
|
||||||
|
TRY_WRAP(err, {
|
||||||
|
const bool need_switch = parent != NULL && parent != curwin;
|
||||||
|
switchwin_T switchwin;
|
||||||
|
if (need_switch) {
|
||||||
|
// `parent` is valid in its tabpage, so switch_win should not fail.
|
||||||
|
const int result = switch_win(&switchwin, parent, parent_tp, true);
|
||||||
|
(void)result;
|
||||||
|
assert(result == OK);
|
||||||
|
}
|
||||||
|
to_split_ok = win_split_ins(0, flags, win, 0, unflat_altfr) != NULL;
|
||||||
|
if (!to_split_ok) {
|
||||||
|
// Restore `win` to the window list now, so it's valid for restore_win (if used).
|
||||||
|
win_append(win->w_prev, win, win_tp == curtab ? NULL : win_tp);
|
||||||
|
}
|
||||||
|
if (need_switch) {
|
||||||
|
restore_win(&switchwin, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!to_split_ok) {
|
||||||
|
if (was_split) {
|
||||||
|
// win_split_ins doesn't change sizes or layout if it fails to insert an existing window, so
|
||||||
|
// just undo winframe_remove.
|
||||||
|
winframe_restore(win, dir, unflat_altfr);
|
||||||
|
}
|
||||||
|
if (!ERROR_SET(err)) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to move window %d into split", win->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `win` moved tabpages and was the curwin of its old one, select a new curwin for it.
|
||||||
|
if (win_tp != parent_tp && win_tp->tp_curwin == win) {
|
||||||
|
win_tp->tp_curwin = altwin;
|
||||||
|
}
|
||||||
|
|
||||||
if (HAS_KEY_X(config, width)) {
|
if (HAS_KEY_X(config, width)) {
|
||||||
win_setwidth_win(fconfig.width, win);
|
win_setwidth_win(fconfig.width, win);
|
||||||
}
|
}
|
||||||
if (HAS_KEY_X(config, height)) {
|
if (HAS_KEY_X(config, height)) {
|
||||||
win_setheight_win(fconfig.height, win);
|
win_setheight_win(fconfig.height, win);
|
||||||
}
|
}
|
||||||
redraw_later(win, UPD_NOT_VALID);
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
win_config_float(win, fconfig);
|
win_config_float(win, fconfig);
|
||||||
win->w_pos_changed = true;
|
win->w_pos_changed = true;
|
||||||
@@ -1071,11 +1147,15 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo
|
|||||||
fconfig->window = config->win;
|
fconfig->window = config->win;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (has_relative) {
|
} else if (HAS_KEY_X(config, win)) {
|
||||||
if (HAS_KEY_X(config, win)) {
|
if (has_relative) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
api_set_error(err, kErrorTypeValidation,
|
||||||
"'win' key is only valid with relative='win' and relative=''");
|
"'win' key is only valid with relative='win' and relative=''");
|
||||||
return false;
|
return false;
|
||||||
|
} else if (!is_split) {
|
||||||
|
api_set_error(err, kErrorTypeValidation,
|
||||||
|
"non-float with 'win' requires at least 'split' or 'vertical'");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1333,7 +1333,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
|
|||||||
|
|
||||||
block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
|
block_autocmds(); // We don't want BufEnter/WinEnter autocommands.
|
||||||
if (need_append) {
|
if (need_append) {
|
||||||
win_append(lastwin, auc_win);
|
win_append(lastwin, auc_win, NULL);
|
||||||
pmap_put(int)(&window_handles, auc_win->handle, auc_win);
|
pmap_put(int)(&window_handles, auc_win->handle, auc_win);
|
||||||
win_config_float(auc_win, auc_win->w_config);
|
win_config_float(auc_win, auc_win->w_config);
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,6 @@
|
|||||||
# include "buffer.c.generated.h"
|
# include "buffer.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char *e_auabort = N_("E855: Autocommands caused command to abort");
|
|
||||||
static const char e_attempt_to_delete_buffer_that_is_in_use_str[]
|
static const char e_attempt_to_delete_buffer_that_is_in_use_str[]
|
||||||
= N_("E937: Attempt to delete a buffer that is in use: %s");
|
= N_("E937: Attempt to delete a buffer that is in use: %s");
|
||||||
|
|
||||||
@@ -569,7 +568,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
|||||||
}
|
}
|
||||||
buf->b_locked--;
|
buf->b_locked--;
|
||||||
buf->b_locked_split--;
|
buf->b_locked_split--;
|
||||||
if (abort_if_last && last_nonfloat(win)) {
|
if (abort_if_last && one_window(win)) {
|
||||||
// Autocommands made this the only window.
|
// Autocommands made this the only window.
|
||||||
emsg(_(e_auabort));
|
emsg(_(e_auabort));
|
||||||
return false;
|
return false;
|
||||||
@@ -588,7 +587,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
|||||||
}
|
}
|
||||||
buf->b_locked--;
|
buf->b_locked--;
|
||||||
buf->b_locked_split--;
|
buf->b_locked_split--;
|
||||||
if (abort_if_last && last_nonfloat(win)) {
|
if (abort_if_last && one_window(win)) {
|
||||||
// Autocommands made this the only window.
|
// Autocommands made this the only window.
|
||||||
emsg(_(e_auabort));
|
emsg(_(e_auabort));
|
||||||
return false;
|
return false;
|
||||||
|
@@ -1513,7 +1513,7 @@ static void win_update(win_T *wp)
|
|||||||
|
|
||||||
// Make sure skipcol is valid, it depends on various options and the window
|
// Make sure skipcol is valid, it depends on various options and the window
|
||||||
// width.
|
// width.
|
||||||
if (wp->w_skipcol > 0) {
|
if (wp->w_skipcol > 0 && wp->w_width_inner > win_col_off(wp)) {
|
||||||
int w = 0;
|
int w = 0;
|
||||||
int width1 = wp->w_width_inner - win_col_off(wp);
|
int width1 = wp->w_width_inner - win_col_off(wp);
|
||||||
int width2 = width1 + win_col_off2(wp);
|
int width2 = width1 + win_col_off2(wp);
|
||||||
|
@@ -12699,10 +12699,10 @@ M.funcs = {
|
|||||||
args = { 2, 3 },
|
args = { 2, 3 },
|
||||||
base = 1,
|
base = 1,
|
||||||
desc = [=[
|
desc = [=[
|
||||||
Move the window {nr} to a new split of the window {target}.
|
Temporarily switch to window {target}, then move window {nr}
|
||||||
This is similar to moving to {target}, creating a new window
|
to a new split adjacent to {target}.
|
||||||
using |:split| but having the same contents as window {nr}, and
|
Unlike commands such as |:split|, no new windows are created
|
||||||
then closing {nr}.
|
(the |window-ID| of window {nr} is unchanged after the move).
|
||||||
|
|
||||||
Both {nr} and {target} can be window numbers or |window-ID|s.
|
Both {nr} and {target} can be window numbers or |window-ID|s.
|
||||||
Both must be in the current tab page.
|
Both must be in the current tab page.
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/eval/typval_defs.h"
|
#include "nvim/eval/typval_defs.h"
|
||||||
#include "nvim/eval/window.h"
|
#include "nvim/eval/window.h"
|
||||||
|
#include "nvim/ex_getln.h"
|
||||||
#include "nvim/garray.h"
|
#include "nvim/garray.h"
|
||||||
#include "nvim/garray_defs.h"
|
#include "nvim/garray_defs.h"
|
||||||
#include "nvim/gettext_defs.h"
|
#include "nvim/gettext_defs.h"
|
||||||
@@ -583,9 +584,13 @@ void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
int id = (int)tv_get_number(&argvars[0]);
|
int id = (int)tv_get_number(&argvars[0]);
|
||||||
|
if (curwin->handle == id) {
|
||||||
|
// Nothing to do.
|
||||||
|
rettv->vval.v_number = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cmdwin_type != 0) {
|
if (text_or_buf_locked()) {
|
||||||
emsg(_(e_cmdwin));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||||
@@ -659,55 +664,19 @@ void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
|
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move the window wp into a new split of targetwin in a given direction
|
|
||||||
static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
|
|
||||||
{
|
|
||||||
int height = wp->w_height;
|
|
||||||
win_T *oldwin = curwin;
|
|
||||||
|
|
||||||
if (wp == targetwin || is_aucmd_win(wp)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump to the target window
|
|
||||||
if (curwin != targetwin) {
|
|
||||||
win_goto(targetwin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the old window and frame from the tree of frames
|
|
||||||
int dir;
|
|
||||||
winframe_remove(wp, &dir, NULL);
|
|
||||||
win_remove(wp, NULL);
|
|
||||||
last_status(false); // may need to remove last status line
|
|
||||||
win_comp_pos(); // recompute window positions
|
|
||||||
|
|
||||||
// Split a window on the desired side and put the old window there
|
|
||||||
win_split_ins(size, flags, wp, dir);
|
|
||||||
|
|
||||||
// If splitting horizontally, try to preserve height
|
|
||||||
if (size == 0 && !(flags & WSP_VERT)) {
|
|
||||||
win_setheight_win(height, wp);
|
|
||||||
if (p_ea) {
|
|
||||||
win_equal(wp, true, 'v');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldwin != curwin) {
|
|
||||||
win_goto(oldwin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "win_splitmove()" function
|
/// "win_splitmove()" function
|
||||||
void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
||||||
win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
|
win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
|
||||||
|
win_T *oldwin = curwin;
|
||||||
|
|
||||||
|
rettv->vval.v_number = -1;
|
||||||
|
|
||||||
if (wp == NULL || targetwin == NULL || wp == targetwin
|
if (wp == NULL || targetwin == NULL || wp == targetwin
|
||||||
|| !win_valid(wp) || !win_valid(targetwin)
|
|| !win_valid(wp) || !win_valid(targetwin)
|
||||||
|| win_float_valid(wp) || win_float_valid(targetwin)) {
|
|| targetwin->w_floating) {
|
||||||
emsg(_(e_invalwindow));
|
emsg(_(e_invalwindow));
|
||||||
rettv->vval.v_number = -1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,7 +701,27 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
size = (int)tv_dict_get_number(d, "size");
|
size = (int)tv_dict_get_number(d, "size");
|
||||||
}
|
}
|
||||||
|
|
||||||
win_move_into_split(wp, targetwin, size, flags);
|
// Check if we can split the target before we bother switching windows.
|
||||||
|
if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(targetwin) == FAIL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curwin != targetwin) {
|
||||||
|
win_goto(targetwin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autocommands may have sent us elsewhere or closed "wp" or "oldwin".
|
||||||
|
if (curwin == targetwin && win_valid(wp)) {
|
||||||
|
if (win_splitmove(wp, size, flags) == OK) {
|
||||||
|
rettv->vval.v_number = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emsg(_(e_auabort));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldwin != curwin && win_valid(oldwin)) {
|
||||||
|
win_goto(oldwin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "win_gettype(nr)" function
|
/// "win_gettype(nr)" function
|
||||||
@@ -969,9 +958,11 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n
|
|||||||
if (no_display) {
|
if (no_display) {
|
||||||
curtab->tp_firstwin = firstwin;
|
curtab->tp_firstwin = firstwin;
|
||||||
curtab->tp_lastwin = lastwin;
|
curtab->tp_lastwin = lastwin;
|
||||||
|
curtab->tp_topframe = topframe;
|
||||||
curtab = tp;
|
curtab = tp;
|
||||||
firstwin = curtab->tp_firstwin;
|
firstwin = curtab->tp_firstwin;
|
||||||
lastwin = curtab->tp_lastwin;
|
lastwin = curtab->tp_lastwin;
|
||||||
|
topframe = curtab->tp_topframe;
|
||||||
} else {
|
} else {
|
||||||
goto_tabpage_tp(tp, false, false);
|
goto_tabpage_tp(tp, false, false);
|
||||||
}
|
}
|
||||||
@@ -1000,9 +991,11 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display)
|
|||||||
if (no_display) {
|
if (no_display) {
|
||||||
curtab->tp_firstwin = firstwin;
|
curtab->tp_firstwin = firstwin;
|
||||||
curtab->tp_lastwin = lastwin;
|
curtab->tp_lastwin = lastwin;
|
||||||
|
curtab->tp_topframe = topframe;
|
||||||
curtab = switchwin->sw_curtab;
|
curtab = switchwin->sw_curtab;
|
||||||
firstwin = curtab->tp_firstwin;
|
firstwin = curtab->tp_firstwin;
|
||||||
lastwin = curtab->tp_lastwin;
|
lastwin = curtab->tp_lastwin;
|
||||||
|
topframe = curtab->tp_topframe;
|
||||||
} else {
|
} else {
|
||||||
goto_tabpage_tp(switchwin->sw_curtab, false, false);
|
goto_tabpage_tp(switchwin->sw_curtab, false, false);
|
||||||
}
|
}
|
||||||
|
@@ -939,6 +939,7 @@ EXTERN const char e_using_float_as_string[] INIT(= N_("E806: Using a Float as a
|
|||||||
EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
|
EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
|
||||||
EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d"));
|
EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d"));
|
||||||
EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s"));
|
EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s"));
|
||||||
|
EXTERN const char e_auabort[] INIT(= N_("E855: Autocommands caused command to abort"));
|
||||||
|
|
||||||
EXTERN const char e_api_error[] INIT(= N_("E5555: API call: %s"));
|
EXTERN const char e_api_error[] INIT(= N_("E5555: API call: %s"));
|
||||||
|
|
||||||
|
@@ -1643,7 +1643,7 @@ static void invalidate(TUIData *tui, int top, int bot, int left, int right)
|
|||||||
static void ensure_space_buf_size(TUIData *tui, size_t len)
|
static void ensure_space_buf_size(TUIData *tui, size_t len)
|
||||||
{
|
{
|
||||||
if (len > tui->space_buf_len) {
|
if (len > tui->space_buf_len) {
|
||||||
tui->space_buf = xrealloc(tui->space_buf, len * sizeof *tui->space_buf);
|
tui->space_buf = xrealloc(tui->space_buf, len);
|
||||||
memset(tui->space_buf + tui->space_buf_len, ' ', len - tui->space_buf_len);
|
memset(tui->space_buf + tui->space_buf_len, ' ', len - tui->space_buf_len);
|
||||||
tui->space_buf_len = len;
|
tui->space_buf_len = len;
|
||||||
}
|
}
|
||||||
|
@@ -455,9 +455,14 @@ newwindow:
|
|||||||
case 'H':
|
case 'H':
|
||||||
case 'L':
|
case 'L':
|
||||||
CHECK_CMDWIN;
|
CHECK_CMDWIN;
|
||||||
win_totop(Prenum,
|
if (one_window(curwin)) {
|
||||||
((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
|
beep_flush();
|
||||||
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
|
} else {
|
||||||
|
const int dir = ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
|
||||||
|
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT);
|
||||||
|
|
||||||
|
win_splitmove(curwin, Prenum, dir);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// make all windows the same width and/or height
|
// make all windows the same width and/or height
|
||||||
@@ -718,6 +723,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
|
|||||||
kErrorTypeException,
|
kErrorTypeException,
|
||||||
"Failed to switch to window %d",
|
"Failed to switch to window %d",
|
||||||
win->handle);
|
win->handle);
|
||||||
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
try_start();
|
try_start();
|
||||||
@@ -729,10 +735,11 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
|
|||||||
buf->handle);
|
buf->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If window is not current, state logic will not validate its cursor.
|
// If window is not current, state logic will not validate its cursor. So do it now.
|
||||||
// So do it now.
|
// Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set).
|
||||||
validate_cursor();
|
validate_cursor();
|
||||||
|
|
||||||
|
cleanup:
|
||||||
restore_win_noblock(&switchwin, true);
|
restore_win_noblock(&switchwin, true);
|
||||||
if (noautocmd) {
|
if (noautocmd) {
|
||||||
unblock_autocmds();
|
unblock_autocmds();
|
||||||
@@ -903,19 +910,35 @@ void ui_ext_win_viewport(win_T *wp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If "split_disallowed" is set give an error and return FAIL.
|
/// If "split_disallowed" is set or "wp"s buffer is closing, give an error and return FAIL.
|
||||||
/// Otherwise return OK.
|
/// Otherwise return OK.
|
||||||
static int check_split_disallowed(void)
|
int check_split_disallowed(const win_T *wp)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
const bool ok = check_split_disallowed_err(wp, &err);
|
||||||
|
if (ERROR_SET(&err)) {
|
||||||
|
emsg(_(err.msg));
|
||||||
|
api_clear_error(&err);
|
||||||
|
}
|
||||||
|
return ok ? OK : FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like `check_split_disallowed`, but set `err` to the (untranslated) error message on failure and
|
||||||
|
/// return false. Otherwise return true.
|
||||||
|
/// @see check_split_disallowed
|
||||||
|
bool check_split_disallowed_err(const win_T *wp, Error *err)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
if (split_disallowed > 0) {
|
if (split_disallowed > 0) {
|
||||||
emsg(_("E242: Can't split a window while closing another"));
|
api_set_error(err, kErrorTypeException, "E242: Can't split a window while closing another");
|
||||||
return FAIL;
|
return false;
|
||||||
}
|
}
|
||||||
if (curwin->w_buffer->b_locked_split) {
|
if (wp->w_buffer->b_locked_split) {
|
||||||
emsg(_(e_cannot_split_window_when_closing_buffer));
|
api_set_error(err, kErrorTypeException, "%s", e_cannot_split_window_when_closing_buffer);
|
||||||
return FAIL;
|
return false;
|
||||||
}
|
}
|
||||||
return OK;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// split the current window, implements CTRL-W s and :split
|
// split the current window, implements CTRL-W s and :split
|
||||||
@@ -934,7 +957,7 @@ static int check_split_disallowed(void)
|
|||||||
// return FAIL for failure, OK otherwise
|
// return FAIL for failure, OK otherwise
|
||||||
int win_split(int size, int flags)
|
int win_split(int size, int flags)
|
||||||
{
|
{
|
||||||
if (check_split_disallowed() == FAIL) {
|
if (check_split_disallowed(curwin) == FAIL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,14 +981,19 @@ int win_split(int size, int flags)
|
|||||||
clear_snapshot(curtab, SNAP_HELP_IDX);
|
clear_snapshot(curtab, SNAP_HELP_IDX);
|
||||||
}
|
}
|
||||||
|
|
||||||
return win_split_ins(size, flags, NULL, 0) == NULL ? FAIL : OK;
|
return win_split_ins(size, flags, NULL, 0, NULL) == NULL ? FAIL : OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When "new_wp" is NULL: split the current window in two.
|
/// When "new_wp" is NULL: split the current window in two.
|
||||||
/// When "new_wp" is not NULL: insert this window at the far
|
/// When "new_wp" is not NULL: insert this window at the far
|
||||||
/// top/left/right/bottom.
|
/// top/left/right/bottom.
|
||||||
|
/// When "to_flatten" is not NULL: flatten this frame before reorganising frames;
|
||||||
|
/// remains unflattened on failure.
|
||||||
|
///
|
||||||
|
/// On failure, if "new_wp" was not NULL, no changes will have been made to the
|
||||||
|
/// window layout or sizes.
|
||||||
/// @return NULL for failure, or pointer to new window
|
/// @return NULL for failure, or pointer to new window
|
||||||
win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_flatten)
|
||||||
{
|
{
|
||||||
win_T *wp = new_wp;
|
win_T *wp = new_wp;
|
||||||
|
|
||||||
@@ -986,13 +1014,12 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
|
|
||||||
int need_status = 0;
|
int need_status = 0;
|
||||||
int new_size = size;
|
int new_size = size;
|
||||||
bool new_in_layout = (new_wp == NULL || new_wp->w_floating);
|
|
||||||
bool vertical = flags & WSP_VERT;
|
bool vertical = flags & WSP_VERT;
|
||||||
bool toplevel = flags & (WSP_TOP | WSP_BOT);
|
bool toplevel = flags & (WSP_TOP | WSP_BOT);
|
||||||
|
|
||||||
// add a status line when p_ls == 1 and splitting the first window
|
// add a status line when p_ls == 1 and splitting the first window
|
||||||
if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) {
|
if (one_window(firstwin) && p_ls == 1 && oldwin->w_status_height == 0) {
|
||||||
if (oldwin->w_height <= p_wmh && new_in_layout) {
|
if (oldwin->w_height <= p_wmh) {
|
||||||
emsg(_(e_noroom));
|
emsg(_(e_noroom));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1041,7 +1068,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
available = oldwin->w_frame->fr_width;
|
available = oldwin->w_frame->fr_width;
|
||||||
needed += minwidth;
|
needed += minwidth;
|
||||||
}
|
}
|
||||||
if (available < needed && new_in_layout) {
|
if (available < needed) {
|
||||||
emsg(_(e_noroom));
|
emsg(_(e_noroom));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1121,7 +1148,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
available = oldwin->w_frame->fr_height;
|
available = oldwin->w_frame->fr_height;
|
||||||
needed += minheight;
|
needed += minheight;
|
||||||
}
|
}
|
||||||
if (available < needed && new_in_layout) {
|
if (available < needed) {
|
||||||
emsg(_(e_noroom));
|
emsg(_(e_noroom));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1191,13 +1218,13 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
if (new_wp == NULL) {
|
if (new_wp == NULL) {
|
||||||
wp = win_alloc(oldwin, false);
|
wp = win_alloc(oldwin, false);
|
||||||
} else {
|
} else {
|
||||||
win_append(oldwin, wp);
|
win_append(oldwin, wp, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (new_wp == NULL) {
|
if (new_wp == NULL) {
|
||||||
wp = win_alloc(oldwin->w_prev, false);
|
wp = win_alloc(oldwin->w_prev, false);
|
||||||
} else {
|
} else {
|
||||||
win_append(oldwin->w_prev, wp);
|
win_append(oldwin->w_prev, wp, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1211,13 +1238,37 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
// make the contents of the new window the same as the current one
|
// make the contents of the new window the same as the current one
|
||||||
win_init(wp, curwin, flags);
|
win_init(wp, curwin, flags);
|
||||||
} else if (wp->w_floating) {
|
} else if (wp->w_floating) {
|
||||||
new_frame(wp);
|
ui_comp_remove_grid(&wp->w_grid_alloc);
|
||||||
|
if (ui_has(kUIMultigrid)) {
|
||||||
|
wp->w_pos_changed = true;
|
||||||
|
} else {
|
||||||
|
// No longer a float, a non-multigrid UI shouldn't draw it as such
|
||||||
|
ui_call_win_hide(wp->w_grid_alloc.handle);
|
||||||
|
win_free_grid(wp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// External windows are independent of tabpages, and may have been the curwin of others.
|
||||||
|
if (wp->w_config.external) {
|
||||||
|
FOR_ALL_TABS(tp) {
|
||||||
|
if (tp != curtab && tp->tp_curwin == wp) {
|
||||||
|
tp->tp_curwin = tp->tp_firstwin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wp->w_floating = false;
|
wp->w_floating = false;
|
||||||
|
new_frame(wp);
|
||||||
|
|
||||||
// non-floating window doesn't store float config or have a border.
|
// non-floating window doesn't store float config or have a border.
|
||||||
wp->w_config = WIN_CONFIG_INIT;
|
wp->w_config = WIN_CONFIG_INIT;
|
||||||
CLEAR_FIELD(wp->w_border_adj);
|
CLEAR_FIELD(wp->w_border_adj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Going to reorganize frames now, make sure they're flat.
|
||||||
|
if (to_flatten != NULL) {
|
||||||
|
frame_flatten(to_flatten);
|
||||||
|
}
|
||||||
|
|
||||||
bool before;
|
bool before;
|
||||||
frame_T *curfrp;
|
frame_T *curfrp;
|
||||||
|
|
||||||
@@ -1453,7 +1504,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
|||||||
|
|
||||||
if (!(flags & WSP_NOENTER)) {
|
if (!(flags & WSP_NOENTER)) {
|
||||||
// make the new window the current window
|
// make the new window the current window
|
||||||
win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS
|
win_enter_ext(wp, (new_wp == NULL ? WEE_TRIGGER_NEW_AUTOCMDS : 0) | WEE_TRIGGER_ENTER_AUTOCMDS
|
||||||
| WEE_TRIGGER_LEAVE_AUTOCMDS);
|
| WEE_TRIGGER_LEAVE_AUTOCMDS);
|
||||||
}
|
}
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
@@ -1690,7 +1741,7 @@ static void win_exchange(int Prenum)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstwin == curwin && lastwin_nofloating() == curwin) {
|
if (one_window(curwin)) {
|
||||||
// just one window
|
// just one window
|
||||||
beep_flush();
|
beep_flush();
|
||||||
return;
|
return;
|
||||||
@@ -1732,13 +1783,13 @@ static void win_exchange(int Prenum)
|
|||||||
if (wp->w_prev != curwin) {
|
if (wp->w_prev != curwin) {
|
||||||
win_remove(curwin, NULL);
|
win_remove(curwin, NULL);
|
||||||
frame_remove(curwin->w_frame);
|
frame_remove(curwin->w_frame);
|
||||||
win_append(wp->w_prev, curwin);
|
win_append(wp->w_prev, curwin, NULL);
|
||||||
frame_insert(frp, curwin->w_frame);
|
frame_insert(frp, curwin->w_frame);
|
||||||
}
|
}
|
||||||
if (wp != wp2) {
|
if (wp != wp2) {
|
||||||
win_remove(wp, NULL);
|
win_remove(wp, NULL);
|
||||||
frame_remove(wp->w_frame);
|
frame_remove(wp->w_frame);
|
||||||
win_append(wp2, wp);
|
win_append(wp2, wp, NULL);
|
||||||
if (frp2 == NULL) {
|
if (frp2 == NULL) {
|
||||||
frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame);
|
frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame);
|
||||||
} else {
|
} else {
|
||||||
@@ -1782,7 +1833,7 @@ static void win_rotate(bool upwards, int count)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count <= 0 || (firstwin == curwin && lastwin_nofloating() == curwin)) {
|
if (count <= 0 || one_window(curwin)) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
beep_flush();
|
beep_flush();
|
||||||
return;
|
return;
|
||||||
@@ -1812,7 +1863,7 @@ static void win_rotate(bool upwards, int count)
|
|||||||
|
|
||||||
// find last frame and append removed window/frame after it
|
// find last frame and append removed window/frame after it
|
||||||
for (; frp->fr_next != NULL; frp = frp->fr_next) {}
|
for (; frp->fr_next != NULL; frp = frp->fr_next) {}
|
||||||
win_append(frp->fr_win, wp1);
|
win_append(frp->fr_win, wp1, NULL);
|
||||||
frame_append(frp, wp1->w_frame);
|
frame_append(frp, wp1->w_frame);
|
||||||
|
|
||||||
wp2 = frp->fr_win; // previously last window
|
wp2 = frp->fr_win; // previously last window
|
||||||
@@ -1827,7 +1878,7 @@ static void win_rotate(bool upwards, int count)
|
|||||||
assert(frp->fr_parent->fr_child);
|
assert(frp->fr_parent->fr_child);
|
||||||
|
|
||||||
// append the removed window/frame before the first in the list
|
// append the removed window/frame before the first in the list
|
||||||
win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1);
|
win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1, NULL);
|
||||||
frame_insert(frp->fr_parent->fr_child, frp);
|
frame_insert(frp->fr_parent->fr_child, frp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1856,48 +1907,58 @@ static void win_rotate(bool upwards, int count)
|
|||||||
redraw_all_later(UPD_NOT_VALID);
|
redraw_all_later(UPD_NOT_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the current window to the very top/bottom/left/right of the screen.
|
/// Move "wp" into a new split in a given direction, possibly relative to the
|
||||||
static void win_totop(int size, int flags)
|
/// current window.
|
||||||
|
/// "wp" must be valid in the current tabpage.
|
||||||
|
/// Returns FAIL for failure, OK otherwise.
|
||||||
|
int win_splitmove(win_T *wp, int size, int flags)
|
||||||
{
|
{
|
||||||
int dir = 0;
|
int dir = 0;
|
||||||
int height = curwin->w_height;
|
int height = wp->w_height;
|
||||||
|
|
||||||
if (firstwin == curwin && lastwin_nofloating() == curwin) {
|
if (firstwin == wp && lastwin_nofloating() == wp) {
|
||||||
beep_flush();
|
return OK; // nothing to do
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (is_aucmd_win(curwin)) {
|
if (is_aucmd_win(wp) || check_split_disallowed(wp) == FAIL) {
|
||||||
return;
|
return FAIL;
|
||||||
}
|
|
||||||
if (check_split_disallowed() == FAIL) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curwin->w_floating) {
|
frame_T *unflat_altfr = NULL;
|
||||||
ui_comp_remove_grid(&curwin->w_grid_alloc);
|
if (wp->w_floating) {
|
||||||
if (ui_has(kUIMultigrid)) {
|
win_remove(wp, NULL);
|
||||||
curwin->w_pos_changed = true;
|
|
||||||
} else {
|
|
||||||
// No longer a float, a non-multigrid UI shouldn't draw it as such
|
|
||||||
ui_call_win_hide(curwin->w_grid_alloc.handle);
|
|
||||||
win_free_grid(curwin, true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Remove the window and frame from the tree of frames.
|
// Remove the window and frame from the tree of frames. Don't flatten any
|
||||||
winframe_remove(curwin, &dir, NULL);
|
// frames yet so we can restore things if win_split_ins fails.
|
||||||
|
winframe_remove(wp, &dir, NULL, &unflat_altfr);
|
||||||
|
win_remove(wp, NULL);
|
||||||
|
last_status(false); // may need to remove last status line
|
||||||
|
win_comp_pos(); // recompute window positions
|
||||||
}
|
}
|
||||||
win_remove(curwin, NULL);
|
|
||||||
last_status(false); // may need to remove last status line
|
|
||||||
win_comp_pos(); // recompute window positions
|
|
||||||
|
|
||||||
// Split a window on the desired side and put the window there.
|
// Split a window on the desired side and put "wp" there.
|
||||||
win_split_ins(size, flags, curwin, dir);
|
if (win_split_ins(size, flags, wp, dir, unflat_altfr) == NULL) {
|
||||||
if (!(flags & WSP_VERT)) {
|
win_append(wp->w_prev, wp, NULL);
|
||||||
win_setheight(height);
|
if (!wp->w_floating) {
|
||||||
|
// win_split_ins doesn't change sizes or layout if it fails to insert an
|
||||||
|
// existing window, so just undo winframe_remove.
|
||||||
|
winframe_restore(wp, dir, unflat_altfr);
|
||||||
|
win_comp_pos(); // recompute window positions
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If splitting horizontally, try to preserve height.
|
||||||
|
// Note that win_split_ins autocommands may have immediately closed "wp", or made it floating!
|
||||||
|
if (size == 0 && !(flags & WSP_VERT) && win_valid(wp) && !wp->w_floating) {
|
||||||
|
win_setheight_win(height, wp);
|
||||||
if (p_ea) {
|
if (p_ea) {
|
||||||
win_equal(curwin, true, 'v');
|
// Equalize windows. Note that win_split_ins autocommands may have
|
||||||
|
// made a window other than "wp" current.
|
||||||
|
win_equal(curwin, curwin == wp, 'v');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move window "win1" to below/right of "win2" and make "win1" the current
|
// Move window "win1" to below/right of "win2" and make "win1" the current
|
||||||
@@ -1955,7 +2016,7 @@ void win_move_after(win_T *win1, win_T *win2)
|
|||||||
}
|
}
|
||||||
win_remove(win1, NULL);
|
win_remove(win1, NULL);
|
||||||
frame_remove(win1->w_frame);
|
frame_remove(win1->w_frame);
|
||||||
win_append(win2, win1);
|
win_append(win2, win1, NULL);
|
||||||
frame_append(win2->w_frame, win1->w_frame);
|
frame_append(win2->w_frame, win1->w_frame);
|
||||||
|
|
||||||
win_comp_pos(); // recompute w_winrow for all windows
|
win_comp_pos(); // recompute w_winrow for all windows
|
||||||
@@ -2434,37 +2495,13 @@ bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if "win" is the only non-floating window in the current tabpage.
|
/// Check if "win" is the only non-floating window in the current tabpage.
|
||||||
|
///
|
||||||
|
/// This should be used in place of ONE_WINDOW when necessary,
|
||||||
|
/// with "firstwin" or the affected window as argument depending on the situation.
|
||||||
bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
if (win->w_floating) {
|
assert(!firstwin->w_floating);
|
||||||
return false;
|
return firstwin == win && (win->w_next == NULL || win->w_next->w_floating);
|
||||||
}
|
|
||||||
|
|
||||||
bool seen_one = false;
|
|
||||||
|
|
||||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
|
||||||
if (!wp->w_floating) {
|
|
||||||
if (seen_one) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seen_one = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like ONE_WINDOW but only considers non-floating windows
|
|
||||||
bool one_nonfloat(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
|
||||||
{
|
|
||||||
return firstwin->w_next == NULL || firstwin->w_next->w_floating;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// if wp is the last non-floating window
|
|
||||||
///
|
|
||||||
/// always false for a floating window
|
|
||||||
bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
|
||||||
{
|
|
||||||
return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if floating windows in the current tab can be closed.
|
/// Check if floating windows in the current tab can be closed.
|
||||||
@@ -2759,13 +2796,10 @@ int win_close(win_T *win, bool free_buf, bool force)
|
|||||||
ui_comp_remove_grid(&win->w_grid_alloc);
|
ui_comp_remove_grid(&win->w_grid_alloc);
|
||||||
assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer"
|
assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer"
|
||||||
if (win->w_config.external) {
|
if (win->w_config.external) {
|
||||||
for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
|
FOR_ALL_TABS(tp) {
|
||||||
if (tp == curtab) {
|
if (tp != curtab && tp->tp_curwin == win) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (tp->tp_curwin == win) {
|
|
||||||
// NB: an autocmd can still abort the closing of this window,
|
// NB: an autocmd can still abort the closing of this window,
|
||||||
// bur carring out this change anyway shouldn't be a catastrophe.
|
// but carrying out this change anyway shouldn't be a catastrophe.
|
||||||
tp->tp_curwin = tp->tp_firstwin;
|
tp->tp_curwin = tp->tp_firstwin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3006,24 +3040,11 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp)
|
|||||||
if (!win->w_floating) {
|
if (!win->w_floating) {
|
||||||
// Remove the window and its frame from the tree of frames.
|
// Remove the window and its frame from the tree of frames.
|
||||||
frame_T *frp = win->w_frame;
|
frame_T *frp = win->w_frame;
|
||||||
wp = winframe_remove(win, dirp, tp);
|
wp = winframe_remove(win, dirp, tp, NULL);
|
||||||
xfree(frp);
|
xfree(frp);
|
||||||
} else {
|
} else {
|
||||||
*dirp = 'h'; // Dummy value.
|
*dirp = 'h'; // Dummy value.
|
||||||
if (tp == NULL) {
|
wp = win_float_find_altwin(win, tp);
|
||||||
if (win_valid(prevwin) && prevwin != win) {
|
|
||||||
wp = prevwin;
|
|
||||||
} else {
|
|
||||||
wp = firstwin;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(tp != curtab);
|
|
||||||
if (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) {
|
|
||||||
wp = tp->tp_prevwin;
|
|
||||||
} else {
|
|
||||||
wp = tp->tp_firstwin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
win_free(win, tp);
|
win_free(win, tp);
|
||||||
|
|
||||||
@@ -3087,9 +3108,60 @@ void win_free_all(void)
|
|||||||
///
|
///
|
||||||
/// @param dirp set to 'v' or 'h' for direction if 'ea'
|
/// @param dirp set to 'v' or 'h' for direction if 'ea'
|
||||||
/// @param tp tab page "win" is in, NULL for current
|
/// @param tp tab page "win" is in, NULL for current
|
||||||
|
/// @param unflat_altfr if not NULL, set to pointer of frame that got
|
||||||
|
/// the space, and it is not flattened
|
||||||
///
|
///
|
||||||
/// @return a pointer to the window that got the freed up space.
|
/// @return a pointer to the window that got the freed up space.
|
||||||
win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
|
win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp, frame_T **unflat_altfr)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||||
|
{
|
||||||
|
frame_T *altfr;
|
||||||
|
win_T *wp = winframe_find_altwin(win, dirp, tp, &altfr);
|
||||||
|
if (wp == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_T *frp_close = win->w_frame;
|
||||||
|
// Remove this frame from the list of frames.
|
||||||
|
frame_remove(frp_close);
|
||||||
|
|
||||||
|
if (*dirp == 'v') {
|
||||||
|
frame_new_height(altfr, altfr->fr_height + frp_close->fr_height,
|
||||||
|
altfr == frp_close->fr_next, false);
|
||||||
|
} else {
|
||||||
|
assert(*dirp == 'h');
|
||||||
|
frame_new_width(altfr, altfr->fr_width + frp_close->fr_width,
|
||||||
|
altfr == frp_close->fr_next, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If rows/columns go to a window below/right its positions need to be
|
||||||
|
// updated. Can only be done after the sizes have been updated.
|
||||||
|
if (altfr == frp_close->fr_next) {
|
||||||
|
int row = win->w_winrow;
|
||||||
|
int col = win->w_wincol;
|
||||||
|
|
||||||
|
frame_comp_pos(altfr, &row, &col);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unflat_altfr == NULL) {
|
||||||
|
frame_flatten(altfr);
|
||||||
|
} else {
|
||||||
|
*unflat_altfr = altfr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the window that will get the freed space from a call to `winframe_remove`.
|
||||||
|
/// Makes no changes to the window layout.
|
||||||
|
///
|
||||||
|
/// @param dirp set to 'v' or 'h' for the direction where "altfr" will be resized
|
||||||
|
/// to fill the space
|
||||||
|
/// @param tp tab page "win" is in, NULL for current
|
||||||
|
/// @param altfr if not NULL, set to pointer of frame that will get the space
|
||||||
|
///
|
||||||
|
/// @return a pointer to the window that will get the freed up space.
|
||||||
|
win_T *winframe_find_altwin(win_T *win, int *dirp, tabpage_T *tp, frame_T **altfr)
|
||||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||||
{
|
{
|
||||||
assert(tp == NULL || tp != curtab);
|
assert(tp == NULL || tp != curtab);
|
||||||
@@ -3101,13 +3173,10 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
|
|||||||
|
|
||||||
frame_T *frp_close = win->w_frame;
|
frame_T *frp_close = win->w_frame;
|
||||||
|
|
||||||
// Remove the window from its frame.
|
// Find the window and frame that gets the space.
|
||||||
frame_T *frp2 = win_altframe(win, tp);
|
frame_T *frp2 = win_altframe(win, tp);
|
||||||
win_T *wp = frame2win(frp2);
|
win_T *wp = frame2win(frp2);
|
||||||
|
|
||||||
// Remove this frame from the list of frames.
|
|
||||||
frame_remove(frp_close);
|
|
||||||
|
|
||||||
if (frp_close->fr_parent->fr_layout == FR_COL) {
|
if (frp_close->fr_parent->fr_layout == FR_COL) {
|
||||||
// When 'winfixheight' is set, try to find another frame in the column
|
// When 'winfixheight' is set, try to find another frame in the column
|
||||||
// (as close to the closed frame as possible) to distribute the height
|
// (as close to the closed frame as possible) to distribute the height
|
||||||
@@ -3134,8 +3203,6 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame_new_height(frp2, frp2->fr_height + frp_close->fr_height,
|
|
||||||
frp2 == frp_close->fr_next, false);
|
|
||||||
*dirp = 'v';
|
*dirp = 'v';
|
||||||
} else {
|
} else {
|
||||||
// When 'winfixwidth' is set, try to find another frame in the column
|
// When 'winfixwidth' is set, try to find another frame in the column
|
||||||
@@ -3163,70 +3230,124 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame_new_width(frp2, frp2->fr_width + frp_close->fr_width,
|
|
||||||
frp2 == frp_close->fr_next, false);
|
|
||||||
*dirp = 'h';
|
*dirp = 'h';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If rows/columns go to a window below/right its positions need to be
|
assert(wp != win && frp2 != frp_close);
|
||||||
// updated. Can only be done after the sizes have been updated.
|
if (altfr != NULL) {
|
||||||
if (frp2 == frp_close->fr_next) {
|
*altfr = frp2;
|
||||||
int row = win->w_winrow;
|
|
||||||
int col = win->w_wincol;
|
|
||||||
|
|
||||||
frame_comp_pos(frp2, &row, &col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frp2->fr_next == NULL && frp2->fr_prev == NULL) {
|
return wp;
|
||||||
// There is no other frame in this list, move its info to the parent
|
}
|
||||||
// and remove it.
|
|
||||||
frp2->fr_parent->fr_layout = frp2->fr_layout;
|
/// Flatten "frp" into its parent frame if it's the only child, also merging its
|
||||||
frp2->fr_parent->fr_child = frp2->fr_child;
|
/// list with the grandparent if they share the same layout.
|
||||||
frame_T *frp;
|
/// Frees "frp" if flattened; also "frp->fr_parent" if it has the same layout.
|
||||||
FOR_ALL_FRAMES(frp, frp2->fr_child) {
|
/// "frp" must be valid in the current tabpage.
|
||||||
frp->fr_parent = frp2->fr_parent;
|
static void frame_flatten(frame_T *frp)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
if (frp->fr_next != NULL || frp->fr_prev != NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no other frame in this list, move its info to the parent
|
||||||
|
// and remove it.
|
||||||
|
frp->fr_parent->fr_layout = frp->fr_layout;
|
||||||
|
frp->fr_parent->fr_child = frp->fr_child;
|
||||||
|
frame_T *frp2;
|
||||||
|
FOR_ALL_FRAMES(frp2, frp->fr_child) {
|
||||||
|
frp2->fr_parent = frp->fr_parent;
|
||||||
|
}
|
||||||
|
frp->fr_parent->fr_win = frp->fr_win;
|
||||||
|
if (frp->fr_win != NULL) {
|
||||||
|
frp->fr_win->w_frame = frp->fr_parent;
|
||||||
|
}
|
||||||
|
frp2 = frp->fr_parent;
|
||||||
|
if (topframe->fr_child == frp) {
|
||||||
|
topframe->fr_child = frp2;
|
||||||
|
}
|
||||||
|
xfree(frp);
|
||||||
|
|
||||||
|
frp = frp2->fr_parent;
|
||||||
|
if (frp != NULL && frp->fr_layout == frp2->fr_layout) {
|
||||||
|
// The frame above the parent has the same layout, have to merge
|
||||||
|
// the frames into this list.
|
||||||
|
if (frp->fr_child == frp2) {
|
||||||
|
frp->fr_child = frp2->fr_child;
|
||||||
}
|
}
|
||||||
frp2->fr_parent->fr_win = frp2->fr_win;
|
assert(frp2->fr_child);
|
||||||
if (frp2->fr_win != NULL) {
|
frp2->fr_child->fr_prev = frp2->fr_prev;
|
||||||
frp2->fr_win->w_frame = frp2->fr_parent;
|
if (frp2->fr_prev != NULL) {
|
||||||
|
frp2->fr_prev->fr_next = frp2->fr_child;
|
||||||
|
}
|
||||||
|
for (frame_T *frp3 = frp2->fr_child;; frp3 = frp3->fr_next) {
|
||||||
|
frp3->fr_parent = frp;
|
||||||
|
if (frp3->fr_next == NULL) {
|
||||||
|
frp3->fr_next = frp2->fr_next;
|
||||||
|
if (frp2->fr_next != NULL) {
|
||||||
|
frp2->fr_next->fr_prev = frp3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
frp = frp2->fr_parent;
|
|
||||||
if (topframe->fr_child == frp2) {
|
if (topframe->fr_child == frp2) {
|
||||||
topframe->fr_child = frp;
|
topframe->fr_child = frp;
|
||||||
}
|
}
|
||||||
xfree(frp2);
|
xfree(frp2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
frp2 = frp->fr_parent;
|
/// Undo changes from a prior call to winframe_remove, also restoring lost
|
||||||
if (frp2 != NULL && frp2->fr_layout == frp->fr_layout) {
|
/// vertical separators and statuslines, and changed window positions for
|
||||||
// The frame above the parent has the same layout, have to merge
|
/// windows within "unflat_altfr".
|
||||||
// the frames into this list.
|
/// Caller must ensure no other changes were made to the layout or window sizes!
|
||||||
if (frp2->fr_child == frp) {
|
void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr)
|
||||||
frp2->fr_child = frp->fr_child;
|
FUNC_ATTR_NONNULL_ALL
|
||||||
}
|
{
|
||||||
assert(frp->fr_child);
|
frame_T *frp = wp->w_frame;
|
||||||
frp->fr_child->fr_prev = frp->fr_prev;
|
|
||||||
if (frp->fr_prev != NULL) {
|
// Put "wp"'s frame back where it was.
|
||||||
frp->fr_prev->fr_next = frp->fr_child;
|
if (frp->fr_prev != NULL) {
|
||||||
}
|
frame_append(frp->fr_prev, frp);
|
||||||
frame_T *frp3;
|
} else {
|
||||||
for (frp3 = frp->fr_child;; frp3 = frp3->fr_next) {
|
frame_insert(frp->fr_next, frp);
|
||||||
frp3->fr_parent = frp2;
|
}
|
||||||
if (frp3->fr_next == NULL) {
|
|
||||||
frp3->fr_next = frp->fr_next;
|
// Vertical separators to the left may have been lost. Restore them.
|
||||||
if (frp->fr_next != NULL) {
|
if (wp->w_vsep_width == 0 && frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
|
||||||
frp->fr_next->fr_prev = frp3;
|
frame_add_vsep(frp->fr_prev);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
// Statuslines or horizontal separators above may have been lost. Restore them.
|
||||||
}
|
if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
|
||||||
if (topframe->fr_child == frp) {
|
if (global_stl_height() == 0 && wp->w_status_height == 0) {
|
||||||
topframe->fr_child = frp2;
|
frame_add_statusline(frp->fr_prev);
|
||||||
}
|
} else if (wp->w_hsep_height == 0) {
|
||||||
xfree(frp);
|
frame_add_hsep(frp->fr_prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wp;
|
int row = wp->w_winrow;
|
||||||
|
int col = wp->w_wincol;
|
||||||
|
|
||||||
|
// Restore the lost room that was redistributed to the altframe.
|
||||||
|
if (dir == 'v') {
|
||||||
|
frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height,
|
||||||
|
unflat_altfr == frp->fr_next, false);
|
||||||
|
row += frp->fr_height;
|
||||||
|
} else if (dir == 'h') {
|
||||||
|
frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width,
|
||||||
|
unflat_altfr == frp->fr_next, false);
|
||||||
|
col += frp->fr_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If rows/columns went to a window below/right, its positions need to be
|
||||||
|
// restored. Can only be done after the sizes have been updated.
|
||||||
|
if (unflat_altfr == frp->fr_next) {
|
||||||
|
frame_comp_pos(unflat_altfr, &row, &col);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If 'splitbelow' or 'splitright' is set, the space goes above or to the left
|
/// If 'splitbelow' or 'splitright' is set, the space goes above or to the left
|
||||||
@@ -3792,7 +3913,7 @@ void close_others(int message, int forceit)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (one_nonfloat() && !lastwin->w_floating) {
|
if (one_window(firstwin) && !lastwin->w_floating) {
|
||||||
if (message
|
if (message
|
||||||
&& !autocmd_busy) {
|
&& !autocmd_busy) {
|
||||||
msg(_(m_onlyone), 0);
|
msg(_(m_onlyone), 0);
|
||||||
@@ -4331,7 +4452,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
|
|||||||
if (wp->w_floating) {
|
if (wp->w_floating) {
|
||||||
if (wp->w_config.external) {
|
if (wp->w_config.external) {
|
||||||
win_remove(wp, old_curtab);
|
win_remove(wp, old_curtab);
|
||||||
win_append(lastwin_nofloating(), wp);
|
win_append(lastwin_nofloating(), wp, NULL);
|
||||||
} else {
|
} else {
|
||||||
ui_comp_remove_grid(&wp->w_grid_alloc);
|
ui_comp_remove_grid(&wp->w_grid_alloc);
|
||||||
}
|
}
|
||||||
@@ -4971,7 +5092,7 @@ win_T *win_alloc(win_T *after, bool hidden)
|
|||||||
block_autocmds();
|
block_autocmds();
|
||||||
// link the window in the window list
|
// link the window in the window list
|
||||||
if (!hidden) {
|
if (!hidden) {
|
||||||
win_append(after, new_wp);
|
win_append(after, new_wp, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_wp->w_wincol = 0;
|
new_wp->w_wincol = 0;
|
||||||
@@ -5141,21 +5262,29 @@ void win_free_grid(win_T *wp, bool reinit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append window "wp" in the window list after window "after".
|
/// Append window "wp" in the window list after window "after".
|
||||||
void win_append(win_T *after, win_T *wp)
|
///
|
||||||
|
/// @param tp tab page "win" (and "after", if not NULL) is in, NULL for current
|
||||||
|
void win_append(win_T *after, win_T *wp, tabpage_T *tp)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(2)
|
||||||
{
|
{
|
||||||
|
assert(tp == NULL || tp != curtab);
|
||||||
|
|
||||||
|
win_T **first = tp == NULL ? &firstwin : &tp->tp_firstwin;
|
||||||
|
win_T **last = tp == NULL ? &lastwin : &tp->tp_lastwin;
|
||||||
|
|
||||||
// after NULL is in front of the first
|
// after NULL is in front of the first
|
||||||
win_T *before = after == NULL ? firstwin : after->w_next;
|
win_T *before = after == NULL ? *first : after->w_next;
|
||||||
|
|
||||||
wp->w_next = before;
|
wp->w_next = before;
|
||||||
wp->w_prev = after;
|
wp->w_prev = after;
|
||||||
if (after == NULL) {
|
if (after == NULL) {
|
||||||
firstwin = wp;
|
*first = wp;
|
||||||
} else {
|
} else {
|
||||||
after->w_next = wp;
|
after->w_next = wp;
|
||||||
}
|
}
|
||||||
if (before == NULL) {
|
if (before == NULL) {
|
||||||
lastwin = wp;
|
*last = wp;
|
||||||
} else {
|
} else {
|
||||||
before->w_prev = wp;
|
before->w_prev = wp;
|
||||||
}
|
}
|
||||||
@@ -7058,7 +7187,7 @@ int global_stl_height(void)
|
|||||||
/// @param morewin pretend there are two or more windows if true.
|
/// @param morewin pretend there are two or more windows if true.
|
||||||
int last_stl_height(bool morewin)
|
int last_stl_height(bool morewin)
|
||||||
{
|
{
|
||||||
return (p_ls > 1 || (p_ls == 1 && (morewin || !one_nonfloat()))) ? STATUS_HEIGHT : 0;
|
return (p_ls > 1 || (p_ls == 1 && (morewin || !one_window(firstwin)))) ? STATUS_HEIGHT : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the minimal number of rows that is needed on the screen to display
|
/// Return the minimal number of rows that is needed on the screen to display
|
||||||
|
@@ -55,13 +55,26 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err)
|
|||||||
api_set_error(err, kErrorTypeException,
|
api_set_error(err, kErrorTypeException,
|
||||||
"Cannot change window from different tabpage into float");
|
"Cannot change window from different tabpage into float");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else if (cmdwin_win != NULL && !cmdwin_win->w_floating) {
|
||||||
|
// cmdwin can't become the only non-float. Check for others.
|
||||||
|
bool other_nonfloat = false;
|
||||||
|
for (win_T *wp2 = firstwin; wp2 != NULL && !wp2->w_floating; wp2 = wp2->w_next) {
|
||||||
|
if (wp2 != wp && wp2 != cmdwin_win) {
|
||||||
|
other_nonfloat = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!other_nonfloat) {
|
||||||
|
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int dir;
|
int dir;
|
||||||
winframe_remove(wp, &dir, NULL);
|
winframe_remove(wp, &dir, NULL, NULL);
|
||||||
XFREE_CLEAR(wp->w_frame);
|
XFREE_CLEAR(wp->w_frame);
|
||||||
win_comp_pos(); // recompute window positions
|
win_comp_pos(); // recompute window positions
|
||||||
win_remove(wp, NULL);
|
win_remove(wp, NULL);
|
||||||
win_append(lastwin_nofloating(), wp);
|
win_append(lastwin_nofloating(), wp, NULL);
|
||||||
}
|
}
|
||||||
wp->w_floating = true;
|
wp->w_floating = true;
|
||||||
wp->w_status_height = 0;
|
wp->w_status_height = 0;
|
||||||
@@ -306,3 +319,21 @@ win_T *win_float_find_preview(void)
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select an alternative window to `win` (assumed floating) in tabpage `tp`.
|
||||||
|
///
|
||||||
|
/// Useful for finding a window to switch to if `win` is the current window, but is then closed or
|
||||||
|
/// moved to a different tabpage.
|
||||||
|
///
|
||||||
|
/// @param tp `win`'s original tabpage, or NULL for current.
|
||||||
|
win_T *win_float_find_altwin(const win_T *win, const tabpage_T *tp)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
if (tp == NULL) {
|
||||||
|
return (win_valid(prevwin) && prevwin != win) ? prevwin : firstwin;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(tp != curtab);
|
||||||
|
return (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) ? tp->tp_prevwin
|
||||||
|
: tp->tp_firstwin;
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
|
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
|
||||||
|
local exec = helpers.exec
|
||||||
|
local feed = helpers.feed
|
||||||
local api = helpers.api
|
local api = helpers.api
|
||||||
local fn = helpers.fn
|
local fn = helpers.fn
|
||||||
local request = helpers.request
|
local request = helpers.request
|
||||||
@@ -86,6 +88,30 @@ describe('api/tabpage', function()
|
|||||||
pcall_err(api.nvim_tabpage_set_win, tab1, win3)
|
pcall_err(api.nvim_tabpage_set_win, tab1, win3)
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('does not switch window when textlocked or in the cmdwin', function()
|
||||||
|
local target_win = api.nvim_get_current_win()
|
||||||
|
feed('q:')
|
||||||
|
local cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
|
||||||
|
pcall_err(api.nvim_tabpage_set_win, 0, target_win)
|
||||||
|
)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
command('quit!')
|
||||||
|
|
||||||
|
exec(([[
|
||||||
|
new
|
||||||
|
call setline(1, 'foo')
|
||||||
|
setlocal debug=throw indentexpr=nvim_tabpage_set_win(0,%d)
|
||||||
|
]]):format(target_win))
|
||||||
|
cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Vim(normal):E5555: API call: Vim:E565: Not allowed to change text or change window',
|
||||||
|
pcall_err(command, 'normal! ==')
|
||||||
|
)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('{get,set,del}_var', function()
|
describe('{get,set,del}_var', function()
|
||||||
|
@@ -1364,6 +1364,308 @@ describe('API/win', function()
|
|||||||
},
|
},
|
||||||
}, layout)
|
}, layout)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local function setup_tabbed_autocmd_test()
|
||||||
|
local info = {}
|
||||||
|
info.orig_buf = api.nvim_get_current_buf()
|
||||||
|
info.other_buf = api.nvim_create_buf(true, true)
|
||||||
|
info.tab1_curwin = api.nvim_get_current_win()
|
||||||
|
info.tab1 = api.nvim_get_current_tabpage()
|
||||||
|
command('tab split | split')
|
||||||
|
info.tab2_curwin = api.nvim_get_current_win()
|
||||||
|
info.tab2 = api.nvim_get_current_tabpage()
|
||||||
|
exec([=[
|
||||||
|
tabfirst
|
||||||
|
let result = []
|
||||||
|
autocmd TabEnter * let result += [["TabEnter", nvim_get_current_tabpage()]]
|
||||||
|
autocmd TabLeave * let result += [["TabLeave", nvim_get_current_tabpage()]]
|
||||||
|
autocmd WinEnter * let result += [["WinEnter", win_getid()]]
|
||||||
|
autocmd WinLeave * let result += [["WinLeave", win_getid()]]
|
||||||
|
autocmd WinNew * let result += [["WinNew", win_getid()]]
|
||||||
|
autocmd WinClosed * let result += [["WinClosed", str2nr(expand("<afile>"))]]
|
||||||
|
autocmd BufEnter * let result += [["BufEnter", win_getid(), bufnr()]]
|
||||||
|
autocmd BufLeave * let result += [["BufLeave", win_getid(), bufnr()]]
|
||||||
|
autocmd BufWinEnter * let result += [["BufWinEnter", win_getid(), bufnr()]]
|
||||||
|
autocmd BufWinLeave * let result += [["BufWinLeave", win_getid(), bufnr()]]
|
||||||
|
]=])
|
||||||
|
return info
|
||||||
|
end
|
||||||
|
|
||||||
|
it('fires expected autocmds when creating splits without entering', function()
|
||||||
|
local info = setup_tabbed_autocmd_test()
|
||||||
|
|
||||||
|
-- For these, don't want BufWinEnter if visiting the same buffer, like :{s}buffer.
|
||||||
|
-- Same tabpage, same buffer.
|
||||||
|
local new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab1_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
}, eval('result'))
|
||||||
|
eq(info.tab1_curwin, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
-- Other tabpage, same buffer.
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab2_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
}, eval('result'))
|
||||||
|
eq(info.tab1_curwin, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
-- Same tabpage, other buffer.
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab1_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'BufWinEnter', new_win, info.other_buf },
|
||||||
|
}, eval('result'))
|
||||||
|
eq(info.tab1_curwin, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
-- Other tabpage, other buffer.
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab2_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'BufWinEnter', new_win, info.other_buf },
|
||||||
|
}, eval('result'))
|
||||||
|
eq(info.tab1_curwin, api.nvim_get_current_win())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('fires expected autocmds when creating and entering splits', function()
|
||||||
|
local info = setup_tabbed_autocmd_test()
|
||||||
|
|
||||||
|
-- Same tabpage, same buffer.
|
||||||
|
local new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab1_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'WinLeave', info.tab1_curwin },
|
||||||
|
{ 'WinEnter', new_win },
|
||||||
|
}, eval('result'))
|
||||||
|
|
||||||
|
-- Same tabpage, other buffer.
|
||||||
|
api.nvim_set_current_win(info.tab1_curwin)
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab1_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'WinLeave', info.tab1_curwin },
|
||||||
|
{ 'WinEnter', new_win },
|
||||||
|
{ 'BufLeave', new_win, info.orig_buf },
|
||||||
|
{ 'BufEnter', new_win, info.other_buf },
|
||||||
|
{ 'BufWinEnter', new_win, info.other_buf },
|
||||||
|
}, eval('result'))
|
||||||
|
|
||||||
|
-- For these, the other tabpage's prevwin and curwin will change like we switched from its old
|
||||||
|
-- curwin to the new window, so the extra events near TabEnter reflect that.
|
||||||
|
-- Other tabpage, same buffer.
|
||||||
|
api.nvim_set_current_win(info.tab1_curwin)
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab2_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'WinLeave', info.tab1_curwin },
|
||||||
|
{ 'TabLeave', info.tab1 },
|
||||||
|
|
||||||
|
{ 'WinEnter', info.tab2_curwin },
|
||||||
|
{ 'TabEnter', info.tab2 },
|
||||||
|
{ 'WinLeave', info.tab2_curwin },
|
||||||
|
{ 'WinEnter', new_win },
|
||||||
|
}, eval('result'))
|
||||||
|
|
||||||
|
-- Other tabpage, other buffer.
|
||||||
|
api.nvim_set_current_win(info.tab2_curwin)
|
||||||
|
api.nvim_set_current_win(info.tab1_curwin)
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'WinLeave', info.tab1_curwin },
|
||||||
|
{ 'TabLeave', info.tab1 },
|
||||||
|
|
||||||
|
{ 'WinEnter', info.tab2_curwin },
|
||||||
|
{ 'TabEnter', info.tab2 },
|
||||||
|
{ 'WinLeave', info.tab2_curwin },
|
||||||
|
{ 'WinEnter', new_win },
|
||||||
|
|
||||||
|
{ 'BufLeave', new_win, info.orig_buf },
|
||||||
|
{ 'BufEnter', new_win, info.other_buf },
|
||||||
|
{ 'BufWinEnter', new_win, info.other_buf },
|
||||||
|
}, eval('result'))
|
||||||
|
|
||||||
|
-- Other tabpage, other buffer; but other tabpage's curwin has a new buffer active.
|
||||||
|
api.nvim_set_current_win(info.tab2_curwin)
|
||||||
|
local new_buf = api.nvim_create_buf(true, true)
|
||||||
|
api.nvim_set_current_buf(new_buf)
|
||||||
|
api.nvim_set_current_win(info.tab1_curwin)
|
||||||
|
command('let result = []')
|
||||||
|
new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin })
|
||||||
|
eq({
|
||||||
|
{ 'WinNew', new_win },
|
||||||
|
{ 'BufLeave', info.tab1_curwin, info.orig_buf },
|
||||||
|
{ 'WinLeave', info.tab1_curwin },
|
||||||
|
{ 'TabLeave', info.tab1 },
|
||||||
|
|
||||||
|
{ 'WinEnter', info.tab2_curwin },
|
||||||
|
{ 'TabEnter', info.tab2 },
|
||||||
|
{ 'BufEnter', info.tab2_curwin, new_buf },
|
||||||
|
{ 'WinLeave', info.tab2_curwin },
|
||||||
|
{ 'WinEnter', new_win },
|
||||||
|
{ 'BufLeave', new_win, new_buf },
|
||||||
|
{ 'BufEnter', new_win, info.other_buf },
|
||||||
|
{ 'BufWinEnter', new_win, info.other_buf },
|
||||||
|
}, eval('result'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('OK when new window is moved to other tabpage by autocommands', function()
|
||||||
|
-- Use nvim_win_set_config in the autocommands, as other methods of moving a window to a
|
||||||
|
-- different tabpage (e.g: wincmd T) actually creates a new window.
|
||||||
|
local tab0 = api.nvim_get_current_tabpage()
|
||||||
|
local tab0_win = api.nvim_get_current_win()
|
||||||
|
command('tabnew')
|
||||||
|
local new_buf = api.nvim_create_buf(true, true)
|
||||||
|
local tab1 = api.nvim_get_current_tabpage()
|
||||||
|
local tab1_parent = api.nvim_get_current_win()
|
||||||
|
command(
|
||||||
|
'tabfirst | autocmd WinNew * ++once call nvim_win_set_config(0, #{split: "left", win: '
|
||||||
|
.. tab1_parent
|
||||||
|
.. '})'
|
||||||
|
)
|
||||||
|
local new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
|
||||||
|
eq(tab1, api.nvim_get_current_tabpage())
|
||||||
|
eq(new_win, api.nvim_get_current_win())
|
||||||
|
eq(new_buf, api.nvim_get_current_buf())
|
||||||
|
|
||||||
|
-- nvim_win_set_config called after entering. It doesn't follow a curwin that is moved to a
|
||||||
|
-- different tabpage, but instead moves to the win filling the space, which is tab0_win.
|
||||||
|
command(
|
||||||
|
'tabfirst | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "left", win: '
|
||||||
|
.. tab1_parent
|
||||||
|
.. '})'
|
||||||
|
)
|
||||||
|
new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
|
||||||
|
eq(tab0, api.nvim_get_current_tabpage())
|
||||||
|
eq(tab0_win, api.nvim_get_current_win())
|
||||||
|
eq(tab1, api.nvim_win_get_tabpage(new_win))
|
||||||
|
eq(new_buf, api.nvim_win_get_buf(new_win))
|
||||||
|
|
||||||
|
command(
|
||||||
|
'tabfirst | autocmd BufEnter * ++once call nvim_win_set_config(0, #{split: "left", win: '
|
||||||
|
.. tab1_parent
|
||||||
|
.. '})'
|
||||||
|
)
|
||||||
|
new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
|
||||||
|
eq(tab0, api.nvim_get_current_tabpage())
|
||||||
|
eq(tab0_win, api.nvim_get_current_win())
|
||||||
|
eq(tab1, api.nvim_win_get_tabpage(new_win))
|
||||||
|
eq(new_buf, api.nvim_win_get_buf(new_win))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not fire BufWinEnter if win_set_buf fails', function()
|
||||||
|
exec([[
|
||||||
|
set nohidden modified
|
||||||
|
autocmd WinNew * ++once only!
|
||||||
|
let fired = v:false
|
||||||
|
autocmd BufWinEnter * ++once let fired = v:true
|
||||||
|
]])
|
||||||
|
eq(
|
||||||
|
'Failed to set buffer 2',
|
||||||
|
pcall_err(api.nvim_open_win, api.nvim_create_buf(true, true), false, { split = 'left' })
|
||||||
|
)
|
||||||
|
eq(false, eval('fired'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('fires Buf* autocommands when `!enter` if window is entered via autocommands', function()
|
||||||
|
exec([[
|
||||||
|
autocmd WinNew * ++once only!
|
||||||
|
let fired = v:false
|
||||||
|
autocmd BufEnter * ++once let fired = v:true
|
||||||
|
]])
|
||||||
|
api.nvim_open_win(api.nvim_create_buf(true, true), false, { split = 'left' })
|
||||||
|
eq(true, eval('fired'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('no heap-use-after-free if target buffer deleted by autocommands', function()
|
||||||
|
local cur_buf = api.nvim_get_current_buf()
|
||||||
|
local new_buf = api.nvim_create_buf(true, true)
|
||||||
|
command('autocmd WinNew * ++once call nvim_buf_delete(' .. new_buf .. ', #{force: 1})')
|
||||||
|
api.nvim_open_win(new_buf, true, { split = 'left' })
|
||||||
|
eq(cur_buf, api.nvim_get_current_buf())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('checks if splitting disallowed', function()
|
||||||
|
command('split | autocmd WinEnter * ++once call nvim_open_win(0, 0, #{split: "right"})')
|
||||||
|
matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit'))
|
||||||
|
|
||||||
|
command('only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left"})')
|
||||||
|
matches(
|
||||||
|
'E1159: Cannot split a window when closing the buffer$',
|
||||||
|
pcall_err(command, 'new | quit')
|
||||||
|
)
|
||||||
|
|
||||||
|
local w = api.nvim_get_current_win()
|
||||||
|
command(
|
||||||
|
'only | new | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: '
|
||||||
|
.. w
|
||||||
|
.. '})'
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E1159: Cannot split a window when closing the buffer$',
|
||||||
|
pcall_err(api.nvim_win_close, w, true)
|
||||||
|
)
|
||||||
|
|
||||||
|
-- OK when using window to different buffer than `win`s.
|
||||||
|
w = api.nvim_get_current_win()
|
||||||
|
command(
|
||||||
|
'only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: '
|
||||||
|
.. w
|
||||||
|
.. '})'
|
||||||
|
)
|
||||||
|
command('new | quit')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('restores last known cursor position if BufWinEnter did not move it', function()
|
||||||
|
-- This test mostly exists to ensure BufWinEnter is executed before enter_buffer's epilogue.
|
||||||
|
local buf = api.nvim_get_current_buf()
|
||||||
|
insert([[
|
||||||
|
foo
|
||||||
|
bar baz .etc
|
||||||
|
i love autocommand bugs!
|
||||||
|
supercalifragilisticexpialidocious
|
||||||
|
marvim is actually a human
|
||||||
|
llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch
|
||||||
|
]])
|
||||||
|
api.nvim_win_set_cursor(0, { 5, 2 })
|
||||||
|
command('set nostartofline | enew')
|
||||||
|
local new_win = api.nvim_open_win(buf, false, { split = 'left' })
|
||||||
|
eq({ 5, 2 }, api.nvim_win_get_cursor(new_win))
|
||||||
|
|
||||||
|
exec([[
|
||||||
|
only!
|
||||||
|
autocmd BufWinEnter * ++once normal! j6l
|
||||||
|
]])
|
||||||
|
new_win = api.nvim_open_win(buf, false, { split = 'left' })
|
||||||
|
eq({ 2, 6 }, api.nvim_win_get_cursor(new_win))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not block all win_set_buf autocommands if !enter and !noautocmd', function()
|
||||||
|
local new_buf = fn.bufadd('foobarbaz')
|
||||||
|
exec([[
|
||||||
|
let triggered = ""
|
||||||
|
autocmd BufReadCmd * ++once let triggered = bufname()
|
||||||
|
]])
|
||||||
|
api.nvim_open_win(new_buf, false, { split = 'left' })
|
||||||
|
eq('foobarbaz', eval('triggered'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('sets error when no room', function()
|
||||||
|
matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_open_win, 0, true, { split = 'above', win = 0 })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_open_win, 0, true, { split = 'below', win = 0 })
|
||||||
|
)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('set_config', function()
|
describe('set_config', function()
|
||||||
@@ -1471,6 +1773,15 @@ describe('API/win', function()
|
|||||||
config = api.nvim_win_get_config(win)
|
config = api.nvim_win_get_config(win)
|
||||||
eq('', config.relative)
|
eq('', config.relative)
|
||||||
eq('below', config.split)
|
eq('below', config.split)
|
||||||
|
|
||||||
|
eq(
|
||||||
|
"non-float with 'win' requires at least 'split' or 'vertical'",
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = 0 })
|
||||||
|
)
|
||||||
|
eq(
|
||||||
|
"non-float with 'win' requires at least 'split' or 'vertical'",
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = 0, relative = '' })
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('creates top-level splits', function()
|
it('creates top-level splits', function()
|
||||||
@@ -1663,6 +1974,474 @@ describe('API/win', function()
|
|||||||
},
|
},
|
||||||
}, fn.winlayout())
|
}, fn.winlayout())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('closing new curwin when moving window to other tabpage works', function()
|
||||||
|
command('split | tabnew')
|
||||||
|
local t2_win = api.nvim_get_current_win()
|
||||||
|
command('tabfirst | autocmd WinEnter * ++once quit')
|
||||||
|
local t1_move_win = api.nvim_get_current_win()
|
||||||
|
-- win_set_config fails to switch away from "t1_move_win" because the WinEnter autocmd that
|
||||||
|
-- closed the window we're switched to returns us to "t1_move_win", as it filled the space.
|
||||||
|
eq(
|
||||||
|
'Failed to switch away from window ' .. t1_move_win,
|
||||||
|
pcall_err(api.nvim_win_set_config, t1_move_win, { win = t2_win, split = 'left' })
|
||||||
|
)
|
||||||
|
eq(t1_move_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
command('split | split | autocmd WinEnter * ++once quit')
|
||||||
|
t1_move_win = api.nvim_get_current_win()
|
||||||
|
-- In this case, we closed the window that we got switched to, but doing so didn't switch us
|
||||||
|
-- back to "t1_move_win", which is fine.
|
||||||
|
api.nvim_win_set_config(t1_move_win, { win = t2_win, split = 'left' })
|
||||||
|
neq(t1_move_win, api.nvim_get_current_win())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('messing with "win" or "parent" when moving "win" to other tabpage', function()
|
||||||
|
command('split | tabnew')
|
||||||
|
local t2 = api.nvim_get_current_tabpage()
|
||||||
|
local t2_win1 = api.nvim_get_current_win()
|
||||||
|
command('split')
|
||||||
|
local t2_win2 = api.nvim_get_current_win()
|
||||||
|
command('split')
|
||||||
|
local t2_win3 = api.nvim_get_current_win()
|
||||||
|
|
||||||
|
command('tabfirst | autocmd WinEnter * ++once call nvim_win_close(' .. t2_win1 .. ', 1)')
|
||||||
|
local cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Windows to split were closed',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_win1, split = 'left' })
|
||||||
|
)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
command('split | autocmd WinLeave * ++once quit!')
|
||||||
|
cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Windows to split were closed',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_win2, split = 'left' })
|
||||||
|
)
|
||||||
|
neq(cur_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
exec([[
|
||||||
|
split
|
||||||
|
autocmd WinLeave * ++once
|
||||||
|
\ call nvim_win_set_config(0, #{relative:'editor', row:0, col:0, width:5, height:5})
|
||||||
|
]])
|
||||||
|
cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Floating state of windows to split changed',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' })
|
||||||
|
)
|
||||||
|
eq('editor', api.nvim_win_get_config(0).relative)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
command('autocmd WinLeave * ++once wincmd J')
|
||||||
|
cur_win = api.nvim_get_current_win()
|
||||||
|
eq(
|
||||||
|
'Floating state of windows to split changed',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' })
|
||||||
|
)
|
||||||
|
eq('', api.nvim_win_get_config(0).relative)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
-- Try to make "parent" floating. This should give the same error as before, but because
|
||||||
|
-- changing a split from another tabpage into a float isn't supported yet, check for that
|
||||||
|
-- error instead for now.
|
||||||
|
-- Use ":silent!" to avoid the one second delay from printing the error message.
|
||||||
|
exec(([[
|
||||||
|
autocmd WinLeave * ++once silent!
|
||||||
|
\ call nvim_win_set_config(%d, #{relative:'editor', row:0, col:0, width:5, height:5})
|
||||||
|
]]):format(t2_win3))
|
||||||
|
cur_win = api.nvim_get_current_win()
|
||||||
|
api.nvim_win_set_config(0, { win = t2_win3, split = 'left' })
|
||||||
|
matches(
|
||||||
|
'Cannot change window from different tabpage into float$',
|
||||||
|
api.nvim_get_vvar('errmsg')
|
||||||
|
)
|
||||||
|
-- The error doesn't abort moving the window (or maybe it should, if that's wanted?)
|
||||||
|
neq(cur_win, api.nvim_get_current_win())
|
||||||
|
eq(t2, api.nvim_win_get_tabpage(cur_win))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('expected autocmds when moving window to other tabpage', function()
|
||||||
|
local new_curwin = api.nvim_get_current_win()
|
||||||
|
command('split')
|
||||||
|
local win = api.nvim_get_current_win()
|
||||||
|
command('tabnew')
|
||||||
|
local parent = api.nvim_get_current_win()
|
||||||
|
exec([[
|
||||||
|
tabfirst
|
||||||
|
let result = []
|
||||||
|
autocmd WinEnter * let result += ["Enter", win_getid()]
|
||||||
|
autocmd WinLeave * let result += ["Leave", win_getid()]
|
||||||
|
autocmd WinNew * let result += ["New", win_getid()]
|
||||||
|
]])
|
||||||
|
api.nvim_win_set_config(0, { win = parent, split = 'left' })
|
||||||
|
-- Shouldn't see WinNew, as we're not creating any new windows, just moving existing ones.
|
||||||
|
eq({ 'Leave', win, 'Enter', new_curwin }, eval('result'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('no autocmds when moving window within same tabpage', function()
|
||||||
|
local parent = api.nvim_get_current_win()
|
||||||
|
exec([[
|
||||||
|
split
|
||||||
|
let result = []
|
||||||
|
autocmd WinEnter * let result += ["Enter", win_getid()]
|
||||||
|
autocmd WinLeave * let result += ["Leave", win_getid()]
|
||||||
|
autocmd WinNew * let result += ["New", win_getid()]
|
||||||
|
]])
|
||||||
|
api.nvim_win_set_config(0, { win = parent, split = 'left' })
|
||||||
|
-- Shouldn't see any of those events, as we remain in the same window.
|
||||||
|
eq({}, eval('result'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('checks if splitting disallowed', function()
|
||||||
|
command('split | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "right"})')
|
||||||
|
matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit'))
|
||||||
|
|
||||||
|
command('autocmd BufHidden * ++once call nvim_win_set_config(0, #{split: "left"})')
|
||||||
|
matches(
|
||||||
|
'E1159: Cannot split a window when closing the buffer$',
|
||||||
|
pcall_err(command, 'new | quit')
|
||||||
|
)
|
||||||
|
|
||||||
|
-- OK when using window to different buffer.
|
||||||
|
local w = api.nvim_get_current_win()
|
||||||
|
command('autocmd BufHidden * ++once call nvim_win_set_config(' .. w .. ', #{split: "left"})')
|
||||||
|
command('new | quit')
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- Returns a function to get information about the window layout, sizes and positions of a
|
||||||
|
--- tabpage.
|
||||||
|
local function define_tp_info_function()
|
||||||
|
exec_lua([[
|
||||||
|
function tp_info(tp)
|
||||||
|
return {
|
||||||
|
layout = vim.fn.winlayout(vim.api.nvim_tabpage_get_number(tp)),
|
||||||
|
pos_sizes = vim.tbl_map(
|
||||||
|
function(w)
|
||||||
|
local pos = vim.fn.win_screenpos(w)
|
||||||
|
return {
|
||||||
|
row = pos[1],
|
||||||
|
col = pos[2],
|
||||||
|
width = vim.fn.winwidth(w),
|
||||||
|
height = vim.fn.winheight(w)
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
vim.api.nvim_tabpage_list_wins(tp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
]])
|
||||||
|
|
||||||
|
return function(tp)
|
||||||
|
return exec_lua('return tp_info(...)', tp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it('attempt to move window with no room', function()
|
||||||
|
-- Fill the 2nd tabpage full of windows until we run out of room.
|
||||||
|
-- Use &laststatus=0 to ensure restoring missing statuslines doesn't affect things.
|
||||||
|
command('set laststatus=0 | tabnew')
|
||||||
|
matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
|
||||||
|
command('vsplit | wincmd | | wincmd p')
|
||||||
|
local t2 = api.nvim_get_current_tabpage()
|
||||||
|
local t2_cur_win = api.nvim_get_current_win()
|
||||||
|
local t2_top_split = fn.win_getid(1)
|
||||||
|
local t2_bot_split = fn.win_getid(fn.winnr('$'))
|
||||||
|
local t2_float = api.nvim_open_win(
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 10, height = 10 }
|
||||||
|
)
|
||||||
|
local t2_float_config = api.nvim_win_get_config(t2_float)
|
||||||
|
local tp_info = define_tp_info_function()
|
||||||
|
local t2_info = tp_info(t2)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'below' })
|
||||||
|
)
|
||||||
|
eq(t2_cur_win, api.nvim_get_current_win())
|
||||||
|
eq(t2_info, tp_info(t2))
|
||||||
|
eq(t2_float_config, api.nvim_win_get_config(t2_float))
|
||||||
|
|
||||||
|
-- Try to move windows from the 1st tabpage to the 2nd.
|
||||||
|
command('tabfirst | split | wincmd _')
|
||||||
|
local t1 = api.nvim_get_current_tabpage()
|
||||||
|
local t1_cur_win = api.nvim_get_current_win()
|
||||||
|
local t1_float = api.nvim_open_win(
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
{ relative = 'editor', row = 5, col = 3, width = 7, height = 6 }
|
||||||
|
)
|
||||||
|
local t1_float_config = api.nvim_win_get_config(t1_float)
|
||||||
|
local t1_info = tp_info(t1)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'below' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'above' })
|
||||||
|
)
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'below' })
|
||||||
|
)
|
||||||
|
eq(t1_cur_win, api.nvim_get_current_win())
|
||||||
|
eq(t1_info, tp_info(t1))
|
||||||
|
eq(t1_float_config, api.nvim_win_get_config(t1_float))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('attempt to move window from other tabpage with no room', function()
|
||||||
|
-- Fill up the 1st tabpage with horizontal splits, then create a 2nd with only a few. Go back
|
||||||
|
-- to the 1st and try to move windows from the 2nd (while it's non-current) to it. Check that
|
||||||
|
-- window positions and sizes in the 2nd are unchanged.
|
||||||
|
command('set laststatus=0')
|
||||||
|
matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
|
||||||
|
|
||||||
|
command('tab split')
|
||||||
|
local t2 = api.nvim_get_current_tabpage()
|
||||||
|
local t2_top = api.nvim_get_current_win()
|
||||||
|
command('belowright split')
|
||||||
|
local t2_mid_left = api.nvim_get_current_win()
|
||||||
|
command('belowright vsplit')
|
||||||
|
local t2_mid_right = api.nvim_get_current_win()
|
||||||
|
command('split | wincmd J')
|
||||||
|
local t2_bot = api.nvim_get_current_win()
|
||||||
|
local tp_info = define_tp_info_function()
|
||||||
|
local t2_info = tp_info(t2)
|
||||||
|
eq({
|
||||||
|
'col',
|
||||||
|
{
|
||||||
|
{ 'leaf', t2_top },
|
||||||
|
{
|
||||||
|
'row',
|
||||||
|
{
|
||||||
|
{ 'leaf', t2_mid_left },
|
||||||
|
{ 'leaf', t2_mid_right },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ 'leaf', t2_bot },
|
||||||
|
},
|
||||||
|
}, t2_info.layout)
|
||||||
|
|
||||||
|
local function try_move_t2_wins_to_t1()
|
||||||
|
for _, w in ipairs({ t2_bot, t2_mid_left, t2_mid_right, t2_top }) do
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, w, { win = 0, split = 'below' })
|
||||||
|
)
|
||||||
|
eq(t2_info, tp_info(t2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
command('tabfirst')
|
||||||
|
try_move_t2_wins_to_t1()
|
||||||
|
-- Go to the 2nd tabpage to ensure nothing changes after win_comp_pos, last_status, .etc.
|
||||||
|
-- from enter_tabpage.
|
||||||
|
command('tabnext')
|
||||||
|
eq(t2_info, tp_info(t2))
|
||||||
|
|
||||||
|
-- Check things are fine with the global statusline too, for good measure.
|
||||||
|
-- Set it while the 2nd tabpage is current, so last_status runs for it.
|
||||||
|
command('set laststatus=3')
|
||||||
|
t2_info = tp_info(t2)
|
||||||
|
command('tabfirst')
|
||||||
|
try_move_t2_wins_to_t1()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('handles cmdwin and textlock restrictions', function()
|
||||||
|
command('tabnew')
|
||||||
|
local t2 = api.nvim_get_current_tabpage()
|
||||||
|
local t2_win = api.nvim_get_current_win()
|
||||||
|
command('tabfirst')
|
||||||
|
local t1_move_win = api.nvim_get_current_win()
|
||||||
|
command('split')
|
||||||
|
|
||||||
|
-- Can't move the cmdwin, or its old curwin to a different tabpage.
|
||||||
|
local old_curwin = api.nvim_get_current_win()
|
||||||
|
feed('q:')
|
||||||
|
eq(
|
||||||
|
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
|
||||||
|
pcall_err(api.nvim_win_set_config, 0, { split = 'left', win = t2_win })
|
||||||
|
)
|
||||||
|
eq(
|
||||||
|
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
|
||||||
|
pcall_err(api.nvim_win_set_config, old_curwin, { split = 'left', win = t2_win })
|
||||||
|
)
|
||||||
|
-- But we can move other windows.
|
||||||
|
api.nvim_win_set_config(t1_move_win, { split = 'left', win = t2_win })
|
||||||
|
eq(t2, api.nvim_win_get_tabpage(t1_move_win))
|
||||||
|
command('quit!')
|
||||||
|
|
||||||
|
-- Can't configure windows such that the cmdwin would become the only non-float.
|
||||||
|
command('only!')
|
||||||
|
feed('q:')
|
||||||
|
eq(
|
||||||
|
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
old_curwin,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- old_curwin is now no longer the only other non-float, so we can make it floating now.
|
||||||
|
local t1_new_win = api.nvim_open_win(
|
||||||
|
api.nvim_create_buf(true, true),
|
||||||
|
false,
|
||||||
|
{ split = 'left', win = old_curwin }
|
||||||
|
)
|
||||||
|
api.nvim_win_set_config(
|
||||||
|
old_curwin,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
|
||||||
|
)
|
||||||
|
eq('editor', api.nvim_win_get_config(old_curwin).relative)
|
||||||
|
-- ...which means we shouldn't be able to also make the new window floating too!
|
||||||
|
eq(
|
||||||
|
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
t1_new_win,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
-- Nothing ought to stop us from making the cmdwin itself floating, though...
|
||||||
|
api.nvim_win_set_config(0, { relative = 'editor', row = 0, col = 0, width = 5, height = 5 })
|
||||||
|
eq('editor', api.nvim_win_get_config(0).relative)
|
||||||
|
-- We can't make our new window from before floating too, as it's now the only non-float.
|
||||||
|
eq(
|
||||||
|
'Cannot change last window into float',
|
||||||
|
pcall_err(
|
||||||
|
api.nvim_win_set_config,
|
||||||
|
t1_new_win,
|
||||||
|
{ relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
command('quit!')
|
||||||
|
|
||||||
|
-- Can't switch away from window before moving it to a different tabpage during textlock.
|
||||||
|
exec(([[
|
||||||
|
new
|
||||||
|
call setline(1, 'foo')
|
||||||
|
setlocal debug=throw indentexpr=nvim_win_set_config(0,#{split:'left',win:%d})
|
||||||
|
]]):format(t2_win))
|
||||||
|
local cur_win = api.nvim_get_current_win()
|
||||||
|
matches(
|
||||||
|
'E565: Not allowed to change text or change window$',
|
||||||
|
pcall_err(command, 'normal! ==')
|
||||||
|
)
|
||||||
|
eq(cur_win, api.nvim_get_current_win())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('updates statusline when moving bottom split', function()
|
||||||
|
local screen = Screen.new(10, 10)
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
|
||||||
|
[1] = { bold = true, reverse = true }, -- StatusLine
|
||||||
|
})
|
||||||
|
screen:attach()
|
||||||
|
exec([[
|
||||||
|
set laststatus=0
|
||||||
|
belowright split
|
||||||
|
call nvim_win_set_config(0, #{split: 'above', win: win_getid(winnr('#'))})
|
||||||
|
]])
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{0:~ }|*3
|
||||||
|
{1:[No Name] }|
|
||||||
|
|
|
||||||
|
{0:~ }|*3
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("updates tp_curwin of moved window's original tabpage", function()
|
||||||
|
local t1 = api.nvim_get_current_tabpage()
|
||||||
|
command('tab split | split')
|
||||||
|
local t2 = api.nvim_get_current_tabpage()
|
||||||
|
local t2_alt_win = api.nvim_get_current_win()
|
||||||
|
command('vsplit')
|
||||||
|
local t2_cur_win = api.nvim_get_current_win()
|
||||||
|
command('tabprevious')
|
||||||
|
eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
|
||||||
|
-- tp_curwin is unchanged when moved within the same tabpage.
|
||||||
|
api.nvim_win_set_config(t2_cur_win, { split = 'left', win = t2_alt_win })
|
||||||
|
eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
|
||||||
|
-- Also unchanged if the move failed.
|
||||||
|
command('let &winwidth = &columns | let &winminwidth = &columns')
|
||||||
|
matches(
|
||||||
|
'E36: Not enough room$',
|
||||||
|
pcall_err(api.nvim_win_set_config, t2_cur_win, { split = 'left', win = 0 })
|
||||||
|
)
|
||||||
|
eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
command('set winminwidth& winwidth&')
|
||||||
|
|
||||||
|
-- But is changed if successfully moved to a different tabpage.
|
||||||
|
api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 })
|
||||||
|
eq(t2_alt_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
eq(t1, api.nvim_win_get_tabpage(t2_cur_win))
|
||||||
|
|
||||||
|
-- Now do it for a float, which has different altwin logic.
|
||||||
|
command('tabnext')
|
||||||
|
t2_cur_win =
|
||||||
|
api.nvim_open_win(0, true, { relative = 'editor', row = 5, col = 5, width = 5, height = 5 })
|
||||||
|
eq(t2_alt_win, fn.win_getid(fn.winnr('#')))
|
||||||
|
command('tabprevious')
|
||||||
|
eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
|
||||||
|
api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 })
|
||||||
|
eq(t2_alt_win, api.nvim_tabpage_get_win(t2))
|
||||||
|
eq(t1, api.nvim_win_get_tabpage(t2_cur_win))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('get_config', function()
|
describe('get_config', function()
|
||||||
|
@@ -3664,6 +3664,20 @@ describe('lua stdlib', function()
|
|||||||
]]
|
]]
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('layout in current tabpage does not affect windows in others', function()
|
||||||
|
command('tab split')
|
||||||
|
local t2_move_win = api.nvim_get_current_win()
|
||||||
|
command('vsplit')
|
||||||
|
local t2_other_win = api.nvim_get_current_win()
|
||||||
|
command('tabprevious')
|
||||||
|
matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
|
||||||
|
command('vsplit')
|
||||||
|
|
||||||
|
-- Without vim-patch:8.2.3862, this gives E36, despite just the 1st tabpage being full.
|
||||||
|
exec_lua('vim.api.nvim_win_call(..., function() vim.cmd.wincmd "J" end)', t2_move_win)
|
||||||
|
eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim.iconv', function()
|
describe('vim.iconv', function()
|
||||||
|
@@ -550,6 +550,43 @@ describe('float window', function()
|
|||||||
eq({ w0 }, api.nvim_list_wins())
|
eq({ w0 }, api.nvim_list_wins())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('win_splitmove() can move float into a split', function()
|
||||||
|
command('split')
|
||||||
|
eq({'col', {{'leaf', 1001}, {'leaf', 1000}}}, fn.winlayout())
|
||||||
|
|
||||||
|
local win1 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5})
|
||||||
|
fn.win_splitmove(win1, 1001, {vertical = true})
|
||||||
|
eq({'col', {{'row', {{'leaf', win1}, {'leaf', 1001}}}, {'leaf', 1000}}}, fn.winlayout())
|
||||||
|
eq('', api.nvim_win_get_config(win1).relative)
|
||||||
|
|
||||||
|
-- Should be unable to create a split relative to a float, though.
|
||||||
|
local win2 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5})
|
||||||
|
eq('Vim:E957: Invalid window number', pcall_err(fn.win_splitmove, win1, win2, {vertical = true}))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('tp_curwin updated if external window is moved into split', function()
|
||||||
|
local screen = Screen.new(20, 7)
|
||||||
|
screen:attach { ext_multigrid = true }
|
||||||
|
|
||||||
|
command('tabnew')
|
||||||
|
local external_win = api.nvim_open_win(0, true, {external = true, width = 5, height = 5})
|
||||||
|
eq(external_win, api.nvim_get_current_win())
|
||||||
|
eq(2, fn.tabpagenr())
|
||||||
|
command('tabfirst')
|
||||||
|
api.nvim_set_current_win(external_win)
|
||||||
|
eq(external_win, api.nvim_get_current_win())
|
||||||
|
eq(1, fn.tabpagenr())
|
||||||
|
|
||||||
|
command('wincmd J')
|
||||||
|
eq(external_win, api.nvim_get_current_win())
|
||||||
|
eq(false, api.nvim_win_get_config(external_win).external)
|
||||||
|
command('tabnext')
|
||||||
|
eq(2, fn.tabpagenr())
|
||||||
|
neq(external_win, api.nvim_get_current_win())
|
||||||
|
|
||||||
|
screen:detach()
|
||||||
|
end)
|
||||||
|
|
||||||
describe('with only one tabpage,', function()
|
describe('with only one tabpage,', 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_buf, old_win
|
local old_buf, old_win
|
||||||
@@ -836,6 +873,39 @@ describe('float window', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe(':close on non-float with floating windows', function()
|
||||||
|
it('does not quit Nvim if BufWinLeave makes it the only non-float', function()
|
||||||
|
exec([[
|
||||||
|
let firstbuf = bufnr()
|
||||||
|
new
|
||||||
|
let midwin = win_getid()
|
||||||
|
new
|
||||||
|
setlocal bufhidden=wipe
|
||||||
|
call nvim_win_set_config(midwin,
|
||||||
|
\ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5})
|
||||||
|
autocmd BufWinLeave * ++once exe firstbuf .. 'bwipe!'
|
||||||
|
]])
|
||||||
|
eq('Vim(close):E855: Autocommands caused command to abort', pcall_err(command, 'close'))
|
||||||
|
assert_alive()
|
||||||
|
end)
|
||||||
|
|
||||||
|
pending('does not crash if BufWinLeave makes it the only non-float in tabpage', function()
|
||||||
|
exec([[
|
||||||
|
tabnew
|
||||||
|
let firstbuf = bufnr()
|
||||||
|
new
|
||||||
|
let midwin = win_getid()
|
||||||
|
new
|
||||||
|
setlocal bufhidden=wipe
|
||||||
|
call nvim_win_set_config(midwin,
|
||||||
|
\ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5})
|
||||||
|
autocmd BufWinLeave * ++once exe firstbuf .. 'bwipe!'
|
||||||
|
]])
|
||||||
|
eq('Vim(close):E855: Autocommands caused command to abort', pcall_err(command, 'close'))
|
||||||
|
assert_alive()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
local function with_ext_multigrid(multigrid)
|
local function with_ext_multigrid(multigrid)
|
||||||
local screen, attrs
|
local screen, attrs
|
||||||
before_each(function()
|
before_each(function()
|
||||||
@@ -9101,6 +9171,22 @@ describe('float window', function()
|
|||||||
]]}
|
]]}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('attempt to turn into split with no room', function()
|
||||||
|
eq('Vim(split):E36: Not enough room', pcall_err(command, 'execute "split |"->repeat(&lines)'))
|
||||||
|
command('vsplit | wincmd | | wincmd p')
|
||||||
|
api.nvim_open_win(0, true, {relative = "editor", row = 0, col = 0, width = 5, height = 5})
|
||||||
|
local config = api.nvim_win_get_config(0)
|
||||||
|
eq('editor', config.relative)
|
||||||
|
|
||||||
|
local layout = fn.winlayout()
|
||||||
|
local restcmd = fn.winrestcmd()
|
||||||
|
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd K'))
|
||||||
|
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd J'))
|
||||||
|
eq(layout, fn.winlayout())
|
||||||
|
eq(restcmd, fn.winrestcmd())
|
||||||
|
eq(config, api.nvim_win_get_config(0))
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe('with ext_multigrid', function()
|
describe('with ext_multigrid', function()
|
||||||
|
@@ -11,6 +11,7 @@ local exec = helpers.exec
|
|||||||
local exec_lua = helpers.exec_lua
|
local exec_lua = helpers.exec_lua
|
||||||
local eval = helpers.eval
|
local eval = helpers.eval
|
||||||
local sleep = vim.uv.sleep
|
local sleep = vim.uv.sleep
|
||||||
|
local pcall_err = helpers.pcall_err
|
||||||
|
|
||||||
local mousemodels = { 'extend', 'popup', 'popup_setpos' }
|
local mousemodels = { 'extend', 'popup', 'popup_setpos' }
|
||||||
|
|
||||||
@@ -474,6 +475,25 @@ describe('global statusline', function()
|
|||||||
|
|
|
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('horizontal separators unchanged when failing to split-move window', function()
|
||||||
|
exec([[
|
||||||
|
botright split
|
||||||
|
let &winwidth = &columns
|
||||||
|
let &winminwidth = &columns
|
||||||
|
]])
|
||||||
|
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L'))
|
||||||
|
command('mode')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
{1:~ }|*5
|
||||||
|
────────────────────────────────────────────────────────────|
|
||||||
|
^ |
|
||||||
|
{1:~ }|*6
|
||||||
|
{2:[No Name] 0,0-1 All}|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('statusline does not crash if it has Arabic characters #19447', function()
|
it('statusline does not crash if it has Arabic characters #19447', function()
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
source view_util.vim
|
source view_util.vim
|
||||||
source check.vim
|
source check.vim
|
||||||
source vim9.vim
|
source vim9.vim
|
||||||
|
source term_util.vim
|
||||||
|
|
||||||
func NestedEval()
|
func NestedEval()
|
||||||
let nested = execute('echo "nested\nlines"')
|
let nested = execute('echo "nested\nlines"')
|
||||||
@@ -177,6 +178,27 @@ func Test_win_execute_visual_redraw()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_win_execute_on_startup()
|
||||||
|
CheckRunVimInTerminal
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
[repeat('x', &columns)]->writefile('Xfile1')
|
||||||
|
silent tabedit Xfile2
|
||||||
|
var id = win_getid()
|
||||||
|
silent tabedit Xfile3
|
||||||
|
autocmd VimEnter * win_execute(id, 'close')
|
||||||
|
END
|
||||||
|
call writefile(lines, 'XwinExecute')
|
||||||
|
let buf = RunVimInTerminal('-p Xfile1 -Nu XwinExecute', {})
|
||||||
|
|
||||||
|
" this was crashing on exit with EXITFREE defined
|
||||||
|
call StopVimInTerminal(buf)
|
||||||
|
|
||||||
|
call delete('XwinExecute')
|
||||||
|
call delete('Xfile1')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_execute_cmd_with_null()
|
func Test_execute_cmd_with_null()
|
||||||
call assert_equal("", execute(v:_null_string))
|
call assert_equal("", execute(v:_null_string))
|
||||||
call assert_equal("", execute(v:_null_list))
|
call assert_equal("", execute(v:_null_list))
|
||||||
|
@@ -963,4 +963,42 @@ func Test_smoothscroll_insert_bottom()
|
|||||||
call StopVimInTerminal(buf)
|
call StopVimInTerminal(buf)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_smoothscroll_in_zero_width_window()
|
||||||
|
set cpo+=n number smoothscroll
|
||||||
|
set winwidth=99999 winminwidth=0
|
||||||
|
|
||||||
|
vsplit
|
||||||
|
call assert_equal(0, winwidth(winnr('#')))
|
||||||
|
call win_execute(win_getid(winnr('#')), "norm! \<C-Y>")
|
||||||
|
|
||||||
|
only!
|
||||||
|
set winwidth& winminwidth&
|
||||||
|
set cpo-=n nonumber nosmoothscroll
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_smoothscroll_textoff_small_winwidth()
|
||||||
|
set smoothscroll number
|
||||||
|
call setline(1, 'llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch')
|
||||||
|
vsplit
|
||||||
|
|
||||||
|
let textoff = getwininfo(win_getid())[0].textoff
|
||||||
|
execute 'vertical resize' textoff + 1
|
||||||
|
redraw
|
||||||
|
call assert_equal(0, winsaveview().skipcol)
|
||||||
|
execute "normal! 0\<C-E>"
|
||||||
|
redraw
|
||||||
|
call assert_equal(1, winsaveview().skipcol)
|
||||||
|
execute 'vertical resize' textoff - 1
|
||||||
|
" This caused a signed integer overflow.
|
||||||
|
redraw
|
||||||
|
call assert_equal(1, winsaveview().skipcol)
|
||||||
|
execute 'vertical resize' textoff
|
||||||
|
" This caused an infinite loop.
|
||||||
|
redraw
|
||||||
|
call assert_equal(1, winsaveview().skipcol)
|
||||||
|
|
||||||
|
%bw!
|
||||||
|
set smoothscroll& number&
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@@ -272,6 +272,16 @@ func Test_window_split_no_room()
|
|||||||
for s in range(1, hor_split_count) | split | endfor
|
for s in range(1, hor_split_count) | split | endfor
|
||||||
call assert_fails('split', 'E36:')
|
call assert_fails('split', 'E36:')
|
||||||
|
|
||||||
|
botright vsplit
|
||||||
|
wincmd |
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
call assert_fails('wincmd J', 'E36:')
|
||||||
|
call assert_fails('wincmd K', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
only
|
||||||
|
|
||||||
" N vertical windows need >= 2*(N - 1) + 1 columns:
|
" N vertical windows need >= 2*(N - 1) + 1 columns:
|
||||||
" - 1 column + 1 separator for each window (except last window)
|
" - 1 column + 1 separator for each window (except last window)
|
||||||
" - 1 column for the last window which does not have separator
|
" - 1 column for the last window which does not have separator
|
||||||
@@ -284,7 +294,39 @@ func Test_window_split_no_room()
|
|||||||
for s in range(1, ver_split_count) | vsplit | endfor
|
for s in range(1, ver_split_count) | vsplit | endfor
|
||||||
call assert_fails('vsplit', 'E36:')
|
call assert_fails('vsplit', 'E36:')
|
||||||
|
|
||||||
|
split
|
||||||
|
wincmd |
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
|
||||||
|
" Check that the last statusline isn't lost.
|
||||||
|
" Set its window's width to 2 for the test.
|
||||||
|
wincmd j
|
||||||
|
set laststatus=0 winminwidth=0
|
||||||
|
vertical resize 2
|
||||||
|
set winminwidth&
|
||||||
|
call setwinvar(winnr('k'), '&statusline', '@#')
|
||||||
|
let last_stl_row = win_screenpos(0)[0] - 1
|
||||||
|
redraw
|
||||||
|
call assert_equal('@#|', GetScreenStr(last_stl_row))
|
||||||
|
call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
|
||||||
|
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
call setwinvar(winnr('k'), '&statusline', '=-')
|
||||||
|
redraw
|
||||||
|
call assert_equal('=-|', GetScreenStr(last_stl_row))
|
||||||
|
call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
|
||||||
|
|
||||||
%bw!
|
%bw!
|
||||||
|
set laststatus&
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_window_exchange()
|
func Test_window_exchange()
|
||||||
@@ -1024,6 +1066,19 @@ func Test_win_splitmove()
|
|||||||
leftabove split b
|
leftabove split b
|
||||||
leftabove vsplit c
|
leftabove vsplit c
|
||||||
leftabove split d
|
leftabove split d
|
||||||
|
|
||||||
|
" win_splitmove doesn't actually create or close any windows, so expect an
|
||||||
|
" unchanged winid and no WinNew/WinClosed events, like :wincmd H/J/K/L.
|
||||||
|
let s:triggered = []
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
" Nvim: WinNewPre not ported yet. Also needs full port of v9.1.0117 to pass.
|
||||||
|
" au WinNewPre * let s:triggered += ['WinNewPre']
|
||||||
|
au WinNew * let s:triggered += ['WinNew', win_getid()]
|
||||||
|
au WinClosed * let s:triggered += ['WinClosed', str2nr(expand('<afile>'))]
|
||||||
|
augroup END
|
||||||
|
let winid = win_getid()
|
||||||
|
|
||||||
call assert_equal(0, win_splitmove(winnr(), winnr('l')))
|
call assert_equal(0, win_splitmove(winnr(), winnr('l')))
|
||||||
call assert_equal(bufname(winbufnr(1)), 'c')
|
call assert_equal(bufname(winbufnr(1)), 'c')
|
||||||
call assert_equal(bufname(winbufnr(2)), 'd')
|
call assert_equal(bufname(winbufnr(2)), 'd')
|
||||||
@@ -1046,6 +1101,11 @@ func Test_win_splitmove()
|
|||||||
call assert_equal(bufname(winbufnr(3)), 'a')
|
call assert_equal(bufname(winbufnr(3)), 'a')
|
||||||
call assert_equal(bufname(winbufnr(4)), 'd')
|
call assert_equal(bufname(winbufnr(4)), 'd')
|
||||||
call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E1297:')
|
call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E1297:')
|
||||||
|
call assert_equal([], s:triggered)
|
||||||
|
call assert_equal(winid, win_getid())
|
||||||
|
|
||||||
|
unlet! s:triggered
|
||||||
|
au! WinSplitMove
|
||||||
only | bd
|
only | bd
|
||||||
|
|
||||||
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
|
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
|
||||||
@@ -1055,18 +1115,53 @@ func Test_win_splitmove()
|
|||||||
tabnew
|
tabnew
|
||||||
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
|
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
|
||||||
tabclose
|
tabclose
|
||||||
endfunc
|
|
||||||
|
|
||||||
func Test_floatwin_splitmove()
|
split
|
||||||
vsplit
|
augroup WinSplitMove
|
||||||
let win2 = win_getid()
|
au!
|
||||||
let popup_winid = nvim_open_win(0, 0, {'relative': 'win',
|
au WinEnter * ++once call win_gotoid(win_getid(winnr('#')))
|
||||||
\ 'row': 3, 'col': 3, 'width': 12, 'height': 3})
|
augroup END
|
||||||
call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:')
|
call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
|
||||||
call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:')
|
|
||||||
|
|
||||||
call nvim_win_close(popup_winid, 1)
|
augroup WinSplitMove
|
||||||
bwipe
|
au!
|
||||||
|
au WinLeave * ++once quit
|
||||||
|
augroup END
|
||||||
|
call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
|
||||||
|
|
||||||
|
split
|
||||||
|
split
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au WinEnter * ++once let s:triggered = v:true
|
||||||
|
\| call assert_fails('call win_splitmove(winnr("$"), winnr())', 'E242:')
|
||||||
|
augroup END
|
||||||
|
quit
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
unlet! s:triggered
|
||||||
|
|
||||||
|
new
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au BufHidden * ++once let s:triggered = v:true
|
||||||
|
\| call assert_fails('call win_splitmove(winnr("#"), winnr())', 'E1159:')
|
||||||
|
augroup END
|
||||||
|
hide
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
unlet! s:triggered
|
||||||
|
|
||||||
|
split
|
||||||
|
let close_win = winnr('#')
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au WinEnter * ++once quit!
|
||||||
|
augroup END
|
||||||
|
call win_splitmove(close_win, winnr())
|
||||||
|
call assert_equal(0, win_id2win(close_win))
|
||||||
|
|
||||||
|
au! WinSplitMove
|
||||||
|
augroup! WinSplitMove
|
||||||
|
%bw!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for the :only command
|
" Test for the :only command
|
||||||
@@ -2006,24 +2101,97 @@ func Test_new_help_window_on_error()
|
|||||||
call assert_equal(expand("<cword>"), "'mod'")
|
call assert_equal(expand("<cword>"), "'mod'")
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_smoothscroll_in_zero_width_window()
|
func Test_splitmove_flatten_frame()
|
||||||
let save_lines = &lines
|
split
|
||||||
let save_columns = &columns
|
vsplit
|
||||||
|
|
||||||
winsize 0 24
|
wincmd L
|
||||||
set cpo+=n
|
let layout = winlayout()
|
||||||
exe "noremap 0 \<C-W>n\<C-W>L"
|
wincmd K
|
||||||
norm 000000
|
wincmd L
|
||||||
set number smoothscroll
|
call assert_equal(winlayout(), layout)
|
||||||
exe "norm \<C-Y>"
|
|
||||||
|
|
||||||
only!
|
only!
|
||||||
let &lines = save_lines
|
|
||||||
let &columns = save_columns
|
|
||||||
set cpo-=n
|
|
||||||
unmap 0
|
|
||||||
set nonumber nosmoothscroll
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_autocmd_window_force_room()
|
||||||
|
" Open as many windows as possible
|
||||||
|
while v:true
|
||||||
|
try
|
||||||
|
split
|
||||||
|
catch /E36:/
|
||||||
|
break
|
||||||
|
endtry
|
||||||
|
endwhile
|
||||||
|
while v:true
|
||||||
|
try
|
||||||
|
vsplit
|
||||||
|
catch /E36:/
|
||||||
|
break
|
||||||
|
endtry
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
wincmd j
|
||||||
|
vsplit
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd J', 'E36:')
|
||||||
|
call assert_fails('wincmd K', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
|
||||||
|
edit unload me
|
||||||
|
enew
|
||||||
|
bunload! unload\ me
|
||||||
|
augroup AucmdWinForceRoom
|
||||||
|
au!
|
||||||
|
au BufEnter * ++once let s:triggered = v:true
|
||||||
|
\| call assert_equal('autocmd', win_gettype())
|
||||||
|
augroup END
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
" bufload opening the autocommand window shouldn't give E36.
|
||||||
|
call bufload('unload me')
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
call assert_equal(winlayout(), layout)
|
||||||
|
call assert_equal(winrestcmd(), restcmd)
|
||||||
|
|
||||||
|
unlet! s:triggered
|
||||||
|
au! AucmdWinForceRoom
|
||||||
|
augroup! AucmdWinForceRoom
|
||||||
|
%bw!
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_win_gotoid_splitmove_textlock_cmdwin()
|
||||||
|
call setline(1, 'foo')
|
||||||
|
new
|
||||||
|
let curwin = win_getid()
|
||||||
|
call setline(1, 'bar')
|
||||||
|
|
||||||
|
set debug+=throw indentexpr=win_gotoid(win_getid(winnr('#')))
|
||||||
|
call assert_fails('normal! ==', 'E565:')
|
||||||
|
call assert_equal(curwin, win_getid())
|
||||||
|
" No error if attempting to switch to curwin; nothing happens.
|
||||||
|
set indentexpr=assert_equal(1,win_gotoid(win_getid()))
|
||||||
|
normal! ==
|
||||||
|
call assert_equal(curwin, win_getid())
|
||||||
|
|
||||||
|
set indentexpr=win_splitmove(winnr('#'),winnr())
|
||||||
|
call assert_fails('normal! ==', 'E565:')
|
||||||
|
call assert_equal(curwin, win_getid())
|
||||||
|
|
||||||
|
%bw!
|
||||||
|
set debug-=throw indentexpr&
|
||||||
|
|
||||||
|
call feedkeys('q:'
|
||||||
|
\ .. ":call assert_fails('call win_splitmove(winnr(''#''), winnr())', 'E11:')\<CR>"
|
||||||
|
\ .. ":call assert_equal('command', win_gettype())\<CR>"
|
||||||
|
\ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
|
||||||
|
|
||||||
|
call feedkeys('q:'
|
||||||
|
\ .. ":call assert_fails('call win_gotoid(win_getid(winnr(''#'')))', 'E11:')\<CR>"
|
||||||
|
"\ No error if attempting to switch to curwin; nothing happens.
|
||||||
|
\ .. ":call assert_equal(1, win_gotoid(win_getid()))\<CR>"
|
||||||
|
\ .. ":call assert_equal('command', win_gettype())\<CR>"
|
||||||
|
\ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Reference in New Issue
Block a user