aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api')
-rw-r--r--src/nvim/api/buffer.c79
-rw-r--r--src/nvim/api/private/helpers.c352
-rw-r--r--src/nvim/api/vim.c120
3 files changed, 525 insertions, 26 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 2c2e8a024f..66c4454f7b 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1430,6 +1430,18 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// - "eol": right after eol character (default)
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
+/// - virt_text_hide : hide the virtual text when the background
+/// text is selected or hidden due to
+/// horizontal scroll 'nowrap'
+/// - hl_mode : control how highlights are combined with the
+/// highlights of the text. Currently only affects
+/// virt_text highlights, but might affect `hl_group`
+/// in later versions.
+/// - "replace": only show the virt_text color. This is the
+/// default
+/// - "combine": combine with background text color
+/// - "blend": blend with background text color.
+///
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
@@ -1477,11 +1489,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
bool ephemeral = false;
uint64_t id = 0;
- int line2 = -1, hl_id = 0;
- DecorPriority priority = DECOR_PRIORITY_BASE;
+ int line2 = -1;
+ Decoration decor = DECORATION_INIT;
colnr_T col2 = -1;
- VirtText virt_text = KV_INITIAL_VALUE;
- VirtTextPos virt_text_pos = kVTEndOfLine;
+
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
@@ -1528,12 +1539,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
switch (v->type) {
case kObjectTypeString:
hl_group = v->data.string;
- hl_id = syn_check_group(
+ decor.hl_id = syn_check_group(
(char_u *)(hl_group.data),
(int)hl_group.size);
break;
case kObjectTypeInteger:
- hl_id = (int)v->data.integer;
+ decor.hl_id = (int)v->data.integer;
break;
default:
api_set_error(err, kErrorTypeValidation,
@@ -1546,7 +1557,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"virt_text is not an Array");
goto error;
}
- virt_text = parse_virt_text(v->data.array, err);
+ decor.virt_text = parse_virt_text(v->data.array, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -1558,9 +1569,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
String str = v->data.string;
if (strequal("eol", str.data)) {
- virt_text_pos = kVTEndOfLine;
+ decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
- virt_text_pos = kVTOverlay;
+ decor.virt_text_pos = kVTOverlay;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos: invalid value");
+ goto error;
+ }
+ } else if (strequal("virt_text_hide", k.data)) {
+ decor.virt_text_hide = api_object_to_bool(*v,
+ "virt_text_hide", false, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ } else if (strequal("hl_mode", k.data)) {
+ if (v->type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "hl_mode is not a String");
+ goto error;
+ }
+ String str = v->data.string;
+ if (strequal("replace", str.data)) {
+ decor.hl_mode = kHlModeReplace;
+ } else if (strequal("combine", str.data)) {
+ decor.hl_mode = kHlModeCombine;
+ } else if (strequal("blend", str.data)) {
+ decor.hl_mode = kHlModeBlend;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
@@ -1583,7 +1618,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
"priority is not a valid value");
goto error;
}
- priority = (DecorPriority)v->data.integer;
+ decor.priority = (DecorPriority)v->data.integer;
} else if (strequal("right_gravity", k.data)) {
if (v->type != kObjectTypeBoolean) {
api_set_error(err, kErrorTypeValidation,
@@ -1631,23 +1666,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
col2 = 0;
}
- Decoration *decor = NULL, tmp = { 0 };
+ Decoration *d = NULL;
- if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) {
+ if (ephemeral) {
+ d = &decor;
+ } else if (kv_size(decor.virt_text)
+ || decor.priority != DECOR_PRIORITY_BASE) {
// TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals
- decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor));
- decor->hl_id = hl_id;
- decor->virt_text = virt_text;
- decor->priority = priority;
- decor->virt_text_pos = virt_text_pos;
- } else if (hl_id) {
- decor = decor_hl(hl_id);
+ d = xcalloc(1, sizeof(*d));
+ *d = decor;
+ } else if (decor.hl_id) {
+ d = decor_hl(decor.hl_id);
}
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
- decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, 0);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
@@ -1655,14 +1690,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col,
- line2, col2, decor, right_gravity,
+ line2, col2, d, right_gravity,
end_right_gravity, kExtmarkNoUndo);
}
return (Integer)id;
error:
- clear_virttext(&virt_text);
+ clear_virttext(&decor.virt_text);
return 0;
}
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 7cee569989..d2b787a6f5 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1645,6 +1645,20 @@ bool api_object_to_bool(Object obj, const char *what,
}
}
+int object_to_hl_id(Object obj, const char *what, Error *err)
+{
+ if (obj.type == kObjectTypeString) {
+ String str = obj.data.string;
+ return str.size ? syn_check_group((char_u *)str.data, (int)str.size) : 0;
+ } else if (obj.type == kObjectTypeInteger) {
+ return (int)obj.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "%s is not a valid highlight", what);
+ return 0;
+ }
+}
+
HlMessage parse_hl_msg(Array chunks, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
@@ -1720,3 +1734,341 @@ DecorProvider *get_provider(NS ns_id, bool force)
return item;
}
+
+static bool parse_float_anchor(String anchor, FloatAnchor *out)
+{
+ if (anchor.size == 0) {
+ *out = (FloatAnchor)0;
+ }
+ char *str = anchor.data;
+ if (striequal(str, "NW")) {
+ *out = 0; // NW is the default
+ } else if (striequal(str, "NE")) {
+ *out = kFloatAnchorEast;
+ } else if (striequal(str, "SW")) {
+ *out = kFloatAnchorSouth;
+ } else if (striequal(str, "SE")) {
+ *out = kFloatAnchorSouth | kFloatAnchorEast;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_relative(String relative, FloatRelative *out)
+{
+ char *str = relative.data;
+ if (striequal(str, "editor")) {
+ *out = kFloatRelativeEditor;
+ } else if (striequal(str, "win")) {
+ *out = kFloatRelativeWindow;
+ } else if (striequal(str, "cursor")) {
+ *out = kFloatRelativeCursor;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_bufpos(Array bufpos, lpos_T *out)
+{
+ if (bufpos.size != 2
+ || bufpos.items[0].type != kObjectTypeInteger
+ || bufpos.items[1].type != kObjectTypeInteger) {
+ return false;
+ }
+ out->lnum = bufpos.items[0].data.integer;
+ out->col = (colnr_T)bufpos.items[1].data.integer;
+ return true;
+}
+
+static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+{
+ struct {
+ const char *name;
+ schar_T chars[8];
+ } defaults[] = {
+ { "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" } },
+ { "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" } },
+ { NULL, { { NUL } } },
+ };
+
+ schar_T *chars = fconfig->border_chars;
+ int *hl_ids = fconfig->border_hl_ids;
+
+ fconfig->border = true;
+
+ if (style.type == kObjectTypeArray) {
+ 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");
+ return;
+ }
+ for (size_t i = 0; i < size; i++) {
+ Object iytem = arr.items[i];
+ String string = NULL_STRING;
+ int hl_id = 0;
+ if (iytem.type == kObjectTypeArray) {
+ Array iarr = iytem.data.array;
+ if (!iarr.size || iarr.size > 2) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (iarr.items[0].type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ string = iarr.items[0].data.string;
+ if (iarr.size == 2) {
+ hl_id = object_to_hl_id(iarr.items[1], "border char highlight", err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+ }
+
+ } else if (iytem.type == kObjectTypeString) {
+ string = iytem.data.string;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (!string.size
+ || mb_string2cells_len((char_u *)string.data, string.size) != 1) {
+ api_set_error(err, kErrorTypeValidation,
+ "border chars must be one cell");
+ }
+ size_t len = MIN(string.size, sizeof(*chars)-1);
+ memcpy(chars[i], string.data, len);
+ chars[i][len] = NUL;
+ hl_ids[i] = hl_id;
+ }
+ while (size < 8) {
+ memcpy(chars+size, chars, sizeof(*chars) * size);
+ memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size);
+ size <<= 1;
+ }
+ } else if (style.type == kObjectTypeString) {
+ String str = style.data.string;
+ if (str.size == 0 || strequal(str.data, "none")) {
+ fconfig->border = false;
+ return;
+ }
+ for (size_t i = 0; defaults[i].name; i++) {
+ if (strequal(str.data, defaults[i].name)) {
+ memcpy(chars, defaults[i].chars, sizeof(defaults[i].chars));
+ memset(hl_ids, 0, 8 * sizeof(*hl_ids));
+ return;
+ }
+ }
+ api_set_error(err, kErrorTypeValidation,
+ "invalid border style \"%s\"", str.data);
+ }
+}
+
+bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
+ Error *err)
+{
+ // TODO(bfredl): use a get/has_key interface instead and get rid of extra
+ // flags
+ bool has_row = false, has_col = false, has_relative = false;
+ bool has_external = false, has_window = false;
+ bool has_width = false, has_height = false;
+ bool has_bufpos = false;
+
+ for (size_t i = 0; i < config.size; i++) {
+ char *key = config.items[i].key.data;
+ Object val = config.items[i].value;
+ if (!strcmp(key, "row")) {
+ has_row = true;
+ if (val.type == kObjectTypeInteger) {
+ fconfig->row = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->row = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'row' key must be Integer or Float");
+ return false;
+ }
+ } else if (!strcmp(key, "col")) {
+ has_col = true;
+ if (val.type == kObjectTypeInteger) {
+ fconfig->col = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->col = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ } else if (strequal(key, "width")) {
+ has_width = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->width = (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'width' key must be a positive Integer");
+ return false;
+ }
+ } else if (strequal(key, "height")) {
+ has_height = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->height= (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'height' key must be a positive Integer");
+ return false;
+ }
+ } else if (!strcmp(key, "anchor")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'anchor' key must be String");
+ return false;
+ }
+ if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'anchor' key");
+ return false;
+ }
+ } else if (!strcmp(key, "relative")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'relative' key must be String");
+ return false;
+ }
+ // ignore empty string, to match nvim_win_get_config
+ if (val.data.string.size > 0) {
+ has_relative = true;
+ if (!parse_float_relative(val.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'relative' key");
+ return false;
+ }
+ }
+ } else if (!strcmp(key, "win")) {
+ has_window = true;
+ if (val.type != kObjectTypeInteger
+ && val.type != kObjectTypeWindow) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key must be Integer or Window");
+ return false;
+ }
+ fconfig->window = (Window)val.data.integer;
+ } else if (!strcmp(key, "bufpos")) {
+ if (val.type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation,
+ "'bufpos' key must be Array");
+ return false;
+ }
+ if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'bufpos' key");
+ return false;
+ }
+ has_bufpos = true;
+ } else if (!strcmp(key, "external")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->external = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->external = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'external' key must be Boolean");
+ return false;
+ }
+ has_external = fconfig->external;
+ } else if (!strcmp(key, "focusable")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->focusable = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->focusable = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'focusable' key must be Boolean");
+ return false;
+ }
+ } else if (!strcmp(key, "border")) {
+ parse_border_style(val, fconfig, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ } else if (!strcmp(key, "style")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'style' key must be String");
+ return false;
+ }
+ if (val.data.string.data[0] == NUL) {
+ fconfig->style = kWinStyleUnused;
+ } else if (striequal(val.data.string.data, "minimal")) {
+ fconfig->style = kWinStyleMinimal;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'style' key");
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid key '%s'", key);
+ return false;
+ }
+ }
+
+ if (has_window && !(has_relative
+ && fconfig->relative == kFloatRelativeWindow)) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key is only valid with relative='win'");
+ return false;
+ }
+
+ if ((has_relative && fconfig->relative == kFloatRelativeWindow)
+ && (!has_window || fconfig->window == 0)) {
+ fconfig->window = curwin->handle;
+ }
+
+ if (has_window && !has_bufpos) {
+ fconfig->bufpos.lnum = -1;
+ }
+
+ if (has_bufpos) {
+ if (!has_row) {
+ fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
+ has_row = true;
+ }
+ if (!has_col) {
+ fconfig->col = 0;
+ has_col = true;
+ }
+ }
+
+ if (has_relative && has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "Only one of 'relative' and 'external' must be used");
+ return false;
+ } else if (!reconf && !has_relative && !has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "One of 'relative' and 'external' must be used");
+ return false;
+ } else if (has_relative) {
+ fconfig->external = false;
+ }
+
+ if (!reconf && !(has_height && has_width)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'width' and 'height'");
+ return false;
+ }
+
+ if (fconfig->external && !ui_has(kUIMultigrid)) {
+ api_set_error(err, kErrorTypeValidation,
+ "UI doesn't support external windows");
+ return false;
+ }
+
+ if (has_relative != has_row || has_row != has_col) {
+ api_set_error(err, kErrorTypeValidation,
+ "'relative' requires 'row'/'col' or 'bufpos'");
+ return false;
+ }
+ return true;
+}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 586123aac1..9dde62f0ee 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -39,6 +39,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/fileio.h"
+#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/state.h"
@@ -241,8 +242,7 @@ void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err)
///
/// @param ns_id the namespace to activate
/// @param[out] err Error details, if any
-void nvim_set_hl_ns(Integer ns_id, Error *err)
- FUNC_API_SINCE(7)
+void nvim__set_hl_ns(Integer ns_id, Error *err)
FUNC_API_FAST
{
if (ns_id >= 0) {
@@ -1246,6 +1246,99 @@ fail:
return 0;
}
+/// Open a terminal instance in a buffer
+///
+/// By default (and currently the only option) the terminal will not be
+/// connected to an external process. Instead, input send on the channel
+/// will be echoed directly by the terminal. This is useful to disply
+/// ANSI terminal sequences returned as part of a rpc message, or similar.
+///
+/// Note: to directly initiate the terminal using the right size, display the
+/// buffer in a configured window before calling this. For instance, for a
+/// floating display, first create an empty buffer using |nvim_create_buf()|,
+/// then display it using |nvim_open_win()|, and then call this function.
+/// Then |nvim_chan_send()| cal be called immediately to process sequences
+/// in a virtual terminal having the intended size.
+///
+/// @param buffer the buffer to use (expected to be empty)
+/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any
+Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
+ FUNC_API_SINCE(7)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return 0;
+ }
+
+ TerminalOptions topts;
+ Channel *chan = channel_alloc(kChannelStreamInternal);
+ topts.data = chan;
+ // NB: overriden in terminal_check_size if a window is already
+ // displaying the buffer
+ topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0);
+ topts.height = (uint16_t)curwin->w_height_inner;
+ topts.write_cb = term_write;
+ topts.resize_cb = term_resize;
+ topts.close_cb = term_close;
+ Terminal *term = terminal_open(buf, topts);
+ terminal_check_size(term);
+ chan->term = term;
+ channel_incref(chan);
+ return (Integer)chan->id;
+}
+
+static void term_write(char *buf, size_t size, void *data)
+{
+ // TODO(bfredl): lua callback
+}
+
+static void term_resize(uint16_t width, uint16_t height, void *data)
+{
+ // TODO(bfredl): lua callback
+}
+
+static void term_close(void *data)
+{
+ Channel *chan = data;
+ terminal_destroy(chan->term);
+ chan->term = NULL;
+ channel_decref(chan);
+}
+
+
+/// Send data to channel `id`. For a job, it writes it to the
+/// stdin of the process. For the stdio channel |channel-stdio|,
+/// it writes to Nvim's stdout. For an internal terminal instance
+/// (|nvim_open_term()|) it writes directly to terimal output.
+/// See |channel-bytes| for more information.
+///
+/// This function writes raw data, not RPC messages. If the channel
+/// was created with `rpc=true` then the channel expects RPC
+/// messages, use |vim.rpcnotify()| and |vim.rpcrequest()| instead.
+///
+/// @param chan id of the channel
+/// @param data data to write. 8-bit clean: can contain NUL bytes.
+/// @param[out] err Error details, if any
+void nvim_chan_send(Integer chan, String data, Error *err)
+ FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY FUNC_API_LUA_ONLY
+{
+ const char *error = NULL;
+ if (!data.size) {
+ return;
+ }
+
+ channel_send((uint64_t)chan, data.data, data.size, &error);
+ if (error) {
+ api_set_error(err, kErrorTypeValidation, "%s", error);
+ }
+}
+
/// Open a new window.
///
/// Currently this is used to open floating and external windows.
@@ -1323,6 +1416,25 @@ fail:
/// end-of-buffer region is hidden by setting `eob` flag of
/// 'fillchars' to a space char, and clearing the
/// |EndOfBuffer| region in 'winhighlight'.
+/// - `border`: style of (optional) window border. This can either be a string
+/// or an array. the string values are:
+/// - "none" No border. This is the default
+/// - "single" a single line box
+/// - "double" a double line box
+/// If it is an array it should be an array of eight items or any divisor of
+/// eight. The array will specifify the eight chars building up the border
+/// in a clockwise fashion starting with the top-left corner. As, an
+/// example, the double box style could be specified as:
+/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]
+/// if the number of chars are less than eight, they will be repeated. Thus
+/// an ASCII border could be specified as:
+/// [ "/", "-", "\\", "|" ]
+/// or all chars the same as:
+/// [ "x" ]
+/// By default `FloatBorder` highlight is used which links to `VertSplit`
+/// when not defined. It could also be specified by character:
+/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]
+///
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
@@ -2738,8 +2850,8 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
g = &pum_grid;
} else if (grid > 1) {
win_T *wp = get_win_by_grid_handle((handle_T)grid);
- if (wp != NULL && wp->w_grid.chars != NULL) {
- g = &wp->w_grid;
+ if (wp != NULL && wp->w_grid_alloc.chars != NULL) {
+ g = &wp->w_grid_alloc;
} else {
api_set_error(err, kErrorTypeValidation,
"No grid with the given handle");