Merge pull request #9726 from mhinz/nvim_win_get_config

Closes #9723
This commit is contained in:
Marco Hinz 2019-03-17 02:34:50 +01:00 committed by GitHub
commit 9ef8d0d6b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 272 additions and 173 deletions

View File

@ -245,10 +245,10 @@ functional buffer windows and support user editing. They support the standard
'statusline' is not supported currently). 'statusline' is not supported currently).
Floating windows are created either by |nvim_open_win()| to open a new window, Floating windows are created either by |nvim_open_win()| to open a new window,
or |nvim_win_config()| to reconfigure a normal window into a float. Currently or |nvim_win_set_config()| to reconfigure a normal window into a float.
the position can either be grid coordinates relative the top-left of some Currently the position can either be grid coordinates relative the top-left of
window, or a position relative to the current window cursor. The parameters some window, or a position relative to the current window cursor. The
for positioning are described in detail at |nvim_open_win()| help. parameters for positioning are described in detail at |nvim_open_win()|.
|nvim_open_win()| assumes an existing buffer to display in the window. To create |nvim_open_win()| assumes an existing buffer to display in the window. To create
a scratch buffer for the float, |nvim_create_buffer()| can be used. The text in a scratch buffer for the float, |nvim_create_buffer()| can be used. The text in
@ -264,8 +264,9 @@ Here is an example for creating a float with scratch buffer: >
let buf = nvim_create_buf(v:false, v:true) let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"]) call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"])
let opts = {'relative': 'cursor', 'col':0, 'row':1, 'anchor': 'NW'} let opts = {'relative': 'cursor', 'width': 10, 'height': 2, 'col': 0,
let win = nvim_open_win(buf, 0, 10, 2, opts) \ 'row': 1, 'anchor': 'NW'}
let win = nvim_open_win(buf, 0, opts)
" optional: change highlight, otherwise Pmenu is used " optional: change highlight, otherwise Pmenu is used
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight') call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
> >
@ -624,8 +625,7 @@ nvim_create_buf({listed}, {scratch}) *nvim_create_buf()*
Return: ~ Return: ~
Buffer handle, or 0 on error Buffer handle, or 0 on error
*nvim_open_win()* nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
nvim_open_win({buffer}, {enter}, {width}, {height}, {options})
Open a new window. Open a new window.
Currently this is used to open floating and external windows. Currently this is used to open floating and external windows.
@ -643,21 +643,21 @@ nvim_open_win({buffer}, {enter}, {width}, {height}, {options})
{buffer} handle of buffer to be displayed in the window {buffer} handle of buffer to be displayed in the window
{enter} whether the window should be entered (made the {enter} whether the window should be entered (made the
current window) current window)
{width} width of window (in character cells) {config} dictionary for the window configuration accepts
{height} height of window (in character cells) these keys:
{options} dict of options for configuring window
positioning accepts the following keys:
`relative`: If set, the window becomes a `relative`: If set, the window becomes a
floating window. The window will be placed with floating window. The window will be placed with
row,col coordinates relative one of the row,col coordinates relative one of the
following: following:
"editor" the global editor grid "editor" the global editor grid
"win" a window. Use 'win' option below to "win" a window. Use `win` to specify window id,
specify window id, or current window will or current window will be used by default.
be used by default.
"cursor" the cursor position in current window. "cursor" the cursor position in current window.
`win`: when using `relative='win'`, window id of the window
where the position is defined.
`anchor`: the corner of the float that the row,col `anchor`: the corner of the float that the row,col
position defines position defines
"NW" north-west (default) "NW" north-west (default)
@ -670,18 +670,19 @@ nvim_open_win({buffer}, {enter}, {width}, {height}, {options})
the window can still be entered using the window can still be entered using
|nvim_set_current_win()| API call. |nvim_set_current_win()| API call.
`height`: Window height in character cells. Minimum of 1.
`width`: Window width in character cells. Minimum of 2.
`row`: row position. Screen cell height are used as unit. `row`: row position. Screen cell height are used as unit.
Can be floating point. Can be floating point.
`col`: column position. Screen cell width is used as `col`: column position. Screen cell width is used as
unit. Can be floating point. unit. Can be floating point.
`win`: when using relative='win', window id of the window
where the position is defined.
`external`: GUI should display the window as an external `external`: GUI should display the window as an external
top-level window. Currently accepts no other top-level window. Currently accepts no other
positioning options together with this. positioning configuration together with this.
With editor positioning row=0, col=0 refers to the top-left With editor positioning row=0, col=0 refers to the top-left
corner of the screen-grid and row=Lines-1, Columns-1 refers to corner of the screen-grid and row=Lines-1, Columns-1 refers to
@ -1561,21 +1562,37 @@ nvim_win_is_valid({window}) *nvim_win_is_valid()*
Return: ~ Return: ~
true if the window is valid, false otherwise true if the window is valid, false otherwise
*nvim_win_config()* nvim_win_set_config({window}, {config}) *nvim_win_set_config()*
nvim_win_config({window}, {width}, {height}, {options})
Configure window position. Currently this is only used to Configure window position. Currently this is only used to
configure floating and external windows (including changing a configure floating and external windows (including changing a
split window to these types). split window to these types).
See documentation at |nvim_open_win()|, for the meaning of See documentation at |nvim_open_win()|, for the meaning of
parameters. Pass in -1 for 'witdh' and 'height' to keep parameters.
exiting size.
When reconfiguring a floating window, absent option keys will When reconfiguring a floating window, absent option keys will
not be changed. The following restriction apply: `row`, `col` not be changed. The following restriction apply: `row`, `col`
and `relative` must be reconfigured together. Only changing a and `relative` must be reconfigured together. Only changing a
subset of these is an error. subset of these is an error.
Parameters: ~
{window} Window handle
{config} Dictionary of window configuration
nvim_win_get_config({window}) *nvim_win_get_config()*
Return window configuration.
Return a dictionary containing the same config that can be
given to |nvim_open_win()|.
`relative` will be an empty string for normal windows.
Parameters: ~
{window} Window handle
Return: ~
Window configuration
nvim_win_close({window}, {force}) *nvim_win_close()* nvim_win_close({window}, {force}) *nvim_win_close()*
Close a window. Close a window.

