diff options
Diffstat (limited to 'src/nvim/api')
| -rw-r--r-- | src/nvim/api/buffer.c | 594 | ||||
| -rw-r--r-- | src/nvim/api/private/helpers.c | 102 | ||||
| -rw-r--r-- | src/nvim/api/private/helpers.h | 4 | ||||
| -rw-r--r-- | src/nvim/api/ui.c | 2 | ||||
| -rw-r--r-- | src/nvim/api/ui_events.in.h | 10 | ||||
| -rw-r--r-- | src/nvim/api/vim.c | 225 | ||||
| -rw-r--r-- | src/nvim/api/window.c | 4 |
7 files changed, 644 insertions, 297 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 8e61976c4b..4fc0ee4fdf 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -7,6 +7,7 @@ #include <stdint.h> #include <stdlib.h> #include <limits.h> + #include <lauxlib.h> #include "nvim/api/buffer.h" @@ -27,6 +28,7 @@ #include "nvim/map.h" #include "nvim/mark.h" #include "nvim/extmark.h" +#include "nvim/decoration.h" #include "nvim/fileio.h" #include "nvim/move.h" #include "nvim/syntax.h" @@ -175,8 +177,7 @@ Boolean nvim_buf_attach(uint64_t channel_id, } cb.on_lines = v->data.luaref; v->data.luaref = LUA_NOREF; - } else if (is_lua && strequal("_on_bytes", k.data)) { - // NB: undocumented, untested and incomplete interface! + } else if (is_lua && strequal("on_bytes", k.data)) { if (v->type != kObjectTypeLuaRef) { api_set_error(err, kErrorTypeValidation, "callback is not a function"); goto error; @@ -213,10 +214,10 @@ Boolean nvim_buf_attach(uint64_t channel_id, error: // TODO(bfredl): ASAN build should check that the ref table is empty? - executor_free_luaref(cb.on_lines); - executor_free_luaref(cb.on_bytes); - executor_free_luaref(cb.on_changedtick); - executor_free_luaref(cb.on_detach); + api_free_luaref(cb.on_lines); + api_free_luaref(cb.on_bytes); + api_free_luaref(cb.on_changedtick); + api_free_luaref(cb.on_detach); return false; } @@ -245,78 +246,6 @@ Boolean nvim_buf_detach(uint64_t channel_id, return true; } -static void buf_clear_luahl(buf_T *buf, bool force) -{ - if (buf->b_luahl || force) { - executor_free_luaref(buf->b_luahl_start); - executor_free_luaref(buf->b_luahl_window); - executor_free_luaref(buf->b_luahl_line); - executor_free_luaref(buf->b_luahl_end); - } - buf->b_luahl_start = LUA_NOREF; - buf->b_luahl_window = LUA_NOREF; - buf->b_luahl_line = LUA_NOREF; - buf->b_luahl_end = LUA_NOREF; -} - -/// Unstabilized interface for defining syntax hl in lua. -/// -/// This is not yet safe for general use, lua callbacks will need to -/// be restricted, like textlock and probably other stuff. -/// -/// The API on_line/nvim__put_attr is quite raw and not intended to be the -/// final shape. Ideally this should operate on chunks larger than a single -/// line to reduce interpreter overhead, and generate annotation objects -/// (bufhl/virttext) on the fly but using the same representation. -void nvim__buf_set_luahl(uint64_t channel_id, Buffer buffer, - DictionaryOf(LuaRef) opts, Error *err) - FUNC_API_LUA_ONLY -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!buf) { - return; - } - - redraw_buf_later(buf, NOT_VALID); - buf_clear_luahl(buf, false); - - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("on_start", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - buf->b_luahl_start = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (strequal("on_window", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - buf->b_luahl_window = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (strequal("on_line", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - buf->b_luahl_line = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - goto error; - } - } - buf->b_luahl = true; - return; -error: - buf_clear_luahl(buf, true); - buf->b_luahl = false; -} - void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err) FUNC_API_LUA_ONLY @@ -1025,6 +954,53 @@ Boolean nvim_buf_is_loaded(Buffer buffer) return buf && buf->b_ml.ml_mfp != NULL; } +/// Deletes the buffer. See |:bwipeout| +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param opts Optional parameters. Keys: +/// - force: Force deletion and ignore unsaved changes. +/// - unload: Unloaded only, do not delete. See |:bunload| +void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err) + FUNC_API_SINCE(7) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (ERROR_SET(err)) { + return; + } + + bool force = false; + bool unload = false; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object v = opts.items[i].value; + if (strequal("force", k.data)) { + force = api_object_to_bool(v, "force", false, err); + } else if (strequal("unload", k.data)) { + unload = api_object_to_bool(v, "unload", false, err); + } else { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + return; + } + } + + if (ERROR_SET(err)) { + return; + } + + int result = do_buffer( + unload ? DOBUF_UNLOAD : DOBUF_WIPE, + DOBUF_FIRST, + FORWARD, + buf->handle, + force); + + if (result == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to unload buffer."); + return; + } +} + /// Checks if a buffer is valid. /// /// @note Even if a buffer is valid it may have been unloaded. See |api-buffer| @@ -1108,15 +1084,67 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) return rv; } +static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) +{ + Array rv = ARRAY_DICT_INIT; + if (id) { + ADD(rv, INTEGER_OBJ((Integer)extmark.mark_id)); + } + ADD(rv, INTEGER_OBJ(extmark.row)); + ADD(rv, INTEGER_OBJ(extmark.col)); + + if (add_dict) { + Dictionary dict = ARRAY_DICT_INIT; + + if (extmark.end_row >= 0) { + PUT(dict, "end_row", INTEGER_OBJ(extmark.end_row)); + PUT(dict, "end_col", INTEGER_OBJ(extmark.end_col)); + } + + if (extmark.decor) { + Decoration *decor = extmark.decor; + if (decor->hl_id) { + String name = cstr_to_string((const char *)syn_id2name(decor->hl_id)); + PUT(dict, "hl_group", STRING_OBJ(name)); + } + if (kv_size(decor->virt_text)) { + Array chunks = ARRAY_DICT_INIT; + for (size_t i = 0; i < decor->virt_text.size; i++) { + Array chunk = ARRAY_DICT_INIT; + VirtTextChunk *vtc = &decor->virt_text.items[i]; + ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); + if (vtc->hl_id > 0) { + ADD(chunk, + STRING_OBJ(cstr_to_string( + (const char *)syn_id2name(vtc->hl_id)))); + } + ADD(chunks, ARRAY_OBJ(chunk)); + } + PUT(dict, "virt_text", ARRAY_OBJ(chunks)); + } + } + + if (dict.size) { + ADD(rv, DICTIONARY_OBJ(dict)); + } + } + + return rv; +} + /// Returns position for a given extmark id /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param id Extmark id +/// @param opts Optional parameters. Keys: +/// - limit: Maximum number of marks to return +/// - details Whether to include the details dict /// @param[out] err Error details, if any /// @return (row, col) tuple or empty list () if extmark id was absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, - Integer id, Error *err) + Integer id, Dictionary opts, + Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -1132,13 +1160,31 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, return rv; } + bool details = false; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object *v = &opts.items[i].value; + if (strequal("details", k.data)) { + if (v->type == kObjectTypeBoolean) { + details = v->data.boolean; + } else if (v->type == kObjectTypeInteger) { + details = v->data.integer; + } else { + api_set_error(err, kErrorTypeValidation, "details is not an boolean"); + return rv; + } + } else { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + return rv; + } + } + + ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id); if (extmark.row < 0) { return rv; } - ADD(rv, INTEGER_OBJ((Integer)extmark.row)); - ADD(rv, INTEGER_OBJ((Integer)extmark.col)); - return rv; + return extmark_to_array(extmark, false, (bool)details); } /// Gets extmarks in "traversal order" from a |charwise| region defined by @@ -1181,10 +1227,12 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// (whose position defines the bound) /// @param opts Optional parameters. Keys: /// - limit: Maximum number of marks to return +/// - details Whether to include the details dict /// @param[out] err Error details, if any /// @return List of [extmark_id, row, col] tuples in "traversal order". -Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, - Object end, Dictionary opts, Error *err) +Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, + Object start, Object end, + Dictionary opts, Error *err) FUNC_API_SINCE(7) { Array rv = ARRAY_DICT_INIT; @@ -1198,7 +1246,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); return rv; } + Integer limit = -1; + bool details = false; for (size_t i = 0; i < opts.size; i++) { String k = opts.items[i].key; @@ -1209,6 +1259,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, return rv; } limit = v->data.integer; + } else if (strequal("details", k.data)) { + if (v->type == kObjectTypeBoolean) { + details = v->data.boolean; + } else if (v->type == kObjectTypeInteger) { + details = v->data.integer; + } else { + api_set_error(err, kErrorTypeValidation, "details is not an boolean"); + return rv; + } } else { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); return rv; @@ -1241,16 +1300,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, } - ExtmarkArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col, u_row, - u_col, (int64_t)limit, reverse); + ExtmarkInfoArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col, + u_row, u_col, (int64_t)limit, reverse); for (size_t i = 0; i < kv_size(marks); i++) { - Array mark = ARRAY_DICT_INIT; - ExtmarkInfo extmark = kv_A(marks, i); - ADD(mark, INTEGER_OBJ((Integer)extmark.mark_id)); - ADD(mark, INTEGER_OBJ(extmark.row)); - ADD(mark, INTEGER_OBJ(extmark.col)); - ADD(rv, ARRAY_OBJ(mark)); + ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, (bool)details))); } kv_destroy(marks); @@ -1260,27 +1314,40 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, /// Creates or updates an extmark. /// /// To create a new extmark, pass id=0. The extmark id will be returned. -// To move an existing mark, pass its id. +/// To move an existing mark, pass its id. /// /// It is also allowed to create a new mark by passing in a previously unused /// id, but the caller must then keep track of existing and unused ids itself. /// (Useful over RPC, to avoid waiting for the return value.) /// +/// Using the optional arguments, it is possible to use this to highlight +/// a range of text, and also to associate virtual text to the mark. +/// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace id from |nvim_create_namespace()| -/// @param id Extmark id, or 0 to create new /// @param line Line number where to place the mark /// @param col Column where to place the mark -/// @param opts Optional parameters. Currently not used. +/// @param opts Optional parameters. +/// - id : id of the extmark to edit. +/// - end_line : ending line of the mark, 0-based inclusive. +/// - end_col : ending col of the mark, 0-based inclusive. +/// - hl_group : name of the highlight group used to highlight +/// this mark. +/// - virt_text : virtual text to link to this mark. +/// - 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 +/// buffer. /// @param[out] err Error details, if any /// @return Id of the created/updated extmark -Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id, +Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col, Dictionary opts, Error *err) FUNC_API_SINCE(7) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { + api_set_error(err, kErrorTypeValidation, "Invalid buffer id"); return 0; } @@ -1289,11 +1356,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id, return 0; } - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - return 0; - } - size_t len = 0; if (line < 0 || line > buf->b_ml.ml_line_count) { api_set_error(err, kErrorTypeValidation, "line value outside range"); @@ -1309,18 +1371,138 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer id, return 0; } - uint64_t id_num; - if (id >= 0) { - id_num = (uint64_t)id; + bool ephemeral = false; + + uint64_t id = 0; + int line2 = -1, hl_id = 0; + colnr_T col2 = 0; + VirtText virt_text = KV_INITIAL_VALUE; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object *v = &opts.items[i].value; + if (strequal("id", k.data)) { + if (v->type != kObjectTypeInteger || v->data.integer <= 0) { + api_set_error(err, kErrorTypeValidation, + "id is not a positive integer"); + goto error; + } + + id = (uint64_t)v->data.integer; + } else if (strequal("end_line", k.data)) { + if (v->type != kObjectTypeInteger) { + api_set_error(err, kErrorTypeValidation, + "end_line is not an integer"); + goto error; + } + if (v->data.integer < 0 || v->data.integer > buf->b_ml.ml_line_count) { + api_set_error(err, kErrorTypeValidation, + "end_line value outside range"); + goto error; + } + + line2 = (int)v->data.integer; + } else if (strequal("end_col", k.data)) { + if (v->type != kObjectTypeInteger) { + api_set_error(err, kErrorTypeValidation, + "end_col is not an integer"); + goto error; + } + if (v->data.integer < 0 || v->data.integer > MAXCOL) { + api_set_error(err, kErrorTypeValidation, + "end_col value outside range"); + goto error; + } + + col2 = (colnr_T)v->data.integer; + } else if (strequal("hl_group", k.data)) { + String hl_group; + switch (v->type) { + case kObjectTypeString: + hl_group = v->data.string; + hl_id = syn_check_group( + (char_u *)(hl_group.data), + (int)hl_group.size); + break; + case kObjectTypeInteger: + hl_id = (int)v->data.integer; + break; + default: + api_set_error(err, kErrorTypeValidation, + "hl_group is not valid."); + goto error; + } + } else if (strequal("virt_text", k.data)) { + if (v->type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, + "virt_text is not an Array"); + goto error; + } + virt_text = parse_virt_text(v->data.array, err); + if (ERROR_SET(err)) { + goto error; + } + } else if (strequal("ephemeral", k.data)) { + ephemeral = api_object_to_bool(*v, "ephemeral", false, err); + if (ERROR_SET(err)) { + goto error; + } + } else { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + goto error; + } + } + + if (col2 >= 0) { + if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { + len = STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false)); + } else if (line2 == buf->b_ml.ml_line_count) { + // We are trying to add an extmark past final newline + len = 0; + } else { + // reuse len from before + line2 = (int)line; + } + if (col2 > (Integer)len) { + api_set_error(err, kErrorTypeValidation, "end_col value outside range"); + goto error; + } + } else if (line2 >= 0) { + col2 = 0; + } + + // TODO(bfredl): synergize these two branches even more + if (ephemeral && decor_state.buf == buf) { + int attr_id = hl_id > 0 ? syn_id2attr(hl_id) : 0; + VirtText *vt_allocated = NULL; + if (kv_size(virt_text)) { + vt_allocated = xmalloc(sizeof *vt_allocated); + *vt_allocated = virt_text; + } + decor_add_ephemeral(attr_id, (int)line, (colnr_T)col, + (int)line2, (colnr_T)col2, vt_allocated); } else { - api_set_error(err, kErrorTypeValidation, "Invalid mark id"); - return 0; + if (ephemeral) { + api_set_error(err, kErrorTypeException, "not yet implemented"); + goto error; + } + Decoration *decor = NULL; + if (kv_size(virt_text)) { + decor = xcalloc(1, sizeof(*decor)); + decor->hl_id = hl_id; + decor->virt_text = virt_text; + } else if (hl_id) { + decor = decor_hl(hl_id); + } + + id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, + line2, col2, decor, kExtmarkNoUndo); } - id_num = extmark_set(buf, (uint64_t)ns_id, id_num, - (int)line, (colnr_T)col, kExtmarkUndo); + return (Integer)id; - return (Integer)id_num; +error: + clear_virttext(&virt_text); + return 0; } /// Removes an extmark. @@ -1358,17 +1540,17 @@ Boolean nvim_buf_del_extmark(Buffer buffer, /// like signs and marks do. /// /// Namespaces are used for batch deletion/updating of a set of highlights. To -/// create a namespace, use |nvim_create_namespace| which returns a namespace +/// create a namespace, use |nvim_create_namespace()| which returns a namespace /// id. Pass it in to this function as `ns_id` to add highlights to the /// namespace. All highlights in the same namespace can then be cleared with -/// single call to |nvim_buf_clear_namespace|. If the highlight never will be +/// single call to |nvim_buf_clear_namespace()|. If the highlight never will be /// deleted by an API call, pass `ns_id = -1`. /// /// As a shorthand, `ns_id = 0` can be used to create a new namespace for the /// highlight, the allocated id is then returned. If `hl_group` is the empty /// string no highlight is added, but a new `ns_id` is still returned. This is /// supported for backwards compatibility, new code should use -/// |nvim_create_namespace| to create a new empty namespace. +/// |nvim_create_namespace()| to create a new empty namespace. /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id namespace to use or -1 for ungrouped highlight @@ -1412,9 +1594,9 @@ Integer nvim_buf_add_highlight(Buffer buffer, return src_id; } - int hlg_id = 0; + int hl_id = 0; if (hl_group.size > 0) { - hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size); + hl_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size); } else { return src_id; } @@ -1425,10 +1607,10 @@ Integer nvim_buf_add_highlight(Buffer buffer, end_line++; } - extmark_add_decoration(buf, ns_id, hlg_id, - (int)line, (colnr_T)col_start, - end_line, (colnr_T)col_end, - VIRTTEXT_EMPTY); + extmark_set(buf, ns_id, 0, + (int)line, (colnr_T)col_start, + end_line, (colnr_T)col_end, + decor_hl(hl_id), kExtmarkNoUndo); return src_id; } @@ -1470,7 +1652,7 @@ void nvim_buf_clear_namespace(Buffer buffer, /// Clears highlights and virtual text from namespace and range of lines /// -/// @deprecated use |nvim_buf_clear_namespace|. +/// @deprecated use |nvim_buf_clear_namespace()|. /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace to clear, or -1 to clear all. @@ -1488,43 +1670,6 @@ void nvim_buf_clear_highlight(Buffer buffer, nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); } -static VirtText parse_virt_text(Array chunks, Error *err) -{ - VirtText virt_text = KV_INITIAL_VALUE; - for (size_t i = 0; i < chunks.size; i++) { - if (chunks.items[i].type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); - goto free_exit; - } - Array chunk = chunks.items[i].data.array; - if (chunk.size == 0 || chunk.size > 2 - || chunk.items[0].type != kObjectTypeString - || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { - api_set_error(err, kErrorTypeValidation, - "Chunk is not an array with one or two strings"); - goto free_exit; - } - - String str = chunk.items[0].data.string; - char *text = transstr(str.size > 0 ? str.data : ""); // allocates - - int hl_id = 0; - if (chunk.size == 2) { - String hl = chunk.items[1].data.string; - if (hl.size > 0) { - hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); - } - } - kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); - } - - return virt_text; - -free_exit: - clear_virttext(&virt_text); - return virt_text; -} - /// Set the virtual text (annotation) for a buffer line. /// @@ -1534,11 +1679,11 @@ free_exit: /// begin one cell (|lcs-eol| or space) after the ordinary text. /// /// Namespaces are used to support batch deletion/updating of virtual text. -/// To create a namespace, use |nvim_create_namespace|. Virtual text is -/// cleared using |nvim_buf_clear_namespace|. The same `ns_id` can be used for -/// both virtual text and highlights added by |nvim_buf_add_highlight|, both -/// can then be cleared with a single call to |nvim_buf_clear_namespace|. If the -/// virtual text never will be cleared by an API call, pass `ns_id = -1`. +/// To create a namespace, use |nvim_create_namespace()|. Virtual text is +/// cleared using |nvim_buf_clear_namespace()|. The same `ns_id` can be used for +/// both virtual text and highlights added by |nvim_buf_add_highlight()|, both +/// can then be cleared with a single call to |nvim_buf_clear_namespace()|. If +/// the virtual text never will be cleared by an API call, pass `ns_id = -1`. /// /// As a shorthand, `ns_id = 0` can be used to create a new namespace for the /// virtual text, the allocated id is then returned. @@ -1584,7 +1729,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, } - VirtText *existing = extmark_find_virttext(buf, (int)line, ns_id); + VirtText *existing = decor_find_virttext(buf, (int)line, ns_id); if (existing) { clear_virttext(existing); @@ -1592,113 +1737,49 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, return src_id; } - extmark_add_decoration(buf, ns_id, 0, - (int)line, 0, -1, -1, - virt_text); + Decoration *decor = xcalloc(1, sizeof(*decor)); + decor->virt_text = virt_text; + + extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, kExtmarkNoUndo); return src_id; } -/// Get the virtual text (annotation) for a buffer line. -/// -/// The virtual text is returned as list of lists, whereas the inner lists have -/// either one or two elements. The first element is the actual text, the -/// optional second element is the highlight group. +/// call a function with buffer as temporary current buffer /// -/// The format is exactly the same as given to nvim_buf_set_virtual_text(). +/// This temporarily switches current buffer to "buffer". +/// If the current window already shows "buffer", the window is not switched +/// If a window inside the current tabpage (including a float) already shows the +/// buffer One of these windows will be set as current window temporarily. +/// Otherwise a temporary scratch window (calleed the "autocmd window" for +/// historical reasons) will be used. /// -/// If there is no virtual text associated with the given line, an empty list -/// is returned. +/// This is useful e.g. to call vimL functions that only work with the current +/// buffer/window currently, like |termopen()|. /// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param line Line to get the virtual text from (zero-indexed) -/// @param[out] err Error details, if any -/// @return List of virtual text chunks -Array nvim_buf_get_virtual_text(Buffer buffer, Integer line, Error *err) +/// @param buffer Buffer handle, or 0 for current buffer +/// @param fun Function to call inside the buffer (currently lua callable +/// only) +/// @param[out] err Error details, if any +/// @return Return value of function. NB: will deepcopy lua values +/// currently, use upvalues to send lua references in and out. +Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) FUNC_API_SINCE(7) -{ - Array chunks = ARRAY_DICT_INIT; - - buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf) { - return chunks; - } - - if (line < 0 || line >= MAXLNUM) { - api_set_error(err, kErrorTypeValidation, "Line number outside range"); - return chunks; - } - - VirtText *virt_text = extmark_find_virttext(buf, (int)line, 0); - - if (!virt_text) { - return chunks; - } - - for (size_t i = 0; i < virt_text->size; i++) { - Array chunk = ARRAY_DICT_INIT; - VirtTextChunk *vtc = &virt_text->items[i]; - ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); - if (vtc->hl_id > 0) { - ADD(chunk, STRING_OBJ(cstr_to_string( - (const char *)syn_id2name(vtc->hl_id)))); - } - ADD(chunks, ARRAY_OBJ(chunk)); - } - - return chunks; -} - -Integer nvim__buf_add_decoration(Buffer buffer, Integer ns_id, String hl_group, - Integer start_row, Integer start_col, - Integer end_row, Integer end_col, - Array virt_text, - Error *err) + FUNC_API_LUA_ONLY { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - return 0; - } - - if (!ns_initialized((uint64_t)ns_id)) { - api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); - return 0; - } - - - if (start_row < 0 || start_row >= MAXLNUM || end_row > MAXCOL) { - api_set_error(err, kErrorTypeValidation, "Line number outside range"); - return 0; - } - - if (start_col < 0 || start_col > MAXCOL || end_col > MAXCOL) { - api_set_error(err, kErrorTypeValidation, "Column value outside range"); - return 0; - } - if (end_row < 0 || end_col < 0) { - end_row = -1; - end_col = -1; - } - - if (start_row >= buf->b_ml.ml_line_count - || end_row >= buf->b_ml.ml_line_count) { - // safety check, we can't add marks outside the range - return 0; - } - - int hlg_id = 0; - if (hl_group.size > 0) { - hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size); + return NIL; } + try_start(); + aco_save_T aco; + aucmd_prepbuf(&aco, (buf_T *)buf); - VirtText vt = parse_virt_text(virt_text, err); - if (ERROR_SET(err)) { - return 0; - } + Array args = ARRAY_DICT_INIT; + Object res = nlua_call_ref(fun, NULL, args, true, err); - uint64_t mark_id = extmark_add_decoration(buf, (uint64_t)ns_id, hlg_id, - (int)start_row, (colnr_T)start_col, - (int)end_row, (colnr_T)end_col, vt); - return (Integer)mark_id; + aucmd_restbuf(&aco); + try_end(err); + return res; } Dictionary nvim__buf_stats(Buffer buffer, Error *err) @@ -1721,6 +1802,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err) // NB: this should be zero at any time API functions are called, // this exists to debug issues PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes)); + PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2)); u_header_T *uhp = NULL; if (buf->b_u_curhead != NULL) { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f194b6b474..2c99d3426c 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -15,6 +15,8 @@ #include "nvim/lua/executor.h" #include "nvim/ascii.h" #include "nvim/assert.h" +#include "nvim/charset.h" +#include "nvim/syntax.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/window.h" @@ -25,6 +27,7 @@ #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/extmark.h" +#include "nvim/decoration.h" #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/version.h" @@ -1198,7 +1201,7 @@ void api_free_object(Object value) break; case kObjectTypeLuaRef: - executor_free_luaref(value.data.luaref); + api_free_luaref(value.data.luaref); break; default: @@ -1579,3 +1582,100 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int return false; } } + +VirtText parse_virt_text(Array chunks, Error *err) +{ + VirtText virt_text = KV_INITIAL_VALUE; + for (size_t i = 0; i < chunks.size; i++) { + if (chunks.items[i].type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); + goto free_exit; + } + Array chunk = chunks.items[i].data.array; + if (chunk.size == 0 || chunk.size > 2 + || chunk.items[0].type != kObjectTypeString + || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { + api_set_error(err, kErrorTypeValidation, + "Chunk is not an array with one or two strings"); + goto free_exit; + } + + String str = chunk.items[0].data.string; + char *text = transstr(str.size > 0 ? str.data : ""); // allocates + + int hl_id = 0; + if (chunk.size == 2) { + String hl = chunk.items[1].data.string; + if (hl.size > 0) { + hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); + } + } + kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); + } + + return virt_text; + +free_exit: + clear_virttext(&virt_text); + return virt_text; +} + +/// Force obj to bool. +/// If it fails, returns false and sets err +/// @param obj The object to coerce to a boolean +/// @param what The name of the object, used for error message +/// @param nil_value What to return if the type is nil. +/// @param err Set if there was an error in converting to a bool +bool api_object_to_bool(Object obj, const char *what, + bool nil_value, Error *err) +{ + if (obj.type == kObjectTypeBoolean) { + return obj.data.boolean; + } else if (obj.type == kObjectTypeInteger) { + return obj.data.integer; // C semantics: non-zero int is true + } else if (obj.type == kObjectTypeNil) { + return nil_value; // caller decides what NIL (missing retval in lua) means + } else { + api_set_error(err, kErrorTypeValidation, "%s is not an boolean", what); + return false; + } +} + +const char *describe_ns(NS ns_id) +{ + String name; + handle_T id; + map_foreach(namespace_ids, name, id, { + if ((NS)id == ns_id && name.size) { + return name.data; + } + }) + return "(UNKNOWN PLUGIN)"; +} + +DecorProvider *get_provider(NS ns_id, bool force) +{ + ssize_t i; + for (i = 0; i < (ssize_t)kv_size(decor_providers); i++) { + DecorProvider *item = &kv_A(decor_providers, i); + if (item->ns_id == ns_id) { + return item; + } else if (item->ns_id > ns_id) { + break; + } + } + + if (!force) { + return NULL; + } + + for (ssize_t j = (ssize_t)kv_size(decor_providers)-1; j >= i; j++) { + // allocates if needed: + (void)kv_a(decor_providers, (size_t)j+1); + kv_A(decor_providers, (size_t)j+1) = kv_A(decor_providers, j); + } + DecorProvider *item = &kv_a(decor_providers, (size_t)i); + *item = DECORATION_PROVIDER_INIT(ns_id); + + return item; +} diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index df3a263dcf..271fd5b485 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -7,6 +7,7 @@ #include "nvim/vim.h" #include "nvim/getchar.h" #include "nvim/memory.h" +#include "nvim/decoration.h" #include "nvim/ex_eval.h" #include "nvim/lib/kvec.h" @@ -52,7 +53,8 @@ .type = kObjectTypeLuaRef, \ .data.luaref = r }) -#define NIL ((Object) {.type = kObjectTypeNil}) +#define NIL ((Object)OBJECT_INIT) +#define NULL_STRING ((String)STRING_INIT) #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 717713b948..51f1af4eb5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -355,7 +355,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) /// Note that this method is not to be confused with |nvim_ui_pum_set_height()|, /// which sets the number of visible items in the popup menu, while this /// function sets the bounding box of the popup menu, including visual -/// decorations such as boarders and sliders. Floats need not use the same font +/// elements such as borders and sliders. Floats need not use the same font /// size, nor be anchored to exact grid corners, so one can set floating-point /// numbers to the popup menu geometry. /// diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index ab31db39e9..e934d5dc92 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -1,7 +1,7 @@ #ifndef NVIM_API_UI_EVENTS_IN_H #define NVIM_API_UI_EVENTS_IN_H -// This file is not compiled, just parsed for definitons +// This file is not compiled, just parsed for definitions #ifdef INCLUDE_GENERATED_DECLARATIONS # error "don't include this file, include nvim/ui.h" #endif @@ -36,13 +36,15 @@ void set_title(String title) FUNC_API_SINCE(3); void set_icon(String icon) FUNC_API_SINCE(3); +void screenshot(String path) + FUNC_API_SINCE(7) FUNC_API_REMOTE_IMPL; void option_set(String name, Object value) FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL; // Stop event is not exported as such, represented by EOF in the msgpack stream. void stop(void) FUNC_API_NOEXPORT; -// First revison of the grid protocol, used by default +// First revision of the grid protocol, used by default void update_fg(Integer fg) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void update_bg(Integer bg) @@ -66,7 +68,7 @@ void set_scroll_region(Integer top, Integer bot, Integer left, Integer right) void scroll(Integer count) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; -// Second revison of the grid protocol, used with ext_linegrid ui option +// Second revision of the grid protocol, used with ext_linegrid ui option void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL; @@ -89,7 +91,7 @@ void grid_scroll(Integer grid, Integer top, Integer bot, void grid_destroy(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; -// For perfomance and simplicity, we use the dense screen representation +// For performance and simplicity, we use the dense screen representation // in internal code, such as compositor and TUI. The remote_ui module will // translate this in to the public grid_line format. void raw_line(Integer grid, Integer row, Integer startcol, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d6f95c7a5f..cf822782d8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -41,7 +41,7 @@ #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/state.h" -#include "nvim/extmark.h" +#include "nvim/decoration.h" #include "nvim/syntax.h" #include "nvim/getchar.h" #include "nvim/os/input.h" @@ -199,14 +199,77 @@ Integer nvim_get_hl_id_by_name(String name) return syn_check_group((const char_u *)name.data, (int)name.size); } +Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) +{ + if (ns_id == 0) { + return get_global_hl_defs(); + } + abort(); +} + +/// Set a highlight group. +/// +/// @param ns_id number of namespace for this highlight +/// @param name highlight group name, like ErrorMsg +/// @param val highlight definiton map, like |nvim_get_hl_by_name|. +/// @param[out] err Error details, if any +/// +/// TODO: ns_id = 0, should modify :highlight namespace +/// TODO val should take update vs reset flag +void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) + FUNC_API_SINCE(7) +{ + int hl_id = syn_check_group( (char_u *)(name.data), (int)name.size); + int link_id = -1; + + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); + if (!ERROR_SET(err)) { + ns_hl_def((NS)ns_id, hl_id, attrs, link_id); + } +} + +/// Set active namespace for highlights. +/// +/// NB: this function can be called from async contexts, but the +/// semantics are not yet well-defined. To start with +/// |nvim_set_decoration_provider| on_win and on_line callbacks +/// are explicitly allowed to change the namespace during a redraw cycle. +/// +/// @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) + FUNC_API_FAST +{ + if (ns_id >= 0) { + ns_hl_active = (NS)ns_id; + } + + // TODO(bfredl): this is a little bit hackish. Eventually we want a standard + // event path for redraws caused by "fast" events. This could tie in with + // better throttling of async events causing redraws, such as non-batched + // nvim_buf_set_extmark calls from async contexts. + if (!updating_screen && !ns_hl_changed) { + multiqueue_put(main_loop.events, on_redraw_event, 0); + } + ns_hl_changed = true; +} + +static void on_redraw_event(void **argv) + FUNC_API_NOEXPORT +{ + redraw_all_later(NOT_VALID); +} + + /// Sends input-keys to Nvim, subject to various quirks controlled by `mode` /// flags. This is a blocking call, unlike |nvim_input()|. /// /// On execution error: does not fail, but updates v:errmsg. -// -// If you need to input sequences like <C-o> use nvim_replace_termcodes -// to replace the termcodes and then pass the resulting string to -// nvim_feedkeys. You'll also want to enable escape_csi. +/// +/// If you need to input sequences like <C-o> use |nvim_replace_termcodes| to +/// replace the termcodes and then pass the resulting string to nvim_feedkeys. +/// You'll also want to enable escape_csi. /// /// Example: /// <pre> @@ -475,7 +538,7 @@ Object nvim_execute_lua(String code, Array args, Error *err) FUNC_API_DEPRECATED_SINCE(7) FUNC_API_REMOTE_ONLY { - return executor_exec_lua_api(code, args, err); + return nlua_exec(code, args, err); } /// Execute Lua code. Parameters (if any) are available as `...` inside the @@ -494,7 +557,7 @@ Object nvim_exec_lua(String code, Array args, Error *err) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY { - return executor_exec_lua_api(code, args, err); + return nlua_exec(code, args, err); } /// Calls a VimL function. @@ -678,7 +741,11 @@ Integer nvim_strwidth(String text, Error *err) ArrayOf(String) nvim_list_runtime_paths(void) FUNC_API_SINCE(1) { + // TODO(bfredl): this should just work: + // return nvim_get_runtime_file(NULL_STRING, true); + Array rv = ARRAY_DICT_INIT; + char_u *rtp = p_rtp; if (*rtp == NUL) { @@ -725,22 +792,30 @@ ArrayOf(String) nvim_list_runtime_paths(void) /// @param name pattern of files to search for /// @param all whether to return all matches or only the first /// @return list of absolute paths to the found files -ArrayOf(String) nvim_get_runtime_file(String name, Boolean all) +ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) FUNC_API_SINCE(7) + FUNC_API_FAST { Array rv = ARRAY_DICT_INIT; - if (!name.data) { + + // TODO(bfredl): + if (name.size == 0) { + api_set_error(err, kErrorTypeValidation, "not yet implemented"); return rv; } + int flags = DIP_START | (all ? DIP_ALL : 0); - do_in_runtimepath((char_u *)name.data, flags, find_runtime_cb, &rv); + do_in_runtimepath((char_u *)name.data, + flags, find_runtime_cb, &rv); return rv; } static void find_runtime_cb(char_u *fname, void *cookie) { Array *rv = (Array *)cookie; - ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + if (fname != NULL) { + ADD(*rv, STRING_OBJ(cstr_to_string((char *)fname))); + } } String nvim__get_lib_dir(void) @@ -1477,7 +1552,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event) Integer nvim_get_color_by_name(String name) FUNC_API_SINCE(1) { - return name_to_color((char_u *)name.data); + return name_to_color(name.data); } /// Returns a map of color names and RGB values. @@ -2477,7 +2552,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); String s = cstr_to_string("return vim._os_proc_children(select(1, ...))"); - Object o = nvim_exec_lua(s, a, err); + Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); if (o.type == kObjectTypeArray) { @@ -2523,7 +2598,7 @@ Object nvim_get_proc(Integer pid, Error *err) Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); String s = cstr_to_string("return vim._os_proc_info(select(1, ...))"); - Object o = nvim_exec_lua(s, a, err); + Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); if (o.type == kObjectTypeArray && o.data.array.size == 0) { @@ -2604,26 +2679,112 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err) return ret; } -/// Set attrs in nvim__buf_set_lua_hl callbacks -/// -/// TODO(bfredl): This is rather pedestrian. The final -/// interface should probably be derived from a reformed -/// bufhl/virttext interface with full support for multi-line -/// ranges etc -void nvim__put_attr(Integer id, Integer start_row, Integer start_col, - Integer end_row, Integer end_col) - FUNC_API_LUA_ONLY +void nvim__screenshot(String path) + FUNC_API_FAST { - if (!lua_attr_active) { - return; - } - if (id == 0 || syn_get_final_id((int)id) == 0) { + ui_call_screenshot(path); +} + +static void clear_provider(DecorProvider *p) +{ + if (p == NULL) { return; } - int attr = syn_id2attr((int)id); - if (attr == 0) { - return; + NLUA_CLEAR_REF(p->redraw_start); + NLUA_CLEAR_REF(p->redraw_buf); + NLUA_CLEAR_REF(p->redraw_win); + NLUA_CLEAR_REF(p->redraw_line); + NLUA_CLEAR_REF(p->redraw_end); + p->active = false; +} + +/// Set or change decoration provider for a namespace +/// +/// This is a very general purpose interface for having lua callbacks +/// being triggered during the redraw code. +/// +/// The expected usage is to set extmarks for the currently +/// redrawn buffer. |nvim_buf_set_extmark| can be called to add marks +/// on a per-window or per-lines basis. Use the `ephemeral` key to only +/// use the mark for the current screen redraw (the callback will be called +/// again for the next redraw ). +/// +/// Note: this function should not be called often. Rather, the callbacks +/// themselves can be used to throttle unneeded callbacks. the `on_start` +/// callback can return `false` to disable the provider until the next redraw. +/// Similarily, return `false` in `on_win` will skip the `on_lines` calls +/// for that window (but any extmarks set in `on_win` will still be used). +/// A plugin managing multiple sources of decoration should ideally only set +/// one provider, and merge the sources internally. You can use multiple `ns_id` +/// for the extmarks set/modified inside the callback anyway. +/// +/// Note: doing anything other than setting extmarks is considered experimental. +/// Doing things like changing options are not expliticly forbidden, but is +/// likely to have unexpected consequences (such as 100% CPU consumption). +/// doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious +/// for the moment. +/// +/// @param ns_id Namespace id from |nvim_create_namespace()| +/// @param opts Callbacks invoked during redraw: +/// - on_start: called first on each screen redraw +/// ["start", tick] +/// - on_buf: called for each buffer being redrawn (before window +/// callbacks) +/// ["buf", bufnr, tick] +/// - on_win: called when starting to redraw a specific window. +/// ["win", winid, bufnr, topline, botline_guess] +/// - on_line: called for each buffer line being redrawn. (The +/// interation with fold lines is subject to change) +/// ["win", winid, bufnr, row] +/// - on_end: called at the end of a redraw cycle +/// ["end", tick] +void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, + Error *err) + FUNC_API_SINCE(7) FUNC_API_LUA_ONLY +{ + DecorProvider *p = get_provider((NS)ns_id, true); + clear_provider(p); + + // regardless of what happens, it seems good idea to redraw + redraw_all_later(NOT_VALID); // TODO(bfredl): too soon? + + struct { + const char *name; + LuaRef *dest; + } cbs[] = { + { "on_start", &p->redraw_start }, + { "on_buf", &p->redraw_buf }, + { "on_win", &p->redraw_win }, + { "on_line", &p->redraw_line }, + { "on_end", &p->redraw_end }, + { "_on_hl_def", &p->hl_def }, + { NULL, NULL }, + }; + + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object *v = &opts.items[i].value; + size_t j; + for (j = 0; cbs[j].name; j++) { + if (strequal(cbs[j].name, k.data)) { + if (v->type != kObjectTypeLuaRef) { + api_set_error(err, kErrorTypeValidation, + "%s is not a function", cbs[j].name); + goto error; + } + *(cbs[j].dest) = v->data.luaref; + v->data.luaref = LUA_NOREF; + break; + } + } + if (!cbs[j].name) { + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + goto error; + } } - decorations_add_luahl_attr(attr, (int)start_row, (colnr_T)start_col, - (int)end_row, (colnr_T)end_col); + + p->active = true; + return; +error: + clear_provider(p); } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ba43bc6cb2..f09a03f592 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -142,7 +142,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) // make sure cursor is in visible range even if win != curwin update_topline_win(win); - redraw_win_later(win, VALID); + redraw_later(win, VALID); } /// Gets the window height @@ -471,7 +471,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err) if (!win_new_float(win, fconfig, err)) { return; } - redraw_later(NOT_VALID); + redraw_later(win, NOT_VALID); } else { win_config_float(win, fconfig); win->w_pos_changed = true; |