api: numerous small fixes

This commit is contained in:
Marco Hinz 2019-03-16 13:57:50 +01:00
parent 27c4b6b9bd
commit 86992a7bb1
No known key found for this signature in database
GPG Key ID: 1C980A1B657B4A4F
5 changed files with 103 additions and 113 deletions

View File

@ -625,7 +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({buffer}, {enter}, {options}) *nvim_open_win()* nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
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,19 +643,21 @@ nvim_open_win({buffer}, {enter}, {options}) *nvim_open_win()*
{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)
{options} dict of options for configuring window {config} dictionary for the window configuration accepts
positioning accepts the following keys: these 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)
@ -668,11 +670,9 @@ nvim_open_win({buffer}, {enter}, {options}) *nvim_open_win()*
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. Cannot be `height`: Window height in character cells. Minimum of 1.
smaller than 1.
`width`: window width in character cells. Cannot be `width`: Window width in character cells. Minimum of 2.
smaller than 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.
@ -680,12 +680,9 @@ nvim_open_win({buffer}, {enter}, {options}) *nvim_open_win()*
`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
@ -1565,14 +1562,13 @@ 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_set_config({window}, {options}) *nvim_win_set_config()* nvim_win_set_config({window}, {config}) *nvim_win_set_config()*
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 0 for `width` and `height` to keep parameters.
existing 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`
@ -1580,16 +1576,16 @@ nvim_win_set_config({window}, {options}) *nvim_win_set_config()*
subset of these is an error. subset of these is an error.
Parameters: ~ Parameters: ~
{window} Window handle {window} Window handle
{options} Dictionary of options {config} Dictionary of window configuration
nvim_win_get_config({window}) *nvim_win_get_config()* nvim_win_get_config({window}) *nvim_win_get_config()*
Return window configuration. Return window configuration.
Return a dictionary containing the same options that can be Return a dictionary containing the same config that can be
given to |nvim_open_win()|. given to |nvim_open_win()|.
`relative` will be empty for normal windows. `relative` will be an empty string for normal windows.
Parameters: ~ Parameters: ~
{window} Window handle {window} Window handle

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 options dict of options for configuring window positioning /// @param config dictionary for the window configuration accepts these keys:
/// 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
/// `height`: window height (in character cells). Cannot be smaller than 1.
/// `width`: window width (in character cells). Cannot be smaller than 2.
/// `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,15 +1047,14 @@ 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, Dictionary options, Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err)
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, config, err); win_T *wp = win_new_float(NULL, fconfig, err);
if (!wp) { if (!wp) {
return 0; return 0;
} }

View File

@ -438,17 +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 0 for `width` and `height` to keep existing 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.
/// ///
/// @param window Window handle /// @param window Window handle
/// @param options Dictionary of options /// @param config Dictionary of window configuration
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
void nvim_win_set_config(Window window, Dictionary options, Error *err) 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);
@ -457,34 +456,34 @@ void nvim_win_set_config(Window window, Dictionary options, Error *err)
} }
bool new_float = !win->w_floating; bool new_float = !win->w_floating;
// 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;
} }
config.height = config.height > 0 ? config.height : win->w_height; fconfig.height = fconfig.height > 0 ? fconfig.height : win->w_height;
config.width = config.width > 0 ? config.width : win->w_width; fconfig.width = fconfig.width > 0 ? fconfig.width : win->w_width;
if (new_float) { if (new_float) {
if (!win_new_float(win, 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, config); win_config_float(win, fconfig);
win->w_pos_changed = true; win->w_pos_changed = true;
} }
} }
/// Return window configuration. /// Return window configuration.
/// ///
/// Return a dictionary containing the same options that can be given to /// Return a dictionary containing the same config that can be given to
/// |nvim_open_win()|. /// |nvim_open_win()|.
/// ///
/// `relative` will be empty for normal windows. /// `relative` will be an empty string for normal windows.
/// ///
/// @param window Window handle /// @param window Window handle
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
/// @return Window configuration /// @return Window configuration
Dictionary nvim_win_get_config(Window window, Error *err) Dictionary nvim_win_get_config(Window window, Error *err)
FUNC_API_SINCE(6) FUNC_API_SINCE(6)
{ {

View File

@ -539,7 +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, FloatConfig config, Error *err) win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
{ {
if (wp == NULL) { if (wp == NULL) {
wp = win_alloc(lastwin_nofloating(), false); wp = win_alloc(lastwin_nofloating(), false);
@ -571,26 +571,26 @@ win_T *win_new_float(win_T *wp, FloatConfig config, Error *err)
// 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, 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, FloatConfig config) void win_config_float(win_T *wp, FloatConfig fconfig)
{ {
wp->w_width = MAX(config.width, 2); wp->w_width = MAX(fconfig.width, 2);
wp->w_height = MAX(config.height, 1); 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);
@ -697,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;
@ -709,62 +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; return false;
} }
} else if (strequal(key, "width")) { } else if (strequal(key, "width")) {
if (val.type == kObjectTypeInteger && val.data.integer >= 0) { if (val.type == kObjectTypeInteger && val.data.integer >= 0) {
out->width = val.data.integer; fconfig->width = val.data.integer;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'width' option has to be a positive Integer"); "'width' key must be a positive Integer");
return false; return false;
} }
} else if (strequal(key, "height")) { } else if (strequal(key, "height")) {
if (val.type == kObjectTypeInteger && val.data.integer >= 0) { if (val.type == kObjectTypeInteger && val.data.integer >= 0) {
out->height= val.data.integer; fconfig->height= val.data.integer;
} else { } else {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"'height' option has to be a positive Integer"); "'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")) {
@ -772,62 +772,62 @@ 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;
@ -3819,8 +3819,6 @@ 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) {
wp->w_float_config.width = wp->w_width;
wp->w_float_config.height = wp->w_height;
win_config_float(wp, wp->w_float_config); win_config_float(wp, wp->w_float_config);
} }
wp->w_pos_changed = true; wp->w_pos_changed = true;
@ -4756,7 +4754,6 @@ 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->w_float_config.width = win->w_width;
win->w_float_config.height = height; win->w_float_config.height = height;
win_config_float(win, win->w_float_config); win_config_float(win, win->w_float_config);
} else { } else {
@ -4964,7 +4961,6 @@ 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) {
wp->w_float_config.width = width; wp->w_float_config.width = width;
wp->w_float_config.height = wp->w_height;
win_config_float(wp, wp->w_float_config); win_config_float(wp, wp->w_float_config);
} else { } else {
beep_flush(); beep_flush();

View File

@ -231,15 +231,15 @@ 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, {width=20,height=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, {width=20,height=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, {width=20,height=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, {width=20,height=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, {width=20,height=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, {width=20,height=2,relative='editor'})) meth_pcall(meths.open_win,buf, false, {width=20,height=2,relative='editor'}))