View File

@ -1003,35 +1003,35 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// Exactly one of `external` and `relative` must be specified. /// Exactly one of `external` and `relative` must be specified.
/// ///
/// @param buffer handle of buffer to be displayed in the window /// @param buffer handle of buffer to be displayed in the window
/// @param enter whether the window should be entered (made the current window) /// @param enter whether the window should be entered (made the current window)
/// @param width width of window (in character cells) /// @param config dictionary for the window configuration accepts these keys:
/// @param height height of window (in character cells) ///
/// @param options dict of options for configuring window positioning
/// accepts the following keys:
/// `relative`: If set, the window becomes a floating window. The window /// `relative`: If set, the window becomes a floating window. The window
/// will be placed with row,col coordinates relative one of the /// will be placed with row,col coordinates relative one of the
/// following: /// following:
/// "editor" the global editor grid /// "editor" the global editor grid
/// "win" a window. Use 'win' option below to specify window id, /// "win" a window. Use `win` to specify a window id,
/// or current window will be used by default. /// or the current window will be used by default.
/// "cursor" the cursor position in current window. /// "cursor" the cursor position in current window.
/// `anchor`: the corner of the float that the row,col position defines /// `win`: When using relative='win', window id of the window where the
/// position is defined.
/// `anchor`: The corner of the float that the row,col position defines:
/// "NW" north-west (default) /// "NW" north-west (default)
/// "NE" north-east /// "NE" north-east
/// "SW" south-west /// "SW" south-west
/// "SE" south-east /// "SE" south-east
/// `focusable`: Whether window can be focused by wincmds and /// `height`: window height (in character cells). Minimum of 1.
/// mouse events. Defaults to true. Even if set to false, the window /// `width`: window width (in character cells). Minimum of 2.
/// can still be entered using |nvim_set_current_win()| API call. /// `row`: row position. Screen cell height are used as unit. Can be
/// `row`: row position. Screen cell height are used as unit. Can be
/// floating point. /// floating point.
/// `col`: column position. Screen cell width is used as unit. Can be /// `col`: column position. Screen cell width is used as unit. Can be
/// floating point. /// floating point.
/// `win`: when using relative='win', window id of the window where the /// `focusable`: Whether window can be focused by wincmds and
/// position is defined. /// mouse events. Defaults to true. Even if set to false, the window
/// `external` GUI should display the window as an external /// can still be entered using |nvim_set_current_win()| API call.
/// top-level window. Currently accepts no other positioning options /// `external`: GUI should display the window as an external
/// together with this. /// top-level window. Currently accepts no other positioning
/// configuration together with this.
/// ///
/// With editor positioning row=0, col=0 refers to the top-left corner of the /// With editor positioning row=0, col=0 refers to the top-left corner of the
/// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner. /// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner.
@ -1047,16 +1047,15 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
/// ///
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
/// @return the window handle or 0 when error /// @return the window handle or 0 when error
Window nvim_open_win(Buffer buffer, Boolean enter, Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
Integer width, Integer height, Error *err)
Dictionary options, Error *err)
FUNC_API_SINCE(6) FUNC_API_SINCE(6)
{ {
FloatConfig config = FLOAT_CONFIG_INIT; FloatConfig fconfig = FLOAT_CONFIG_INIT;
if (!parse_float_config(options, &config, false, err)) { if (!parse_float_config(config, &fconfig, false, err)) {
return 0; return 0;
} }
win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err); win_T *wp = win_new_float(NULL, fconfig, err);
if (!wp) { if (!wp) {
return 0; return 0;
} }

View File

@ -438,14 +438,16 @@ Boolean nvim_win_is_valid(Window window)
/// floating and external windows (including changing a split window to these /// floating and external windows (including changing a split window to these
/// types). /// types).
/// ///
/// See documentation at |nvim_open_win()|, for the meaning of parameters. Pass /// See documentation at |nvim_open_win()|, for the meaning of parameters.
/// in -1 for 'witdh' and 'height' to keep exiting size.
/// ///
/// When reconfiguring a floating window, absent option keys will not be /// When reconfiguring a floating window, absent option keys will not be
/// changed. The following restriction apply: `row`, `col` and `relative` /// changed. The following restriction apply: `row`, `col` and `relative`
/// must be reconfigured together. Only changing a subset of these is an error. /// must be reconfigured together. Only changing a subset of these is an error.
void nvim_win_config(Window window, Integer width, Integer height, ///
Dictionary options, Error *err) /// @param window Window handle
/// @param config Dictionary of window configuration
/// @param[out] err Error details, if any
void nvim_win_set_config(Window window, Dictionary config, Error *err)
FUNC_API_SINCE(6) FUNC_API_SINCE(6)
{ {
win_T *win = find_window_by_handle(window, err); win_T *win = find_window_by_handle(window, err);
@ -453,25 +455,70 @@ void nvim_win_config(Window window, Integer width, Integer height,
return; return;
} }
bool new_float = !win->w_floating; bool new_float = !win->w_floating;
width = width > 0 ? width: win->w_width;
height = height > 0 ? height : win->w_height;
// reuse old values, if not overriden // reuse old values, if not overriden
FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
if (!parse_float_config(options, &config, !new_float, err)) { if (!parse_float_config(config, &fconfig, !new_float, err)) {
return; return;
} }
fconfig.height = fconfig.height > 0 ? fconfig.height : win->w_height;
fconfig.width = fconfig.width > 0 ? fconfig.width : win->w_width;
if (new_float) { if (new_float) {
if (!win_new_float(win, (int)width, (int)height, config, err)) { if (!win_new_float(win, fconfig, err)) {
return; return;
} }
redraw_later(NOT_VALID); redraw_later(NOT_VALID);
} else { } else {
win_config_float(win, (int)width, (int)height, config); win_config_float(win, fconfig);
win->w_pos_changed = true; win->w_pos_changed = true;
} }
} }
/// Return window configuration.
///
/// Return a dictionary containing the same config that can be given to
/// |nvim_open_win()|.
///
/// `relative` will be an empty string for normal windows.
///
/// @param window Window handle
/// @param[out] err Error details, if any
/// @return Window configuration
Dictionary nvim_win_get_config(Window window, Error *err)
FUNC_API_SINCE(6)
{
Dictionary rv = ARRAY_DICT_INIT;
win_T *wp = find_window_by_handle(window, err);
if (!wp) {
return rv;
}
PUT(rv, "width", INTEGER_OBJ(wp->w_float_config.width));
PUT(rv, "height", INTEGER_OBJ(wp->w_float_config.height));
PUT(rv, "focusable", BOOLEAN_OBJ(wp->w_float_config.focusable));
PUT(rv, "external", BOOLEAN_OBJ(wp->w_float_config.external));
PUT(rv, "anchor", STRING_OBJ(cstr_to_string(
float_anchor_str[wp->w_float_config.anchor])));
if (wp->w_float_config.relative == kFloatRelativeWindow) {
PUT(rv, "win", INTEGER_OBJ(wp->w_float_config.window));
}
if (wp->w_float_config.external) {
return rv;
}
PUT(rv, "row", FLOAT_OBJ(wp->w_float_config.row));
PUT(rv, "col", FLOAT_OBJ(wp->w_float_config.col));
const char *rel =
wp->w_floating ? float_relative_str[wp->w_float_config.relative] : "";
PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel)));
return rv;
}
/// Close a window. /// Close a window.
/// ///
/// This is equivalent to |:close| with count except that it takes a window id. /// This is equivalent to |:close| with count except that it takes a window id.

