mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07:00
feat(api): make nvim_open_win support non-floating windows (#25550)
Adds support to `nvim_open_win` and `nvim_win_set_config` for creating and manipulating split (non-floating) windows.
This commit is contained in:
parent
8fa67fdae5
commit
6bba4beced
@ -3093,18 +3093,28 @@ nvim_win_text_height({window}, {*opts}) *nvim_win_text_height()*
|
||||
Win_Config Functions *api-win_config*
|
||||
|
||||
nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
|
||||
Open a new window.
|
||||
Opens a new split window, or a floating window if `relative` is specified,
|
||||
or an external window (managed by the UI) if `external` is specified.
|
||||
|
||||
Currently this is used to open floating and external windows. Floats are
|
||||
windows that are drawn above the split layout, at some anchor position in
|
||||
some other window. Floats can be drawn internally or by external GUI with
|
||||
the |ui-multigrid| extension. External windows are only supported with
|
||||
multigrid GUIs, and are displayed as separate top-level windows.
|
||||
Floats are windows that are drawn above the split layout, at some anchor
|
||||
position in some other window. Floats can be drawn internally or by
|
||||
external GUI with the |ui-multigrid| extension. External windows are only
|
||||
supported with multigrid GUIs, and are displayed as separate top-level
|
||||
windows.
|
||||
|
||||
For a general overview of floats, see |api-floatwin|.
|
||||
|
||||
Exactly one of `external` and `relative` must be specified. The `width`
|
||||
and `height` of the new window must be specified.
|
||||
The `width` and `height` of the new window must be specified when opening
|
||||
a floating window, but are optional for normal windows.
|
||||
|
||||
If `relative` and `external` are omitted, a normal "split" window is
|
||||
created. The `win` property determines which window will be split. If no
|
||||
`win` is provided or `win == 0`, a window will be created adjacent to the
|
||||
current window. If -1 is provided, a top-level split will be created.
|
||||
`vertical` and `split` are only valid for normal windows, and are used to
|
||||
control split direction. For `vertical`, the exact direction is determined
|
||||
by |'splitright'| and |'splitbelow'|. Split windows cannot have
|
||||
`bufpos`/`row`/`col`/`border`/`title`/`footer` properties.
|
||||
|
||||
With relative=editor (row=0,col=0) refers to the top-left corner of the
|
||||
screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
|
||||
@ -3127,6 +3137,13 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
|
||||
{relative='win', width=12, height=3, bufpos={100,10}})
|
||||
<
|
||||
|
||||
Example (Lua): vertical split left of the current window >lua
|
||||
vim.api.nvim_open_win(0, false, {
|
||||
split = 'left',
|
||||
win = 0
|
||||
})
|
||||
<
|
||||
|
||||
Attributes: ~
|
||||
not allowed when |textlock| is active
|
||||
|
||||
@ -3142,7 +3159,8 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
|
||||
• "cursor" Cursor position in current window.
|
||||
• "mouse" Mouse position
|
||||
|
||||
• win: |window-ID| for relative="win".
|
||||
• win: |window-ID| window to split, or relative window when
|
||||
creating a float (relative="win").
|
||||
• anchor: Decides which corner of the float to place at
|
||||
(row,col):
|
||||
• "NW" northwest (default)
|
||||
@ -3239,6 +3257,8 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()*
|
||||
• fixed: If true when anchor is NW or SW, the float window
|
||||
would be kept fixed even if the window would be truncated.
|
||||
• hide: If true the floating window will be hidden.
|
||||
• vertical: Split vertically |:vertical|.
|
||||
• split: Split direction: "left", "right", "above", "below".
|
||||
|
||||
Return: ~
|
||||
Window handle, or 0 on error
|
||||
|
@ -406,6 +406,9 @@ The following changes to existing APIs or features add new behavior.
|
||||
• |:checkhealth| buffer can now be opened in a split window using modifiers like
|
||||
|:vertical|, |:horizontal| and |:botright|.
|
||||
|
||||
• |nvim_open_win()| and |nvim_win_set_config()| now support opening normal (split)
|
||||
windows, and moving floating windows into split windows.
|
||||
|
||||
==============================================================================
|
||||
REMOVED FEATURES *news-removed*
|
||||
|
||||
|
39
runtime/lua/vim/_meta/api.lua
generated
39
runtime/lua/vim/_meta/api.lua
generated
@ -1483,15 +1483,24 @@ function vim.api.nvim_notify(msg, log_level, opts) end
|
||||
--- @return integer
|
||||
function vim.api.nvim_open_term(buffer, opts) end
|
||||
|
||||
--- Open a new window.
|
||||
--- Currently this is used to open floating and external windows. Floats are
|
||||
--- windows that are drawn above the split layout, at some anchor position in
|
||||
--- some other window. Floats can be drawn internally or by external GUI with
|
||||
--- the `ui-multigrid` extension. External windows are only supported with
|
||||
--- multigrid GUIs, and are displayed as separate top-level windows.
|
||||
--- Opens a new split window, or a floating window if `relative` is specified,
|
||||
--- or an external window (managed by the UI) if `external` is specified.
|
||||
--- Floats are windows that are drawn above the split layout, at some anchor
|
||||
--- position in some other window. Floats can be drawn internally or by
|
||||
--- external GUI with the `ui-multigrid` extension. External windows are only
|
||||
--- supported with multigrid GUIs, and are displayed as separate top-level
|
||||
--- windows.
|
||||
--- For a general overview of floats, see `api-floatwin`.
|
||||
--- Exactly one of `external` and `relative` must be specified. The `width`
|
||||
--- and `height` of the new window must be specified.
|
||||
--- The `width` and `height` of the new window must be specified when opening
|
||||
--- a floating window, but are optional for normal windows.
|
||||
--- If `relative` and `external` are omitted, a normal "split" window is
|
||||
--- created. The `win` property determines which window will be split. If no
|
||||
--- `win` is provided or `win == 0`, a window will be created adjacent to the
|
||||
--- current window. If -1 is provided, a top-level split will be created.
|
||||
--- `vertical` and `split` are only valid for normal windows, and are used to
|
||||
--- control split direction. For `vertical`, the exact direction is determined
|
||||
--- by `'splitright'` and `'splitbelow'`. Split windows cannot have
|
||||
--- `bufpos`/`row`/`col`/`border`/`title`/`footer` properties.
|
||||
--- With relative=editor (row=0,col=0) refers to the top-left corner of the
|
||||
--- screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
|
||||
--- corner. Fractional values are allowed, but the builtin implementation
|
||||
@ -1515,6 +1524,15 @@ function vim.api.nvim_open_term(buffer, opts) end
|
||||
--- {relative='win', width=12, height=3, bufpos={100,10}})
|
||||
--- ```
|
||||
---
|
||||
--- Example (Lua): vertical split left of the current window
|
||||
---
|
||||
--- ```lua
|
||||
--- vim.api.nvim_open_win(0, false, {
|
||||
--- split = 'left',
|
||||
--- win = 0
|
||||
--- })
|
||||
--- ```
|
||||
---
|
||||
--- @param buffer integer Buffer to display, or 0 for current buffer
|
||||
--- @param enter boolean Enter the window (make it the current window)
|
||||
--- @param config vim.api.keyset.float_config Map defining the window configuration. Keys:
|
||||
@ -1526,7 +1544,8 @@ function vim.api.nvim_open_term(buffer, opts) end
|
||||
--- • "cursor" Cursor position in current window.
|
||||
--- • "mouse" Mouse position
|
||||
---
|
||||
--- • win: `window-ID` for relative="win".
|
||||
--- • win: `window-ID` window to split, or relative window when
|
||||
--- creating a float (relative="win").
|
||||
--- • anchor: Decides which corner of the float to place at
|
||||
--- (row,col):
|
||||
--- • "NW" northwest (default)
|
||||
@ -1623,6 +1642,8 @@ function vim.api.nvim_open_term(buffer, opts) end
|
||||
--- • fixed: If true when anchor is NW or SW, the float window
|
||||
--- would be kept fixed even if the window would be truncated.
|
||||
--- • hide: If true the floating window will be hidden.
|
||||
--- • vertical: Split vertically `:vertical`.
|
||||
--- • split: Split direction: "left", "right", "above", "below".
|
||||
--- @return integer
|
||||
function vim.api.nvim_open_win(buffer, enter, config) end
|
||||
|
||||
|
2
runtime/lua/vim/_meta/api_keysets.lua
generated
2
runtime/lua/vim/_meta/api_keysets.lua
generated
@ -118,10 +118,12 @@ error('Cannot require a meta file')
|
||||
--- @field height? integer
|
||||
--- @field anchor? string
|
||||
--- @field relative? string
|
||||
--- @field split? string
|
||||
--- @field win? integer
|
||||
--- @field bufpos? any[]
|
||||
--- @field external? boolean
|
||||
--- @field focusable? boolean
|
||||
--- @field vertical? boolean
|
||||
--- @field zindex? integer
|
||||
--- @field border? any
|
||||
--- @field title? any
|
||||
|
@ -115,10 +115,12 @@ typedef struct {
|
||||
Integer height;
|
||||
String anchor;
|
||||
String relative;
|
||||
String split;
|
||||
Window win;
|
||||
Array bufpos;
|
||||
Boolean external;
|
||||
Boolean focusable;
|
||||
Boolean vertical;
|
||||
Integer zindex;
|
||||
Object border;
|
||||
Object title;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/dispatch.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/tabpage.h"
|
||||
#include "nvim/api/win_config.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/autocmd.h"
|
||||
@ -15,6 +16,8 @@
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/decoration_defs.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/eval/window.h"
|
||||
#include "nvim/extmark_defs.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/grid_defs.h"
|
||||
#include "nvim/highlight_group.h"
|
||||
@ -22,12 +25,15 @@
|
||||
#include "nvim/mbyte.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/option_vars.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/ui_defs.h"
|
||||
#include "nvim/vim_defs.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/winfloat.h"
|
||||
|
||||
@ -35,9 +41,9 @@
|
||||
# include "api/win_config.c.generated.h"
|
||||
#endif
|
||||
|
||||
/// Open a new window.
|
||||
/// Opens a new split window, or a floating window if `relative` is specified,
|
||||
/// or an external window (managed by the UI) if `external` is specified.
|
||||
///
|
||||
/// Currently this is used to open floating and external windows.
|
||||
/// Floats are windows that are drawn above the split layout, at some anchor
|
||||
/// position in some other window. Floats can be drawn internally or by external
|
||||
/// GUI with the |ui-multigrid| extension. External windows are only supported
|
||||
@ -45,8 +51,17 @@
|
||||
///
|
||||
/// For a general overview of floats, see |api-floatwin|.
|
||||
///
|
||||
/// Exactly one of `external` and `relative` must be specified. The `width` and
|
||||
/// `height` of the new window must be specified.
|
||||
/// The `width` and `height` of the new window must be specified when opening
|
||||
/// a floating window, but are optional for normal windows.
|
||||
///
|
||||
/// If `relative` and `external` are omitted, a normal "split" window is created.
|
||||
/// The `win` property determines which window will be split. If no `win` is
|
||||
/// provided or `win == 0`, a window will be created adjacent to the current window.
|
||||
/// If -1 is provided, a top-level split will be created. `vertical` and `split` are
|
||||
/// only valid for normal windows, and are used to control split direction. For `vertical`,
|
||||
/// the exact direction is determined by |'splitright'| and |'splitbelow'|.
|
||||
/// Split windows cannot have `bufpos`/`row`/`col`/`border`/`title`/`footer`
|
||||
/// properties.
|
||||
///
|
||||
/// With relative=editor (row=0,col=0) refers to the top-left corner of the
|
||||
/// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
|
||||
@ -73,6 +88,15 @@
|
||||
/// {relative='win', width=12, height=3, bufpos={100,10}})
|
||||
/// ```
|
||||
///
|
||||
/// Example (Lua): vertical split left of the current window
|
||||
///
|
||||
/// ```lua
|
||||
/// vim.api.nvim_open_win(0, false, {
|
||||
/// split = 'left',
|
||||
/// win = 0
|
||||
/// })
|
||||
/// ```
|
||||
///
|
||||
/// @param buffer Buffer to display, or 0 for current buffer
|
||||
/// @param enter Enter the window (make it the current window)
|
||||
/// @param config Map defining the window configuration. Keys:
|
||||
@ -82,7 +106,8 @@
|
||||
/// - "win" Window given by the `win` field, or current window.
|
||||
/// - "cursor" Cursor position in current window.
|
||||
/// - "mouse" Mouse position
|
||||
/// - win: |window-ID| for relative="win".
|
||||
/// - win: |window-ID| window to split, or relative window when creating a
|
||||
/// float (relative="win").
|
||||
/// - anchor: Decides which corner of the float to place at (row,col):
|
||||
/// - "NW" northwest (default)
|
||||
/// - "NE" northeast
|
||||
@ -169,13 +194,14 @@
|
||||
/// - fixed: If true when anchor is NW or SW, the float window
|
||||
/// would be kept fixed even if the window would be truncated.
|
||||
/// - hide: If true the floating window will be hidden.
|
||||
/// - vertical: Split vertically |:vertical|.
|
||||
/// - split: Split direction: "left", "right", "above", "below".
|
||||
///
|
||||
/// @param[out] err Error details, if any
|
||||
///
|
||||
/// @return Window handle, or 0 on error
|
||||
Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err)
|
||||
FUNC_API_SINCE(6)
|
||||
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
|
||||
FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
if (!buf) {
|
||||
@ -190,22 +216,67 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
|
||||
if (!parse_float_config(config, &fconfig, false, true, err)) {
|
||||
return 0;
|
||||
}
|
||||
win_T *wp = win_new_float(NULL, false, fconfig, err);
|
||||
if (!wp) {
|
||||
|
||||
bool is_split = HAS_KEY(config, float_config, split) || HAS_KEY(config, float_config, vertical);
|
||||
|
||||
win_T *wp = NULL;
|
||||
tabpage_T *tp = curtab;
|
||||
if (is_split) {
|
||||
win_T *parent = NULL;
|
||||
if (!HAS_KEY(config, float_config, win) || config->win != -1) {
|
||||
parent = find_window_by_handle(fconfig.window, err);
|
||||
if (!parent) {
|
||||
// find_window_by_handle has already set the error
|
||||
return 0;
|
||||
} else if (parent->w_floating) {
|
||||
api_set_error(err, kErrorTypeException, "Cannot split a floating window");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY(config, float_config, vertical) && !HAS_KEY(config, float_config, split)) {
|
||||
if (config->vertical) {
|
||||
fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft;
|
||||
} else {
|
||||
fconfig.split = p_sb ? kWinSplitBelow : kWinSplitAbove;
|
||||
}
|
||||
}
|
||||
int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER;
|
||||
|
||||
if (parent == NULL) {
|
||||
wp = win_split_ins(0, flags, NULL, 0);
|
||||
} else {
|
||||
tp = win_find_tabpage(parent);
|
||||
switchwin_T switchwin;
|
||||
// `parent` is valid in `tp`, so switch_win should not fail.
|
||||
const int result = switch_win(&switchwin, parent, tp, true);
|
||||
(void)result;
|
||||
assert(result == OK);
|
||||
wp = win_split_ins(0, flags, NULL, 0);
|
||||
restore_win(&switchwin, true);
|
||||
}
|
||||
if (wp) {
|
||||
wp->w_float_config = fconfig;
|
||||
}
|
||||
} else {
|
||||
wp = win_new_float(NULL, false, fconfig, err);
|
||||
}
|
||||
if (!wp) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to create window");
|
||||
return 0;
|
||||
}
|
||||
switchwin_T switchwin;
|
||||
if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
|
||||
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
|
||||
}
|
||||
restore_win_noblock(&switchwin, true);
|
||||
if (enter) {
|
||||
win_enter(wp, false);
|
||||
goto_tabpage_win(tp, wp);
|
||||
}
|
||||
// autocmds in win_enter or win_set_buf below may close the window
|
||||
if (win_valid(wp) && buffer > 0) {
|
||||
Boolean noautocmd = !enter || fconfig.noautocmd;
|
||||
win_set_buf(wp, buf, noautocmd, err);
|
||||
if (!fconfig.noautocmd) {
|
||||
apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf);
|
||||
if (win_valid_any_tab(wp) && buf != wp->w_buffer) {
|
||||
win_set_buf(wp, buf, !enter || fconfig.noautocmd, err);
|
||||
}
|
||||
}
|
||||
if (!win_valid(wp)) {
|
||||
if (!win_valid_any_tab(wp)) {
|
||||
api_set_error(err, kErrorTypeException, "Window was closed immediately");
|
||||
return 0;
|
||||
}
|
||||
@ -217,6 +288,36 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
|
||||
return wp->handle;
|
||||
}
|
||||
|
||||
static WinSplit win_split_dir(win_T *win)
|
||||
{
|
||||
if (win->w_frame == NULL || win->w_frame->fr_parent == NULL) {
|
||||
return kWinSplitLeft;
|
||||
}
|
||||
|
||||
char layout = win->w_frame->fr_parent->fr_layout;
|
||||
if (layout == FR_COL) {
|
||||
return win->w_frame->fr_next ? kWinSplitAbove : kWinSplitBelow;
|
||||
} else {
|
||||
return win->w_frame->fr_next ? kWinSplitLeft : kWinSplitRight;
|
||||
}
|
||||
}
|
||||
|
||||
static int win_split_flags(WinSplit split, bool toplevel)
|
||||
{
|
||||
int flags = 0;
|
||||
if (split == kWinSplitAbove || split == kWinSplitBelow) {
|
||||
flags |= WSP_HOR;
|
||||
} else {
|
||||
flags |= WSP_VERT;
|
||||
}
|
||||
if (split == kWinSplitAbove || split == kWinSplitLeft) {
|
||||
flags |= toplevel ? WSP_TOP : WSP_ABOVE;
|
||||
} else {
|
||||
flags |= toplevel ? WSP_BOT : WSP_BELOW;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
/// Configures window layout. Currently only for floating and external windows
|
||||
/// (including changing a split window to those layouts).
|
||||
///
|
||||
@ -236,18 +337,195 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
bool new_float = !win->w_floating;
|
||||
tabpage_T *win_tp = win_find_tabpage(win);
|
||||
bool was_split = !win->w_floating;
|
||||
bool has_split = HAS_KEY(config, float_config, split);
|
||||
bool has_vertical = HAS_KEY(config, float_config, vertical);
|
||||
// reuse old values, if not overridden
|
||||
FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
|
||||
FloatConfig fconfig = win->w_float_config;
|
||||
|
||||
if (!parse_float_config(config, &fconfig, !new_float, false, err)) {
|
||||
bool to_split = (!HAS_KEY(config, float_config, relative) || striequal(config->relative.data, ""))
|
||||
&& ((!HAS_KEY(config, float_config, external) && !fconfig.external)
|
||||
|| !config->external)
|
||||
&& (has_split || has_vertical || was_split);
|
||||
|
||||
if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) {
|
||||
return;
|
||||
}
|
||||
if (new_float) {
|
||||
if (was_split && !to_split) {
|
||||
if (!win_new_float(win, false, fconfig, err)) {
|
||||
return;
|
||||
}
|
||||
redraw_later(win, UPD_NOT_VALID);
|
||||
} else if (to_split) {
|
||||
win_T *parent = NULL;
|
||||
if (!HAS_KEY(config, float_config, win) || config->win != -1) {
|
||||
parent = find_window_by_handle(fconfig.window, err);
|
||||
if (!parent) {
|
||||
return;
|
||||
} else if (parent->w_floating) {
|
||||
api_set_error(err, kErrorTypeException, "Cannot split a floating window");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WinSplit old_split = win_split_dir(win);
|
||||
if (has_vertical && !has_split) {
|
||||
if (config->vertical) {
|
||||
if (old_split == kWinSplitRight || p_spr) {
|
||||
fconfig.split = kWinSplitRight;
|
||||
} else {
|
||||
fconfig.split = kWinSplitLeft;
|
||||
}
|
||||
} else {
|
||||
if (old_split == kWinSplitBelow || p_sb) {
|
||||
fconfig.split = kWinSplitBelow;
|
||||
} else {
|
||||
fconfig.split = kWinSplitAbove;
|
||||
}
|
||||
}
|
||||
}
|
||||
win->w_float_config = fconfig;
|
||||
|
||||
// If there's no vertical or split set, or if the split is the same as the old split,
|
||||
// then we can just change the size of the window.
|
||||
if ((!has_vertical && !has_split)
|
||||
|| (was_split
|
||||
&& !HAS_KEY(config, float_config,
|
||||
win) && ((!has_split && !has_vertical) || old_split == fconfig.split))) {
|
||||
if (HAS_KEY(config, float_config, width)) {
|
||||
win_setwidth_win(fconfig.width, win);
|
||||
}
|
||||
if (HAS_KEY(config, float_config, height)) {
|
||||
win_setheight_win(fconfig.height, win);
|
||||
}
|
||||
redraw_later(win, UPD_NOT_VALID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (was_split) {
|
||||
win_T *new_curwin = NULL;
|
||||
|
||||
// If the window is the last in the tabpage or `fconfig.win` is
|
||||
// a handle to itself, we can't split it.
|
||||
if (win->w_frame->fr_parent == NULL) {
|
||||
// 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
|
||||
// tabpage and close the previous one, or just error?
|
||||
api_set_error(err, kErrorTypeValidation, "Cannot move last window");
|
||||
return;
|
||||
} else if (parent != NULL && parent->handle == win->handle) {
|
||||
int n_frames = 0;
|
||||
for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) {
|
||||
n_frames++;
|
||||
}
|
||||
|
||||
win_T *neighbor = NULL;
|
||||
|
||||
if (n_frames > 2) {
|
||||
// There are three or more windows in the frame, we need to split a neighboring window.
|
||||
frame_T *frame = win->w_frame->fr_parent;
|
||||
|
||||
if (frame->fr_parent) {
|
||||
// ┌──────────────┐
|
||||
// │ A │
|
||||
// ├────┬────┬────┤
|
||||
// │ B │ C │ D │
|
||||
// └────┴────┴────┘
|
||||
// ||
|
||||
// \/
|
||||
// ┌───────────────────┐
|
||||
// │ A │
|
||||
// ├─────────┬─────────┤
|
||||
// │ │ C │
|
||||
// │ B ├─────────┤
|
||||
// │ │ D │
|
||||
// └─────────┴─────────┘
|
||||
if (fconfig.split == kWinSplitAbove || fconfig.split == kWinSplitLeft) {
|
||||
neighbor = win->w_next;
|
||||
} else {
|
||||
neighbor = win->w_prev;
|
||||
}
|
||||
}
|
||||
// If the frame doesn't have a parent, the old frame
|
||||
// was the root frame and we need to create a top-level split.
|
||||
int dir;
|
||||
new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
|
||||
} else if (n_frames == 2) {
|
||||
// There are two windows in the frame, we can just rotate it.
|
||||
int dir;
|
||||
neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
|
||||
new_curwin = neighbor;
|
||||
} else {
|
||||
// There is only one window in the frame, we can't split it.
|
||||
api_set_error(err, kErrorTypeValidation, "Cannot split window into itself");
|
||||
return;
|
||||
}
|
||||
// Set the parent to whatever the correct
|
||||
// neighbor window was determined to be.
|
||||
parent = neighbor;
|
||||
} else {
|
||||
int dir;
|
||||
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_float_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 {
|
||||
win_execute_T args;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (HAS_KEY(config, float_config, width)) {
|
||||
win_setwidth_win(fconfig.width, win);
|
||||
}
|
||||
if (HAS_KEY(config, float_config, height)) {
|
||||
win_setheight_win(fconfig.height, win);
|
||||
}
|
||||
redraw_later(win, UPD_NOT_VALID);
|
||||
return;
|
||||
} else {
|
||||
win_config_float(win, fconfig);
|
||||
win->w_pos_changed = true;
|
||||
@ -317,6 +595,9 @@ Dictionary nvim_win_get_config(Window window, Error *err)
|
||||
/// Keep in sync with FloatRelative in buffer_defs.h
|
||||
static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
|
||||
|
||||
/// Keep in sync with WinSplit in buffer_defs.h
|
||||
static const char *const win_split_str[] = { "left", "right", "above", "below" };
|
||||
|
||||
Dictionary rv = ARRAY_DICT_INIT;
|
||||
|
||||
win_T *wp = find_window_by_handle(window, err);
|
||||
@ -373,11 +654,18 @@ Dictionary nvim_win_get_config(Window window, Error *err)
|
||||
rv = config_put_bordertext(rv, config, kBorderTextFooter);
|
||||
}
|
||||
}
|
||||
} else if (!config->external) {
|
||||
PUT(rv, "width", INTEGER_OBJ(wp->w_width));
|
||||
PUT(rv, "height", INTEGER_OBJ(wp->w_height));
|
||||
WinSplit split = win_split_dir(wp);
|
||||
PUT(rv, "split", CSTR_TO_OBJ(win_split_str[split]));
|
||||
}
|
||||
|
||||
const char *rel = (wp->w_floating && !config->external
|
||||
? float_relative_str[config->relative] : "");
|
||||
PUT(rv, "relative", CSTR_TO_OBJ(rel));
|
||||
if (wp->w_floating && !config->external) {
|
||||
PUT(rv, "relative", CSTR_TO_OBJ(float_relative_str[config->relative]));
|
||||
} else {
|
||||
PUT(rv, "relative", CSTR_TO_OBJ(""));
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -419,10 +707,26 @@ static bool parse_float_relative(String relative, FloatRelative *out)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_config_split(String split, WinSplit *out)
|
||||
{
|
||||
char *str = split.data;
|
||||
if (striequal(str, "left")) {
|
||||
*out = kWinSplitLeft;
|
||||
} else if (striequal(str, "right")) {
|
||||
*out = kWinSplitRight;
|
||||
} else if (striequal(str, "above")) {
|
||||
*out = kWinSplitAbove;
|
||||
} else if (striequal(str, "below")) {
|
||||
*out = kWinSplitBelow;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_float_bufpos(Array bufpos, lpos_T *out)
|
||||
{
|
||||
if (bufpos.size != 2
|
||||
|| bufpos.items[0].type != kObjectTypeInteger
|
||||
if (bufpos.size != 2 || bufpos.items[0].type != kObjectTypeInteger
|
||||
|| bufpos.items[1].type != kObjectTypeInteger) {
|
||||
return false;
|
||||
}
|
||||
@ -544,7 +848,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|
||||
{ NULL, { { NUL } }, false },
|
||||
};
|
||||
|
||||
char (*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars;
|
||||
char(*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars;
|
||||
int *hl_ids = fconfig->border_hl_ids;
|
||||
|
||||
fconfig->border = true;
|
||||
@ -553,8 +857,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|
||||
Array arr = style.data.array;
|
||||
size_t size = arr.size;
|
||||
if (!size || size > 8 || (size & (size - 1))) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"invalid number of border chars");
|
||||
api_set_error(err, kErrorTypeValidation, "invalid number of border chars");
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
@ -584,10 +887,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|
||||
api_set_error(err, kErrorTypeValidation, "invalid border char");
|
||||
return;
|
||||
}
|
||||
if (string.size
|
||||
&& mb_string2cells_len(string.data, string.size) > 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"border chars must be one cell");
|
||||
if (string.size && mb_string2cells_len(string.data, string.size) > 1) {
|
||||
api_set_error(err, kErrorTypeValidation, "border chars must be one cell");
|
||||
return;
|
||||
}
|
||||
size_t len = MIN(string.size, sizeof(*chars) - 1);
|
||||
@ -606,8 +907,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|
||||
|| (chars[1][0] && chars[3][0] && !chars[2][0])
|
||||
|| (chars[3][0] && chars[5][0] && !chars[4][0])
|
||||
|| (chars[5][0] && chars[7][0] && !chars[6][0])) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"corner between used edges must be specified");
|
||||
api_set_error(err, kErrorTypeValidation, "corner between used edges must be specified");
|
||||
}
|
||||
} else if (style.type == kObjectTypeString) {
|
||||
String str = style.data.string;
|
||||
@ -634,8 +934,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|
||||
return;
|
||||
}
|
||||
}
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"invalid border style \"%s\"", str.data);
|
||||
api_set_error(err, kErrorTypeValidation, "invalid border style \"%s\"", str.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -643,17 +942,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
bool new_win, Error *err)
|
||||
{
|
||||
#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key)
|
||||
bool has_relative = false, relative_is_win = false;
|
||||
// ignore empty string, to match nvim_win_get_config
|
||||
if (HAS_KEY_X(config, relative) && config->relative.size > 0) {
|
||||
bool has_relative = false, relative_is_win = false, is_split = false;
|
||||
if (HAS_KEY_X(config, relative) && !striequal(config->relative.data, "")) {
|
||||
if (!parse_float_relative(config->relative, &fconfig->relative)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"'relative' requires 'row'/'col' or 'bufpos'");
|
||||
if (config->relative.size > 0 && !(HAS_KEY_X(config, row) && HAS_KEY_X(config, col))
|
||||
&& !HAS_KEY_X(config, bufpos)) {
|
||||
api_set_error(err, kErrorTypeValidation, "'relative' requires 'row'/'col' or 'bufpos'");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -663,6 +961,32 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
relative_is_win = true;
|
||||
fconfig->bufpos.lnum = -1;
|
||||
}
|
||||
} else if (!HAS_KEY_X(config, external) || !config->external) {
|
||||
if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) {
|
||||
is_split = true;
|
||||
} else if (new_win) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Must specify 'relative' or 'external' when creating a float");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, vertical)) {
|
||||
if (!is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'vertical'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, split)) {
|
||||
if (!is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'split'");
|
||||
return false;
|
||||
}
|
||||
if (!parse_config_split(config->split, &fconfig->split)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value of 'split' key");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, anchor)) {
|
||||
@ -673,7 +997,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, row)) {
|
||||
if (!has_relative) {
|
||||
if (!has_relative || is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'");
|
||||
return false;
|
||||
}
|
||||
@ -681,7 +1005,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, col)) {
|
||||
if (!has_relative) {
|
||||
if (!has_relative || is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'");
|
||||
return false;
|
||||
}
|
||||
@ -689,7 +1013,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, bufpos)) {
|
||||
if (!has_relative) {
|
||||
if (!has_relative || is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'");
|
||||
return false;
|
||||
} else {
|
||||
@ -714,7 +1038,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer");
|
||||
return false;
|
||||
}
|
||||
} else if (!reconf) {
|
||||
} else if (!reconf && !is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "Must specify 'width'");
|
||||
return false;
|
||||
}
|
||||
@ -726,21 +1050,22 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer");
|
||||
return false;
|
||||
}
|
||||
} else if (!reconf) {
|
||||
} else if (!reconf && !is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "Must specify 'height'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (relative_is_win) {
|
||||
if (relative_is_win || is_split) {
|
||||
fconfig->window = curwin->handle;
|
||||
if (HAS_KEY_X(config, win)) {
|
||||
if (config->win > 0) {
|
||||
fconfig->window = config->win;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (has_relative) {
|
||||
if (HAS_KEY_X(config, win)) {
|
||||
api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'");
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"'win' key is only valid with relative='win' and relative=''");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -753,23 +1078,20 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
return false;
|
||||
}
|
||||
if (fconfig->external && !ui_has(kUIMultigrid)) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"UI doesn't support external windows");
|
||||
api_set_error(err, kErrorTypeValidation, "UI doesn't support external windows");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reconf && (!has_relative && !fconfig->external)) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"One of 'relative' and 'external' must be used");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, focusable)) {
|
||||
fconfig->focusable = config->focusable;
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, zindex)) {
|
||||
if (is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'zindex'");
|
||||
return false;
|
||||
}
|
||||
if (config->zindex > 0) {
|
||||
fconfig->zindex = (int)config->zindex;
|
||||
} else {
|
||||
@ -779,6 +1101,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, title)) {
|
||||
if (is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'title'");
|
||||
return false;
|
||||
}
|
||||
// title only work with border
|
||||
if (!HAS_KEY_X(config, border) && !fconfig->border) {
|
||||
api_set_error(err, kErrorTypeException, "title requires border to be set");
|
||||
@ -802,6 +1128,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, footer)) {
|
||||
if (is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'footer'");
|
||||
return false;
|
||||
}
|
||||
// footer only work with border
|
||||
if (!HAS_KEY_X(config, border) && !fconfig->border) {
|
||||
api_set_error(err, kErrorTypeException, "footer requires border to be set");
|
||||
@ -825,6 +1155,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
|
||||
}
|
||||
|
||||
if (HAS_KEY_X(config, border)) {
|
||||
if (is_split) {
|
||||
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'");
|
||||
return false;
|
||||
}
|
||||
parse_border_style(config->border, fconfig, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return false;
|
||||
|
@ -889,6 +889,14 @@ typedef enum {
|
||||
kFloatRelativeMouse = 3,
|
||||
} FloatRelative;
|
||||
|
||||
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
|
||||
typedef enum {
|
||||
kWinSplitLeft = 0,
|
||||
kWinSplitRight = 1,
|
||||
kWinSplitAbove = 2,
|
||||
kWinSplitBelow = 3,
|
||||
} WinSplit;
|
||||
|
||||
typedef enum {
|
||||
kWinStyleUnused = 0,
|
||||
kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc
|
||||
@ -914,6 +922,7 @@ typedef struct {
|
||||
FloatRelative relative;
|
||||
bool external;
|
||||
bool focusable;
|
||||
WinSplit split;
|
||||
int zindex;
|
||||
WinStyle style;
|
||||
bool border;
|
||||
@ -939,6 +948,7 @@ typedef struct {
|
||||
.row = 0, .col = 0, .anchor = 0, \
|
||||
.relative = 0, .external = false, \
|
||||
.focusable = true, \
|
||||
.split = 0, \
|
||||
.zindex = kZIndexFloatDefault, \
|
||||
.style = kWinStyleUnused, \
|
||||
.noautocmd = false, \
|
||||
|
@ -713,7 +713,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
|
||||
}
|
||||
|
||||
switchwin_T switchwin;
|
||||
if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) {
|
||||
if (switch_win_noblock(&switchwin, win, tab, true) == FAIL) {
|
||||
api_set_error(err,
|
||||
kErrorTypeException,
|
||||
"Failed to switch to window %d",
|
||||
@ -733,7 +733,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
|
||||
// So do it now.
|
||||
validate_cursor();
|
||||
|
||||
restore_win_noblock(&switchwin, false);
|
||||
restore_win_noblock(&switchwin, true);
|
||||
if (noautocmd) {
|
||||
unblock_autocmds();
|
||||
}
|
||||
@ -928,6 +928,7 @@ static int check_split_disallowed(void)
|
||||
// WSP_TOP: open window at the top-left of the screen (help window).
|
||||
// WSP_BOT: open window at the bottom-right of the screen (quickfix window).
|
||||
// WSP_HELP: creating the help window, keep layout snapshot
|
||||
// WSP_NOENTER: do not enter the new window or trigger WinNew autocommands
|
||||
//
|
||||
// return FAIL for failure, OK otherwise
|
||||
int win_split(int size, int flags)
|
||||
@ -956,20 +957,20 @@ int win_split(int size, int flags)
|
||||
clear_snapshot(curtab, SNAP_HELP_IDX);
|
||||
}
|
||||
|
||||
return win_split_ins(size, flags, NULL, 0);
|
||||
return win_split_ins(size, flags, NULL, 0) == NULL ? FAIL : OK;
|
||||
}
|
||||
|
||||
/// When "new_wp" is NULL: split the current window in two.
|
||||
/// When "new_wp" is not NULL: insert this window at the far
|
||||
/// top/left/right/bottom.
|
||||
/// @return FAIL for failure, OK otherwise
|
||||
int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
/// @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 *wp = new_wp;
|
||||
|
||||
// aucmd_win[] should always remain floating
|
||||
if (new_wp != NULL && is_aucmd_win(new_wp)) {
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win_T *oldwin;
|
||||
@ -985,22 +986,24 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
int need_status = 0;
|
||||
int new_size = size;
|
||||
bool new_in_layout = (new_wp == NULL || new_wp->w_floating);
|
||||
bool vertical = flags & WSP_VERT;
|
||||
bool toplevel = flags & (WSP_TOP | WSP_BOT);
|
||||
|
||||
// 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 (oldwin->w_height <= p_wmh && new_in_layout) {
|
||||
emsg(_(e_noroom));
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
need_status = STATUS_HEIGHT;
|
||||
}
|
||||
|
||||
bool do_equal = false;
|
||||
int oldwin_height = 0;
|
||||
const int layout = flags & WSP_VERT ? FR_ROW : FR_COL;
|
||||
const int layout = vertical ? FR_ROW : FR_COL;
|
||||
bool did_set_fraction = false;
|
||||
|
||||
if (flags & WSP_VERT) {
|
||||
if (vertical) {
|
||||
// Check if we are able to split the current window and compute its
|
||||
// width.
|
||||
// Current window requires at least 1 space.
|
||||
@ -1011,7 +1014,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
int minwidth;
|
||||
int available;
|
||||
if (flags & (WSP_BOT | WSP_TOP)) {
|
||||
if (toplevel) {
|
||||
minwidth = frame_minwidth(topframe, NOWIN);
|
||||
available = topframe->fr_width;
|
||||
needed += minwidth;
|
||||
@ -1039,7 +1042,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
if (available < needed && new_in_layout) {
|
||||
emsg(_(e_noroom));
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
if (new_size == 0) {
|
||||
new_size = oldwin->w_width / 2;
|
||||
@ -1092,7 +1095,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
int minheight;
|
||||
int available;
|
||||
if (flags & (WSP_BOT | WSP_TOP)) {
|
||||
if (toplevel) {
|
||||
minheight = frame_minheight(topframe, NOWIN) + need_status;
|
||||
available = topframe->fr_height;
|
||||
needed += minheight;
|
||||
@ -1119,7 +1122,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
if (available < needed && new_in_layout) {
|
||||
emsg(_(e_noroom));
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
oldwin_height = oldwin->w_height;
|
||||
if (need_status) {
|
||||
@ -1182,7 +1185,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
&& ((flags & WSP_BOT)
|
||||
|| (flags & WSP_BELOW)
|
||||
|| (!(flags & WSP_ABOVE)
|
||||
&& ((flags & WSP_VERT) ? p_spr : p_sb)))) {
|
||||
&& (vertical ? p_spr : p_sb)))) {
|
||||
// new window below/right of current one
|
||||
if (new_wp == NULL) {
|
||||
wp = win_alloc(oldwin, false);
|
||||
@ -1199,7 +1202,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
|
||||
if (new_wp == NULL) {
|
||||
if (wp == NULL) {
|
||||
return FAIL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_frame(wp);
|
||||
@ -1218,9 +1221,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
frame_T *curfrp;
|
||||
|
||||
// Reorganise the tree of frames to insert the new window.
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
|
||||
|| (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) {
|
||||
if (toplevel) {
|
||||
if ((topframe->fr_layout == FR_COL && !vertical)
|
||||
|| (topframe->fr_layout == FR_ROW && vertical)) {
|
||||
curfrp = topframe->fr_child;
|
||||
if (flags & WSP_BOT) {
|
||||
while (curfrp->fr_next != NULL) {
|
||||
@ -1237,7 +1240,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
before = false;
|
||||
} else if (flags & WSP_ABOVE) {
|
||||
before = true;
|
||||
} else if (flags & WSP_VERT) {
|
||||
} else if (vertical) {
|
||||
before = !p_spr;
|
||||
} else {
|
||||
before = !p_sb;
|
||||
@ -1285,14 +1288,14 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
wp->w_fraction = oldwin->w_fraction;
|
||||
|
||||
if (flags & WSP_VERT) {
|
||||
if (vertical) {
|
||||
wp->w_p_scr = curwin->w_p_scr;
|
||||
|
||||
if (need_status) {
|
||||
win_new_height(oldwin, oldwin->w_height - 1);
|
||||
oldwin->w_status_height = need_status;
|
||||
}
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if (toplevel) {
|
||||
// set height and row of new window to full height
|
||||
wp->w_winrow = tabline_height();
|
||||
win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2));
|
||||
@ -1316,7 +1319,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
wp->w_vsep_width = oldwin->w_vsep_width;
|
||||
oldwin->w_vsep_width = 1;
|
||||
}
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if (toplevel) {
|
||||
if (flags & WSP_BOT) {
|
||||
frame_add_vsep(curfrp);
|
||||
}
|
||||
@ -1338,7 +1341,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
} else {
|
||||
const bool is_stl_global = global_stl_height() > 0;
|
||||
// width and column of new window is same as current window
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if (toplevel) {
|
||||
wp->w_wincol = 0;
|
||||
win_new_width(wp, Columns);
|
||||
wp->w_vsep_width = 0;
|
||||
@ -1359,7 +1362,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
wp->w_hsep_height = oldwin->w_hsep_height;
|
||||
oldwin->w_hsep_height = is_stl_global ? 1 : 0;
|
||||
}
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if (toplevel) {
|
||||
int new_fr_height = curfrp->fr_height - new_size;
|
||||
if (is_stl_global) {
|
||||
if (flags & WSP_BOT) {
|
||||
@ -1405,7 +1408,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
frame_fix_height(oldwin);
|
||||
}
|
||||
|
||||
if (flags & (WSP_TOP | WSP_BOT)) {
|
||||
if (toplevel) {
|
||||
win_comp_pos();
|
||||
}
|
||||
|
||||
@ -1426,7 +1429,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
|
||||
// equalize the window sizes.
|
||||
if (do_equal || dir != 0) {
|
||||
win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v'));
|
||||
win_equal(wp, true, vertical ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v'));
|
||||
} else if (!is_aucmd_win(wp)) {
|
||||
win_fix_scroll(false);
|
||||
}
|
||||
@ -1447,10 +1450,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & WSP_NOENTER)) {
|
||||
// make the new window the current window
|
||||
win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS
|
||||
| WEE_TRIGGER_LEAVE_AUTOCMDS);
|
||||
if (flags & WSP_VERT) {
|
||||
}
|
||||
if (vertical) {
|
||||
p_wiw = i;
|
||||
} else {
|
||||
p_wh = i;
|
||||
@ -1461,7 +1466,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
|
||||
oldwin->w_pos_changed = true;
|
||||
}
|
||||
|
||||
return OK;
|
||||
return wp;
|
||||
}
|
||||
|
||||
// Initialize window "newp" from window "oldp".
|
||||
|
@ -30,6 +30,7 @@ enum {
|
||||
WSP_BELOW = 0x40, ///< put new window below/right
|
||||
WSP_ABOVE = 0x80, ///< put new window above/left
|
||||
WSP_NEWLOC = 0x100, ///< don't copy location list
|
||||
WSP_NOENTER = 0x200, ///< don't enter the new window
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -1232,6 +1232,437 @@ describe('API/win', function()
|
||||
)
|
||||
eq(wins_before, api.nvim_list_wins())
|
||||
end)
|
||||
|
||||
it('creates a split window', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = false,
|
||||
})
|
||||
eq('', api.nvim_win_get_config(win).relative)
|
||||
end)
|
||||
|
||||
it('creates split windows in the correct direction', function()
|
||||
local initial_win = api.nvim_get_current_win()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
})
|
||||
eq('', api.nvim_win_get_config(win).relative)
|
||||
|
||||
local layout = fn.winlayout()
|
||||
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', initial_win },
|
||||
},
|
||||
}, layout)
|
||||
end)
|
||||
|
||||
it("respects the 'split' option", function()
|
||||
local initial_win = api.nvim_get_current_win()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
split = 'below',
|
||||
})
|
||||
eq('', api.nvim_win_get_config(win).relative)
|
||||
|
||||
local layout = fn.winlayout()
|
||||
|
||||
eq({
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', initial_win },
|
||||
{ 'leaf', win },
|
||||
},
|
||||
}, layout)
|
||||
end)
|
||||
|
||||
it(
|
||||
"doesn't change tp_curwin when splitting window in non-current tab with enter=false",
|
||||
function()
|
||||
local tab1 = api.nvim_get_current_tabpage()
|
||||
local tab1_win = api.nvim_get_current_win()
|
||||
|
||||
helpers.command('tabnew')
|
||||
local tab2 = api.nvim_get_current_tabpage()
|
||||
local tab2_win = api.nvim_get_current_win()
|
||||
|
||||
eq({ tab1_win, tab2_win }, api.nvim_list_wins())
|
||||
eq({ tab1, tab2 }, api.nvim_list_tabpages())
|
||||
|
||||
api.nvim_set_current_tabpage(tab1)
|
||||
eq(tab1_win, api.nvim_get_current_win())
|
||||
|
||||
local tab2_prevwin = fn.tabpagewinnr(tab2, '#')
|
||||
|
||||
-- split in tab2 whine in tab2, with enter = false
|
||||
local tab2_win2 = api.nvim_open_win(api.nvim_create_buf(false, true), false, {
|
||||
win = tab2_win,
|
||||
split = 'right',
|
||||
})
|
||||
eq(tab1_win, api.nvim_get_current_win()) -- we should still be in the first tp
|
||||
eq(tab1_win, api.nvim_tabpage_get_win(tab1))
|
||||
|
||||
eq(tab2_win, api.nvim_tabpage_get_win(tab2)) -- tab2's tp_curwin should not have changed
|
||||
eq(tab2_prevwin, fn.tabpagewinnr(tab2, '#')) -- tab2's tp_prevwin should not have changed
|
||||
eq({ tab1_win, tab2_win, tab2_win2 }, api.nvim_list_wins())
|
||||
eq({ tab2_win, tab2_win2 }, api.nvim_tabpage_list_wins(tab2))
|
||||
end
|
||||
)
|
||||
|
||||
it('creates splits in the correct location', function()
|
||||
local first_win = api.nvim_get_current_win()
|
||||
-- specifying window 0 should create a split next to the current window
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = false,
|
||||
})
|
||||
local layout = fn.winlayout()
|
||||
eq({
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', first_win },
|
||||
},
|
||||
}, layout)
|
||||
-- not specifying a window should create a top-level split
|
||||
local win2 = api.nvim_open_win(0, true, {
|
||||
split = 'left',
|
||||
win = -1,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win2 },
|
||||
{
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', first_win },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, layout)
|
||||
|
||||
-- specifying a window should create a split next to that window
|
||||
local win3 = api.nvim_open_win(0, true, {
|
||||
win = win,
|
||||
vertical = false,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win2 },
|
||||
{
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win3 },
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', first_win },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, layout)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('set_config', function()
|
||||
it('moves a split into a float', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = false,
|
||||
})
|
||||
eq('', api.nvim_win_get_config(win).relative)
|
||||
api.nvim_win_set_config(win, {
|
||||
relative = 'editor',
|
||||
row = 5,
|
||||
col = 5,
|
||||
width = 5,
|
||||
height = 5,
|
||||
})
|
||||
eq('editor', api.nvim_win_get_config(win).relative)
|
||||
end)
|
||||
|
||||
it('throws error when attempting to move the last window', function()
|
||||
local err = pcall_err(api.nvim_win_set_config, 0, {
|
||||
vertical = false,
|
||||
})
|
||||
eq('Cannot move last window', err)
|
||||
end)
|
||||
|
||||
it('passing retval of get_config results in no-op', function()
|
||||
-- simple split layout
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
split = 'left',
|
||||
})
|
||||
local layout = fn.winlayout()
|
||||
local config = api.nvim_win_get_config(win)
|
||||
api.nvim_win_set_config(win, config)
|
||||
eq(layout, fn.winlayout())
|
||||
|
||||
-- nested split layout
|
||||
local win2 = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
})
|
||||
local win3 = api.nvim_open_win(0, true, {
|
||||
win = win2,
|
||||
vertical = false,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
config = api.nvim_win_get_config(win2)
|
||||
api.nvim_win_set_config(win2, config)
|
||||
eq(layout, fn.winlayout())
|
||||
|
||||
config = api.nvim_win_get_config(win3)
|
||||
api.nvim_win_set_config(win3, config)
|
||||
eq(layout, fn.winlayout())
|
||||
end)
|
||||
|
||||
it('moves a float into a split', function()
|
||||
local layout = fn.winlayout()
|
||||
eq('leaf', layout[1])
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
relative = 'editor',
|
||||
row = 5,
|
||||
col = 5,
|
||||
width = 5,
|
||||
height = 5,
|
||||
})
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'below',
|
||||
win = -1,
|
||||
})
|
||||
eq('', api.nvim_win_get_config(win).relative)
|
||||
layout = fn.winlayout()
|
||||
eq('col', layout[1])
|
||||
eq(2, #layout[2])
|
||||
eq(win, layout[2][2][2])
|
||||
end)
|
||||
|
||||
it('respects the "split" option', function()
|
||||
local layout = fn.winlayout()
|
||||
eq('leaf', layout[1])
|
||||
local first_win = layout[2]
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
relative = 'editor',
|
||||
row = 5,
|
||||
col = 5,
|
||||
width = 5,
|
||||
height = 5,
|
||||
})
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'right',
|
||||
win = first_win,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
eq('row', layout[1])
|
||||
eq(2, #layout[2])
|
||||
eq(win, layout[2][2][2])
|
||||
local config = api.nvim_win_get_config(win)
|
||||
eq('', config.relative)
|
||||
eq('right', config.split)
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'below',
|
||||
win = first_win,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
eq('col', layout[1])
|
||||
eq(2, #layout[2])
|
||||
eq(win, layout[2][2][2])
|
||||
config = api.nvim_win_get_config(win)
|
||||
eq('', config.relative)
|
||||
eq('below', config.split)
|
||||
end)
|
||||
|
||||
it('creates top-level splits', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = false,
|
||||
})
|
||||
local win2 = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
win = -1,
|
||||
})
|
||||
local layout = fn.winlayout()
|
||||
eq('row', layout[1])
|
||||
eq(2, #layout[2])
|
||||
eq(win2, layout[2][1][2])
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'below',
|
||||
win = -1,
|
||||
})
|
||||
layout = fn.winlayout()
|
||||
eq('col', layout[1])
|
||||
eq(2, #layout[2])
|
||||
eq('row', layout[2][1][1])
|
||||
eq(win, layout[2][2][2])
|
||||
end)
|
||||
|
||||
it('moves splits to other tabpages', function()
|
||||
local curtab = api.nvim_get_current_tabpage()
|
||||
local win = api.nvim_open_win(0, false, { split = 'left' })
|
||||
command('tabnew')
|
||||
local tabnr = api.nvim_get_current_tabpage()
|
||||
command('tabprev') -- return to the initial tab
|
||||
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'right',
|
||||
win = api.nvim_tabpage_get_win(tabnr),
|
||||
})
|
||||
|
||||
eq(tabnr, api.nvim_win_get_tabpage(win))
|
||||
-- we are changing the config, the current tabpage should not change
|
||||
eq(curtab, api.nvim_get_current_tabpage())
|
||||
|
||||
command('tabnext') -- switch to the new tabpage so we can get the layout
|
||||
local layout = fn.winlayout()
|
||||
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', api.nvim_tabpage_get_win(tabnr) },
|
||||
{ 'leaf', win },
|
||||
},
|
||||
}, layout)
|
||||
end)
|
||||
|
||||
it('correctly moves curwin when moving curwin to a different tabpage', function()
|
||||
local curtab = api.nvim_get_current_tabpage()
|
||||
command('tabnew')
|
||||
local tab2 = api.nvim_get_current_tabpage()
|
||||
local tab2_win = api.nvim_get_current_win()
|
||||
|
||||
command('tabprev') -- return to the initial tab
|
||||
|
||||
local neighbor = api.nvim_get_current_win()
|
||||
|
||||
-- create and enter a new split
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = false,
|
||||
})
|
||||
|
||||
eq(curtab, api.nvim_win_get_tabpage(win))
|
||||
|
||||
eq({ win, neighbor }, api.nvim_tabpage_list_wins(curtab))
|
||||
|
||||
-- move the current win to a different tabpage
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'right',
|
||||
win = api.nvim_tabpage_get_win(tab2),
|
||||
})
|
||||
|
||||
eq(curtab, api.nvim_get_current_tabpage())
|
||||
|
||||
-- win should have moved to tab2
|
||||
eq(tab2, api.nvim_win_get_tabpage(win))
|
||||
-- tp_curwin of tab2 should not have changed
|
||||
eq(tab2_win, api.nvim_tabpage_get_win(tab2))
|
||||
-- win lists should be correct
|
||||
eq({ tab2_win, win }, api.nvim_tabpage_list_wins(tab2))
|
||||
eq({ neighbor }, api.nvim_tabpage_list_wins(curtab))
|
||||
|
||||
-- current win should have moved to neighboring win
|
||||
eq(neighbor, api.nvim_tabpage_get_win(curtab))
|
||||
end)
|
||||
|
||||
it('splits windows in non-current tabpage', function()
|
||||
local curtab = api.nvim_get_current_tabpage()
|
||||
command('tabnew')
|
||||
local tabnr = api.nvim_get_current_tabpage()
|
||||
command('tabprev') -- return to the initial tab
|
||||
|
||||
local win = api.nvim_open_win(0, false, {
|
||||
vertical = false,
|
||||
win = api.nvim_tabpage_get_win(tabnr),
|
||||
})
|
||||
|
||||
eq(tabnr, api.nvim_win_get_tabpage(win))
|
||||
-- since enter = false, the current tabpage should not change
|
||||
eq(curtab, api.nvim_get_current_tabpage())
|
||||
end)
|
||||
|
||||
it('moves the current split window', function()
|
||||
local initial_win = api.nvim_get_current_win()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
})
|
||||
local win2 = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
})
|
||||
api.nvim_set_current_win(win)
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win2 },
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', initial_win },
|
||||
},
|
||||
}, fn.winlayout())
|
||||
|
||||
api.nvim_win_set_config(0, {
|
||||
vertical = false,
|
||||
win = 0,
|
||||
})
|
||||
eq(win, api.nvim_get_current_win())
|
||||
eq({
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win2 },
|
||||
{ 'leaf', initial_win },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, fn.winlayout())
|
||||
|
||||
api.nvim_set_current_win(win2)
|
||||
local win3 = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
})
|
||||
eq(win3, api.nvim_get_current_win())
|
||||
|
||||
eq({
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win3 },
|
||||
{ 'leaf', win2 },
|
||||
{ 'leaf', initial_win },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, fn.winlayout())
|
||||
|
||||
api.nvim_win_set_config(0, {
|
||||
vertical = false,
|
||||
win = 0,
|
||||
})
|
||||
|
||||
eq(win3, api.nvim_get_current_win())
|
||||
eq({
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{
|
||||
'row',
|
||||
{
|
||||
{
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win3 },
|
||||
{ 'leaf', win2 },
|
||||
},
|
||||
},
|
||||
{ 'leaf', initial_win },
|
||||
},
|
||||
},
|
||||
},
|
||||
}, fn.winlayout())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('get_config', function()
|
||||
@ -1292,6 +1723,154 @@ describe('API/win', function()
|
||||
eq(title, cfg.title)
|
||||
eq(footer, cfg.footer)
|
||||
end)
|
||||
|
||||
it('includes split for normal windows', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
vertical = true,
|
||||
win = -1,
|
||||
})
|
||||
eq('left', api.nvim_win_get_config(win).split)
|
||||
api.nvim_win_set_config(win, {
|
||||
vertical = false,
|
||||
win = -1,
|
||||
})
|
||||
eq('above', api.nvim_win_get_config(win).split)
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'below',
|
||||
win = -1,
|
||||
})
|
||||
eq('below', api.nvim_win_get_config(win).split)
|
||||
end)
|
||||
|
||||
it('includes split when splitting with ex commands', function()
|
||||
local win = api.nvim_get_current_win()
|
||||
eq('left', api.nvim_win_get_config(win).split)
|
||||
|
||||
command('vsplit')
|
||||
local win2 = api.nvim_get_current_win()
|
||||
|
||||
-- initial window now be marked as right split
|
||||
-- since it was split with a vertical split
|
||||
-- and 'splitright' is false by default
|
||||
eq('right', api.nvim_win_get_config(win).split)
|
||||
eq('left', api.nvim_win_get_config(win2).split)
|
||||
|
||||
api.nvim_set_option_value('splitbelow', true, {
|
||||
scope = 'global',
|
||||
})
|
||||
api.nvim_win_close(win, true)
|
||||
command('split')
|
||||
local win3 = api.nvim_get_current_win()
|
||||
eq('below', api.nvim_win_get_config(win3).split)
|
||||
end)
|
||||
|
||||
it("includes the correct 'split' option in complex layouts", function()
|
||||
local initial_win = api.nvim_get_current_win()
|
||||
local win = api.nvim_open_win(0, false, {
|
||||
split = 'right',
|
||||
win = -1,
|
||||
})
|
||||
|
||||
local win2 = api.nvim_open_win(0, false, {
|
||||
split = 'below',
|
||||
win = win,
|
||||
})
|
||||
|
||||
api.nvim_win_set_config(win2, {
|
||||
width = 50,
|
||||
})
|
||||
|
||||
api.nvim_win_set_config(win, {
|
||||
split = 'left',
|
||||
win = -1,
|
||||
})
|
||||
|
||||
local win3 = api.nvim_open_win(0, false, {
|
||||
split = 'above',
|
||||
win = -1,
|
||||
})
|
||||
local float = api.nvim_open_win(0, false, {
|
||||
relative = 'editor',
|
||||
width = 40,
|
||||
height = 20,
|
||||
col = 20,
|
||||
row = 10,
|
||||
})
|
||||
api.nvim_win_set_config(float, {
|
||||
split = 'right',
|
||||
win = -1,
|
||||
})
|
||||
|
||||
local layout = fn.winlayout()
|
||||
|
||||
eq({
|
||||
'row',
|
||||
{
|
||||
{
|
||||
'col',
|
||||
{
|
||||
{ 'leaf', win3 },
|
||||
{
|
||||
'row',
|
||||
{
|
||||
{ 'leaf', win },
|
||||
{ 'leaf', initial_win },
|
||||
{ 'leaf', win2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'leaf',
|
||||
float,
|
||||
},
|
||||
},
|
||||
}, layout)
|
||||
|
||||
eq('above', api.nvim_win_get_config(win3).split)
|
||||
eq('left', api.nvim_win_get_config(win).split)
|
||||
eq('left', api.nvim_win_get_config(initial_win).split)
|
||||
eq('right', api.nvim_win_get_config(win2).split)
|
||||
eq('right', api.nvim_win_get_config(float).split)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('set_config', function()
|
||||
it('no crash with invalid title', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
width = 10,
|
||||
height = 10,
|
||||
relative = 'editor',
|
||||
row = 10,
|
||||
col = 10,
|
||||
title = { { 'test' } },
|
||||
border = 'single',
|
||||
})
|
||||
eq(
|
||||
'title/footer cannot be an empty array',
|
||||
pcall_err(api.nvim_win_set_config, win, { title = {} })
|
||||
)
|
||||
command('redraw!')
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('no crash with invalid footer', function()
|
||||
local win = api.nvim_open_win(0, true, {
|
||||
width = 10,
|
||||
height = 10,
|
||||
relative = 'editor',
|
||||
row = 10,
|
||||
col = 10,
|
||||
footer = { { 'test' } },
|
||||
border = 'single',
|
||||
})
|
||||
eq(
|
||||
'title/footer cannot be an empty array',
|
||||
pcall_err(api.nvim_win_set_config, win, { footer = {} })
|
||||
)
|
||||
command('redraw!')
|
||||
assert_alive()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('set_config', function()
|
||||
|
@ -104,14 +104,20 @@ describe('float window', function()
|
||||
end)
|
||||
|
||||
it('open with WinNew autocmd', function()
|
||||
local res = exec_lua([[
|
||||
local triggerd = false
|
||||
local new_triggered_before_enter, new_curwin, win = unpack(exec_lua([[
|
||||
local enter_triggered = false
|
||||
local new_triggered_before_enter = false
|
||||
local new_curwin
|
||||
local buf = vim.api.nvim_create_buf(true, true)
|
||||
vim.api.nvim_create_autocmd('WinNew', {
|
||||
callback = function(opt)
|
||||
if opt.buf == buf then
|
||||
triggerd = true
|
||||
vim.api.nvim_create_autocmd('WinEnter', {
|
||||
callback = function()
|
||||
enter_triggered = true
|
||||
end
|
||||
})
|
||||
vim.api.nvim_create_autocmd('WinNew', {
|
||||
callback = function()
|
||||
new_triggered_before_enter = not enter_triggered
|
||||
new_curwin = vim.api.nvim_get_current_win()
|
||||
end
|
||||
})
|
||||
local opts = {
|
||||
@ -120,10 +126,11 @@ describe('float window', function()
|
||||
width = 1, height = 1,
|
||||
noautocmd = false,
|
||||
}
|
||||
vim.api.nvim_open_win(buf, true, opts)
|
||||
return triggerd
|
||||
]])
|
||||
eq(true, res)
|
||||
local win = vim.api.nvim_open_win(buf, true, opts)
|
||||
return {new_triggered_before_enter, new_curwin, win}
|
||||
]]))
|
||||
eq(true, new_triggered_before_enter)
|
||||
eq(win, new_curwin)
|
||||
end)
|
||||
|
||||
it('opened with correct height', function()
|
||||
@ -1095,7 +1102,7 @@ describe('float window', function()
|
||||
local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20, zindex=60, hide=false}
|
||||
eq(expected, api.nvim_win_get_config(win))
|
||||
|
||||
eq({relative='', external=false, focusable=true, hide=false}, api.nvim_win_get_config(0))
|
||||
eq({external=false, focusable=true, hide=false, relative='',split="left",width=40,height=6}, api.nvim_win_get_config(0))
|
||||
|
||||
if multigrid then
|
||||
api.nvim_win_set_config(win, {external=true, width=10, height=1})
|
||||
@ -2878,27 +2885,31 @@ describe('float window', function()
|
||||
it('API has proper error messages', function()
|
||||
local buf = api.nvim_create_buf(false,false)
|
||||
eq("Invalid key: 'bork'",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,bork=true}))
|
||||
eq("'win' key is only valid with relative='win'",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,bork=true}))
|
||||
eq("'win' key is only valid with relative='win' and relative=''",
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
|
||||
eq("floating windows cannot have 'vertical'",
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor',row=0,col=0,vertical=true}))
|
||||
eq("floating windows cannot have 'split'",
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor',row=0,col=0,split="left"}))
|
||||
eq("Only one of 'relative' and 'external' must be used",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,external=true}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor',row=0,col=0,external=true}))
|
||||
eq("Invalid value of 'relative' key",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,relative='shell',row=0,col=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='shell',row=0,col=0}))
|
||||
eq("Invalid value of 'anchor' key",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,anchor='bottom'}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor',row=0,col=0,anchor='bottom'}))
|
||||
eq("'relative' requires 'row'/'col' or 'bufpos'",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=2,relative='editor'}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=2,relative='editor'}))
|
||||
eq("'width' key must be a positive Integer",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=-1,height=2,relative='editor', row=0, col=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=-1,height=2,relative='editor', row=0, col=0}))
|
||||
eq("'height' key must be a positive Integer",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=-1,relative='editor', row=0, col=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=-1,relative='editor', row=0, col=0}))
|
||||
eq("'height' key must be a positive Integer",
|
||||
pcall_err(api.nvim_open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {width=20,height=0,relative='editor', row=0, col=0}))
|
||||
eq("Must specify 'width'",
|
||||
pcall_err(api.nvim_open_win,buf, false, {relative='editor', row=0, col=0}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {relative='editor', row=0, col=0}))
|
||||
eq("Must specify 'height'",
|
||||
pcall_err(api.nvim_open_win,buf, false, {relative='editor', row=0, col=0, width=2}))
|
||||
pcall_err(api.nvim_open_win, buf, false, {relative='editor', row=0, col=0, width=2}))
|
||||
end)
|
||||
|
||||
it('can be placed relative window or cursor', function()
|
||||
|
Loading…
Reference in New Issue
Block a user