View File

@ -958,24 +958,32 @@ struct matchitem {
int conceal_char; ///< cchar for Conceal highlighting int conceal_char; ///< cchar for Conceal highlighting
}; };
typedef enum { typedef int FloatAnchor;
kFloatAnchorEast = 1, typedef int FloatRelative;
kFloatAnchorSouth = 2,
kFloatAnchorNW = 0, enum {
kFloatAnchorNE = 1, kFloatAnchorEast = 1,
kFloatAnchorSW = 2, kFloatAnchorSouth = 2,
kFloatAnchorSE = 3, };
} FloatAnchor;
typedef enum { // NW -> 0
kFloatRelativeEditor = 0, // NE -> kFloatAnchorEast
kFloatRelativeWindow = 1, // SW -> kFloatAnchorSouth
kFloatRelativeCursor = 2, // SE -> kFloatAnchorSouth | kFloatAnchorEast
} FloatRelative; EXTERN const char *const float_anchor_str[] INIT(= { "NW", "NE", "SW", "SE" });
enum {
kFloatRelativeEditor = 0,
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
};
EXTERN const char *const float_relative_str[] INIT(= { "editor", "window",
"cursor" });
typedef struct { typedef struct {
Window window; Window window;
int height, width;
double row, col; double row, col;
FloatAnchor anchor; FloatAnchor anchor;
FloatRelative relative; FloatRelative relative;
@ -983,7 +991,8 @@ typedef struct {
bool focusable; bool focusable;
} FloatConfig; } FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \ #define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
.row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \ .relative = 0, .external = false, \
.focusable = true }) .focusable = true })

View File

@ -460,7 +460,9 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
if (wp->w_floating) { if (wp->w_floating) {
if (width != wp->w_width && height != wp->w_height) { if (width != wp->w_width && height != wp->w_height) {
win_config_float(wp, (int)width, (int)height, wp->w_float_config); wp->w_float_config.width = width;
wp->w_float_config.height = height;
win_config_float(wp, wp->w_float_config);
} }
} else { } else {
// non-positive indicates no request // non-positive indicates no request

View File

@ -502,10 +502,11 @@ wingotofile:
break; break;
} }
FloatConfig config = FLOAT_CONFIG_INIT; FloatConfig config = FLOAT_CONFIG_INIT;
config.width = curwin->w_width;
config.height = curwin->w_height;
config.external = true; config.external = true;
Error err = ERROR_INIT; Error err = ERROR_INIT;
if (!win_new_float(curwin, curwin->w_width, curwin->w_height, config, if (!win_new_float(curwin, config, &err)) {
&err)) {
EMSG(err.msg); EMSG(err.msg);
api_clear_error(&err); api_clear_error(&err);
beep_flush(); beep_flush();
@ -538,8 +539,7 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
/// float. It must then already belong to the current tabpage! /// float. It must then already belong to the current tabpage!
/// ///
/// config must already have been validated! /// config must already have been validated!
win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config, win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
Error *err)
{ {
if (wp == NULL) { if (wp == NULL) {
wp = win_alloc(lastwin_nofloating(), false); wp = win_alloc(lastwin_nofloating(), false);
@ -571,27 +571,26 @@ win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config,
// TODO(bfredl): use set_option_to() after merging #9110 ? // TODO(bfredl): use set_option_to() after merging #9110 ?
wp->w_p_nu = false; wp->w_p_nu = false;
wp->w_allbuf_opt.wo_nu = false; wp->w_allbuf_opt.wo_nu = false;
win_config_float(wp, width, height, config); win_config_float(wp, fconfig);
wp->w_pos_changed = true; wp->w_pos_changed = true;
redraw_win_later(wp, VALID); redraw_win_later(wp, VALID);
return wp; return wp;
} }
void win_config_float(win_T *wp, int width, int height, void win_config_float(win_T *wp, FloatConfig fconfig)
FloatConfig config)
{ {
wp->w_height = MAX(height, 1); wp->w_width = MAX(fconfig.width, 2);
wp->w_width = MAX(width, 2); wp->w_height = MAX(fconfig.height, 1);
if (config.relative == kFloatRelativeCursor) { if (fconfig.relative == kFloatRelativeCursor) {
config.relative = kFloatRelativeWindow; fconfig.relative = kFloatRelativeWindow;
config.row += curwin->w_wrow; fconfig.row += curwin->w_wrow;
config.col += curwin->w_wcol; fconfig.col += curwin->w_wcol;
config.window = curwin->handle; fconfig.window = curwin->handle;
} }
bool change_external = config.external != wp->w_float_config.external; bool change_external = fconfig.external != wp->w_float_config.external;
wp->w_float_config = config; wp->w_float_config = fconfig;
if (!ui_has(kUIMultigrid)) { if (!ui_has(kUIMultigrid)) {
wp->w_height = MIN(wp->w_height, Rows-1); wp->w_height = MIN(wp->w_height, Rows-1);
@ -614,12 +613,6 @@ static void ui_ext_win_position(win_T *wp)
wp->w_wincol, wp->w_width, wp->w_height); wp->w_wincol, wp->w_width, wp->w_height);
return; return;
} }
const char *const anchor_str[] = {
"NW",
"NE",
"SW",
"SE"
};
FloatConfig c = wp->w_float_config; FloatConfig c = wp->w_float_config;
if (!c.external) { if (!c.external) {
@ -635,7 +628,7 @@ static void ui_ext_win_position(win_T *wp)
api_clear_error(&dummy); api_clear_error(&dummy);
} }
if (ui_has(kUIMultigrid)) { if (ui_has(kUIMultigrid)) {
String anchor = cstr_to_string(anchor_str[c.anchor]); String anchor = cstr_to_string(float_anchor_str[c.anchor]);
ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle, ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle,
row, col, c.focusable); row, col, c.focusable);
} else { } else {
@ -672,14 +665,14 @@ static bool parse_float_anchor(String anchor, FloatAnchor *out)
*out = (FloatAnchor)0; *out = (FloatAnchor)0;
} }
char *str = anchor.data; char *str = anchor.data;
if (!STRICMP(str, "NW")) { if (striequal(str, "NW")) {
*out = kFloatAnchorNW; *out = 0; // NW is the default
} else if (!STRICMP(str, "NE")) { } else if (striequal(str, "NE")) {
*out = kFloatAnchorNE; *out = kFloatAnchorEast;
} else if (!STRICMP(str, "SW")) { } else if (striequal(str, "SW")) {
*out = kFloatAnchorSW; *out = kFloatAnchorSouth;
} else if (!STRICMP(str, "SE")) { } else if (striequal(str, "SE")) {
*out = kFloatAnchorSE; *out = kFloatAnchorSouth | kFloatAnchorEast;
} else { } else {
return false; return false;
} }
@ -692,11 +685,11 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = (FloatRelative)0; *out = (FloatRelative)0;
} }
char *str = relative.data; char *str = relative.data;
if (!STRICMP(str, "editor")) { if (striequal(str, "editor")) {
*out = kFloatRelativeEditor; *out = kFloatRelativeEditor;
} else if (!STRICMP(str, "win")) { } else if (striequal(str, "win")) {
*out = kFloatRelativeWindow; *out = kFloatRelativeWindow;
} else if (!STRICMP(str, "cursor")) { } else if (striequal(str, "cursor")) {
*out = kFloatRelativeCursor; *out = kFloatRelativeCursor;
} else { } else {
return false; return false;
@ -704,7 +697,7 @@ static bool parse_float_relative(String relative, FloatRelative *out)
return true; return true;
} }
bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf, bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
Error *err) Error *err)
{ {
bool has_row = false, has_col = false, has_relative = false; bool has_row = false, has_col = false, has_relative = false;
@ -716,46 +709,62 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
if (!strcmp(key, "row")) { if (!strcmp(key, "row")) {
has_row = true; has_row = true;
if (val.type == kObjectTypeInteger) { if (val.type == kObjectTypeInteger) {
out->row = val.data.integer; fconfig->row = val.data.integer;
} else if (val.type == kObjectTypeFloat) { } else if (val.type == kObjectTypeFloat) {
out->row = val.data.floating; fconfig->row = val.data.floating;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'row' option has to be Integer or Float"); "'row' key must be Integer or Float");
return false; return false;
} }
} else if (!strcmp(key, "col")) { } else if (!strcmp(key, "col")) {
has_col = true; has_col = true;
if (val.type == kObjectTypeInteger) { if (val.type == kObjectTypeInteger) {
out->col = val.data.integer; fconfig->col = val.data.integer;
} else if (val.type == kObjectTypeFloat) { } else if (val.type == kObjectTypeFloat) {
out->col = val.data.floating; fconfig->col = val.data.floating;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'col' option has to be Integer or Float"); "'col' key must be Integer or Float");
return false;
}
} else if (strequal(key, "width")) {
if (val.type == kObjectTypeInteger && val.data.integer >= 0) {
fconfig->width = val.data.integer;
} else {
api_set_error(err, kErrorTypeValidation,
"'width' key must be a positive Integer");
return false;
}
} else if (strequal(key, "height")) {
if (val.type == kObjectTypeInteger && val.data.integer >= 0) {
fconfig->height= val.data.integer;
} else {
api_set_error(err, kErrorTypeValidation,
"'height' key must be a positive Integer");
return false; return false;
} }
} else if (!strcmp(key, "anchor")) { } else if (!strcmp(key, "anchor")) {
if (val.type != kObjectTypeString) { if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'anchor' option has to be String"); "'anchor' key must be String");
return false; return false;
} }
if (!parse_float_anchor(val.data.string, &out->anchor)) { if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Invalid value of 'anchor' option"); "Invalid value of 'anchor' key");
return false; return false;
} }
} else if (!strcmp(key, "relative")) { } else if (!strcmp(key, "relative")) {
has_relative = true; has_relative = true;
if (val.type != kObjectTypeString) { if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'relative' option has to be String"); "'relative' key must be String");
return false; return false;
} }
if (!parse_float_relative(val.data.string, &out->relative)) { if (!parse_float_relative(val.data.string, &fconfig->relative)) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Invalid value of 'relative' option"); "Invalid value of 'relative' key");
return false; return false;
} }
} else if (!strcmp(key, "win")) { } else if (!strcmp(key, "win")) {
@ -763,62 +772,63 @@ bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf,
if (val.type != kObjectTypeInteger if (val.type != kObjectTypeInteger
&& val.type != kObjectTypeWindow) { && val.type != kObjectTypeWindow) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'win' option has to be Integer or Window"); "'win' key must be Integer or Window");
return false; return false;
} }
out->window = val.data.integer; fconfig->window = val.data.integer;
} else if (!strcmp(key, "external")) { } else if (!strcmp(key, "external")) {
if (val.type == kObjectTypeInteger) { if (val.type == kObjectTypeInteger) {
out->external = val.data.integer; fconfig->external = val.data.integer;
} else if (val.type == kObjectTypeBoolean) { } else if (val.type == kObjectTypeBoolean) {
out->external = val.data.boolean; fconfig->external = val.data.boolean;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'external' option has to be Boolean"); "'external' key must be Boolean");
return false; return false;
} }
has_external = out->external; has_external = fconfig->external;
} else if (!strcmp(key, "focusable")) { } else if (!strcmp(key, "focusable")) {
if (val.type == kObjectTypeInteger) { if (val.type == kObjectTypeInteger) {
out->focusable = val.data.integer; fconfig->focusable = val.data.integer;
} else if (val.type == kObjectTypeBoolean) { } else if (val.type == kObjectTypeBoolean) {
out->focusable = val.data.boolean; fconfig->focusable = val.data.boolean;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'focusable' option has to be Boolean"); "'focusable' key must be Boolean");
return false; return false;
} }
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Invalid options key '%s'", key); "Invalid key '%s'", key);
return false; return false;
} }
} }
if (has_window && !(has_relative && out->relative == kFloatRelativeWindow)) { if (has_window && !(has_relative
&& fconfig->relative == kFloatRelativeWindow)) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'win' option is only valid with relative='win'"); "'win' key is only valid with relative='win'");
return false; return false;
} }
if ((has_relative && out->relative == kFloatRelativeWindow) if ((has_relative && fconfig->relative == kFloatRelativeWindow)
&& (!has_window || out->window == 0)) { && (!has_window || fconfig->window == 0)) {
out->window = curwin->handle; fconfig->window = curwin->handle;
} }
if (has_relative && has_external) { if (has_relative && has_external) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Only one of 'relative' and 'external' should be used"); "Only one of 'relative' and 'external' must be used");
return false; return false;
} else if (!reconf && !has_relative && !has_external) { } else if (!reconf && !has_relative && !has_external) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"One of 'relative' and 'external' must be used"); "One of 'relative' and 'external' must be used");
return false; return false;
} else if (has_relative) { } else if (has_relative) {
out->external = false; fconfig->external = false;
} }
if (out->external && !ui_has(kUIMultigrid)) { if (fconfig->external && !ui_has(kUIMultigrid)) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"UI doesn't support external windows"); "UI doesn't support external windows");
return false; return false;
@ -3810,7 +3820,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
for (win_T *wp = firstwin; wp; wp = wp->w_next) { for (win_T *wp = firstwin; wp; wp = wp->w_next) {
if (wp->w_floating && !wp->w_float_config.external) { if (wp->w_floating && !wp->w_float_config.external) {
win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); win_config_float(wp, wp->w_float_config);
} }
wp->w_pos_changed = true; wp->w_pos_changed = true;
} }
@ -4670,7 +4680,9 @@ int win_comp_pos(void)
// Too often, but when we support anchoring floats to split windows, // Too often, but when we support anchoring floats to split windows,
// this will be needed // this will be needed
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); wp->w_float_config.width = wp->w_width;
wp->w_float_config.height = wp->w_height;
win_config_float(wp, wp->w_float_config);
} }
return row; return row;
@ -4743,7 +4755,8 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) { if (win->w_floating) {
if (win->w_float_config.external) { if (win->w_float_config.external) {
win_config_float(win, win->w_width, height, win->w_float_config); win->w_float_config.height = height;
win_config_float(win, win->w_float_config);
} else { } else {
beep_flush(); beep_flush();
return; return;
@ -4948,7 +4961,8 @@ void win_setwidth_win(int width, win_T *wp)
} }
if (wp->w_floating) { if (wp->w_floating) {
if (wp->w_float_config.external) { if (wp->w_float_config.external) {
win_config_float(wp, width, wp->w_height, wp->w_float_config); wp->w_float_config.width = width;
win_config_float(wp, wp->w_float_config);
} else { } else {
beep_flush(); beep_flush();
return; return;

View File

@ -45,7 +45,7 @@ describe('floating windows', function()
it('can be created and reconfigured', function() it('can be created and reconfigured', function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
local win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
local expected_pos = { local expected_pos = {
[3]={{id=1001}, 'NW', 1, 2, 5, true}, [3]={{id=1001}, 'NW', 1, 2, 5, true},
} }
@ -84,7 +84,7 @@ describe('floating windows', function()
end end
meths.win_config(win,0,0,{relative='editor', row=0, col=10}) meths.win_set_config(win, {relative='editor', row=0, col=10})
expected_pos[3][4] = 0 expected_pos[3][4] = 0
expected_pos[3][5] = 10 expected_pos[3][5] = 10
if multigrid then if multigrid then
@ -152,11 +152,18 @@ describe('floating windows', function()
end end
end) end)
it('return their configuration', function()
local buf = meths.create_buf(false, false)
local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5})
local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20}
eq(expected, meths.win_get_config(win))
end)
it('defaults to nonumber and NormalFloat highlight', function() it('defaults to nonumber and NormalFloat highlight', function()
command('set number') command('set number')
command('hi NormalFloat guibg=#333333') command('hi NormalFloat guibg=#333333')
feed('ix<cr>y<cr><esc>gg') feed('ix<cr>y<cr><esc>gg')
local win = meths.open_win(0, false, 20, 4, {relative='editor', row=4, col=10}) local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -231,18 +238,22 @@ describe('floating windows', function()
it('API has proper error messages', function() it('API has proper error messages', function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
eq({false, "Invalid options key 'bork'"}, eq({false, "Invalid key 'bork'"},
meth_pcall(meths.open_win,buf, false, 20, 2, {bork=true})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,bork=true}))
eq({false, "'win' option is only valid with relative='win'"}, eq({false, "'win' key is only valid with relative='win'"},
meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,win=0})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
eq({false, "Only one of 'relative' and 'external' should be used"}, eq({false, "Only one of 'relative' and 'external' must be used"},
meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,external=true})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,external=true}))
eq({false, "Invalid value of 'relative' option"}, eq({false, "Invalid value of 'relative' key"},
meth_pcall(meths.open_win,buf, false, 20, 2, {relative='shell',row=0,col=0})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='shell',row=0,col=0}))
eq({false, "Invalid value of 'anchor' option"}, eq({false, "Invalid value of 'anchor' key"},
meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,anchor='bottom'})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,anchor='bottom'}))
eq({false, "All of 'relative', 'row', and 'col' has to be specified at once"}, eq({false, "All of 'relative', 'row', and 'col' has to be specified at once"},
meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor'})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor'}))
eq({false, "'width' key must be a positive Integer"},
meth_pcall(meths.open_win,buf, false, {width=-1,height=2,relative='editor'}))
eq({false, "'height' key must be a positive Integer"},
meth_pcall(meths.open_win,buf, false, {width=20,height=-1,relative='editor'}))
end) end)
it('can be placed relative window or cursor', function() it('can be placed relative window or cursor', function()
@ -288,7 +299,7 @@ describe('floating windows', function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
-- no 'win' arg, relative default window -- no 'win' arg, relative default window
local win = meths.open_win(buf, false, 20, 2, {relative='win', row=0, col=10}) local win = meths.open_win(buf, false, {relative='win', width=20, height=2, row=0, col=10})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -329,7 +340,7 @@ describe('floating windows', function()
]]) ]])
end end
meths.win_config(win, -1, -1, {relative='cursor', row=1, col=-2}) meths.win_set_config(win, {relative='cursor', row=1, col=-2})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -370,7 +381,7 @@ describe('floating windows', function()
]]) ]])
end end
meths.win_config(win, -1, -1, {relative='cursor', row=0, col=0, anchor='SW'}) meths.win_set_config(win, {relative='cursor', row=0, col=0, anchor='SW'})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -412,7 +423,7 @@ describe('floating windows', function()
end end
meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=1, col=10, anchor='NW'}) meths.win_set_config(win, {relative='win', win=oldwin, row=1, col=10, anchor='NW'})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -453,7 +464,7 @@ describe('floating windows', function()
]]) ]])
end end
meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=3, col=39, anchor='SE'}) meths.win_set_config(win, {relative='win', win=oldwin, row=3, col=39, anchor='SE'})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -494,7 +505,7 @@ describe('floating windows', function()
]]) ]])
end end
meths.win_config(win, -1, -1, {relative='win', win=0, row=0, col=50, anchor='NE'}) meths.win_set_config(win, {relative='win', win=0, row=0, col=50, anchor='NE'})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -544,7 +555,7 @@ describe('floating windows', function()
screen2:attach(nil, session2) screen2:attach(nil, session2)
screen2:set_default_attr_ids(attrs) screen2:set_default_attr_ids(attrs)
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
meths.open_win(buf, true, 20, 2, {relative='editor', row=2, col=5}) meths.open_win(buf, true, {relative='editor', width=20, height=2, row=2, col=5})
local expected_pos = { local expected_pos = {
[2]={{id=1001}, 'NW', 1, 2, 5} [2]={{id=1001}, 'NW', 1, 2, 5}
} }
@ -577,7 +588,7 @@ describe('floating windows', function()
it('handles resized screen', function() it('handles resized screen', function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
meths.buf_set_lines(buf, 0, -1, true, {'such', 'very', 'float'}) meths.buf_set_lines(buf, 0, -1, true, {'such', 'very', 'float'})
local win = meths.open_win(buf, false, 15, 4, {relative='editor', row=2, col=10}) local win = meths.open_win(buf, false, {relative='editor', width=15, height=4, row=2, col=10})
local expected_pos = { local expected_pos = {
[4]={{id=1002}, 'NW', 1, 2, 10, true}, [4]={{id=1002}, 'NW', 1, 2, 10, true},
} }
@ -756,7 +767,7 @@ describe('floating windows', function()
]]) ]])
end end
meths.win_config(win, -1, 3, {}) meths.win_set_config(win, {width=0, height=3})
feed('gg') feed('gg')
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
@ -1096,7 +1107,7 @@ describe('floating windows', function()
it('does not crash when set cmdheight #9680', function() it('does not crash when set cmdheight #9680', function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
command("set cmdheight=2") command("set cmdheight=2")
eq(1, meths.eval('1')) eq(1, meths.eval('1'))
end) end)
@ -1104,7 +1115,7 @@ describe('floating windows', function()
describe('and completion', function() describe('and completion', function()
before_each(function() before_each(function()
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
local win = meths.open_win(buf, true, 12, 4, {relative='editor', row=2, col=5}) local win = meths.open_win(buf, true, {relative='editor', width=12, height=4, row=2, col=5})
meths.win_set_option(win , 'winhl', 'Normal:ErrorMsg') meths.win_set_option(win , 'winhl', 'Normal:ErrorMsg')
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
@ -1523,7 +1534,7 @@ describe('floating windows', function()
local buf = meths.create_buf(false,true) local buf = meths.create_buf(false,true)
meths.buf_set_lines(buf,0,-1,true,{"some info", "about item"}) meths.buf_set_lines(buf,0,-1,true,{"some info", "about item"})
win = meths.open_win(buf, false, 12, 2, {relative='cursor', row=1, col=10}) win = meths.open_win(buf, false, {relative='cursor', width=12, height=2, row=1, col=10})
if multigrid then if multigrid then
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -1714,7 +1725,7 @@ describe('floating windows', function()
command("set hidden") command("set hidden")
meths.buf_set_lines(0,0,-1,true,{"x"}) meths.buf_set_lines(0,0,-1,true,{"x"})
local buf = meths.create_buf(false,false) local buf = meths.create_buf(false,false)
win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=2, col=5})
meths.buf_set_lines(buf,0,-1,true,{"y"}) meths.buf_set_lines(buf,0,-1,true,{"y"})
expected_pos = { expected_pos = {
[3]={{id=1001}, 'NW', 1, 2, 5, true} [3]={{id=1001}, 'NW', 1, 2, 5, true}
@ -1824,7 +1835,7 @@ describe('floating windows', function()
end) end)
it("w with focusable=false", function() it("w with focusable=false", function()
meths.win_config(win, -1, -1, {focusable=false}) meths.win_set_config(win, {focusable=false})
expected_pos[3][6] = false expected_pos[3][6] = false
feed("<c-w>wi") -- i to provoke redraw feed("<c-w>wi") -- i to provoke redraw
if multigrid then if multigrid then
@ -2038,7 +2049,7 @@ describe('floating windows', function()
end) end)
it("focus by mouse (focusable=false)", function() it("focus by mouse (focusable=false)", function()
meths.win_config(win, -1, -1, {focusable=false}) meths.win_set_config(win, {focusable=false})
meths.buf_set_lines(0, -1, -1, true, {"a"}) meths.buf_set_lines(0, -1, -1, true, {"a"})
expected_pos[3][6] = false expected_pos[3][6] = false
if multigrid then if multigrid then
@ -2939,7 +2950,7 @@ describe('floating windows', function()
end end
if multigrid then if multigrid then
meths.win_config(0,-1,-1,{external=true}) meths.win_set_config(0, {external=true})
expected_pos = {[3]={external=true}} expected_pos = {[3]={external=true}}
screen:expect{grid=[[ screen:expect{grid=[[
## grid 1 ## grid 1
@ -2962,7 +2973,7 @@ describe('floating windows', function()
]], float_pos=expected_pos} ]], float_pos=expected_pos}
else else
eq({false, "UI doesn't support external windows"}, eq({false, "UI doesn't support external windows"},
meth_pcall(meths.win_config, 0,-1,-1,{external=true})) meth_pcall(meths.win_set_config, 0, {external=true}))
return return
end end
@ -3228,7 +3239,7 @@ describe('floating windows', function()
it(":tabnew and :tabnext (external)", function() it(":tabnew and :tabnext (external)", function()
if multigrid then if multigrid then
meths.win_config(win,-1,-1,{external=true}) meths.win_set_config(win, {external=true})
expected_pos = {[3]={external=true}} expected_pos = {[3]={external=true}}
feed(":tabnew<cr>") feed(":tabnew<cr>")
screen:expect{grid=[[ screen:expect{grid=[[
@ -3259,7 +3270,7 @@ describe('floating windows', function()
]], float_pos=expected_pos} ]], float_pos=expected_pos}
else else
eq({false, "UI doesn't support external windows"}, eq({false, "UI doesn't support external windows"},
meth_pcall(meths.win_config, 0,-1,-1,{external=true})) meth_pcall(meths.win_set_config, 0, {external=true}))
end end
feed(":tabnext<cr>") feed(":tabnext<cr>")