aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
commitd5f194ce780c95821a855aca3c19426576d28ae0 (patch)
treed45f461b19f9118ad2bb1f440a7a08973ad18832 /src/nvim/api
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-rahm.tar.gz
rneovim-rahm.tar.bz2
rneovim-rahm.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
Diffstat (limited to 'src/nvim/api')
-rw-r--r--src/nvim/api/autocmd.c104
-rw-r--r--src/nvim/api/buffer.c351
-rw-r--r--src/nvim/api/command.c8
-rw-r--r--src/nvim/api/deprecated.c223
-rw-r--r--src/nvim/api/extmark.c157
-rw-r--r--src/nvim/api/extmark.h1
-rw-r--r--src/nvim/api/keysets_defs.h114
-rw-r--r--src/nvim/api/options.c60
-rw-r--r--src/nvim/api/private/converter.c3
-rw-r--r--src/nvim/api/private/defs.h2
-rw-r--r--src/nvim/api/private/helpers.c89
-rw-r--r--src/nvim/api/private/helpers.h16
-rw-r--r--src/nvim/api/tabpage.c13
-rw-r--r--src/nvim/api/ui.c4
-rw-r--r--src/nvim/api/ui.h1
-rw-r--r--src/nvim/api/ui_events.in.h6
-rw-r--r--src/nvim/api/vim.c416
-rw-r--r--src/nvim/api/vimscript.c70
-rw-r--r--src/nvim/api/win_config.c35
-rw-r--r--src/nvim/api/window.c77
20 files changed, 900 insertions, 850 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 22932fd1a2..d436dbb7f1 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -2,7 +2,6 @@
#include <lauxlib.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,6 +22,7 @@
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -68,32 +68,31 @@ static int64_t next_autocmd_id = 1;
/// match any combination of them.
///
/// @param opts Dict with at least one of the following:
-/// - group (string|integer): the autocommand group name or id to match against.
-/// - event (string|array): event or events to match against |autocmd-events|.
-/// - pattern (string|array): pattern or patterns to match against |autocmd-pattern|.
-/// Cannot be used with {buffer}
-/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands
/// |autocmd-buflocal|. Cannot be used with {pattern}
+/// - event: (string|table) event or events to match against |autocmd-events|.
+/// - id: (integer) Autocommand ID to match.
+/// - group: (string|table) the autocommand group name or id to match against.
+/// - pattern: (string|table) pattern or patterns to match against |autocmd-pattern|.
+/// Cannot be used with {buffer}
/// @return Array of autocommands matching the criteria, with each item
/// containing the following fields:
-/// - id (number): the autocommand id (only when defined with the API).
-/// - group (integer): the autocommand group id.
-/// - group_name (string): the autocommand group name.
-/// - desc (string): the autocommand description.
-/// - event (string): the autocommand event.
-/// - command (string): the autocommand command. Note: this will be empty if a callback is set.
-/// - callback (function|string|nil): Lua function or name of a Vim script function
+/// - buffer: (integer) the buffer number.
+/// - buflocal: (boolean) true if the autocommand is buffer local.
+/// - command: (string) the autocommand command. Note: this will be empty if a callback is set.
+/// - callback: (function|string|nil): Lua function or name of a Vim script function
/// which is executed when this autocommand is triggered.
-/// - once (boolean): whether the autocommand is only run once.
-/// - pattern (string): the autocommand pattern.
+/// - desc: (string) the autocommand description.
+/// - event: (string) the autocommand event.
+/// - id: (integer) the autocommand id (only when defined with the API).
+/// - group: (integer) the autocommand group id.
+/// - group_name: (string) the autocommand group name.
+/// - once: (boolean) whether the autocommand is only run once.
+/// - pattern: (string) the autocommand pattern.
/// If the autocommand is buffer local |autocmd-buffer-local|:
-/// - buflocal (boolean): true if the autocommand is buffer local.
-/// - buffer (number): the buffer number.
Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
- // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... })
-
ArrayBuilder autocmd_list = KV_INITIAL_VALUE;
kvi_init(autocmd_list);
char *pattern_filters[AUCMD_MAX_PATTERNS];
@@ -127,6 +126,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
});
}
+ int id = (HAS_KEY(opts, get_autocmds, id)) ? (int)opts->id : -1;
+
if (HAS_KEY(opts, get_autocmds, event)) {
check_event = true;
@@ -237,6 +238,10 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
continue;
}
+ if (id != -1 && ac->id != id) {
+ continue;
+ }
+
// Skip autocmds from invalid groups if passed.
if (group != 0 && ap->group != group) {
continue;
@@ -285,10 +290,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc));
}
- if (ac->exec.type == CALLABLE_CB) {
+ if (ac->handler_cmd) {
+ PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->handler_cmd));
+ } else {
PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT));
- Callback *cb = &ac->exec.callable.cb;
+ Callback *cb = &ac->handler_fn;
switch (cb->type) {
case kCallbackLua:
if (nlua_ref_is_function(cb->data.luaref)) {
@@ -302,8 +309,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
case kCallbackNone:
abort();
}
- } else {
- PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd));
}
PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat));
@@ -317,23 +322,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
}
- // TODO(sctx): It would be good to unify script_ctx to actually work with lua
- // right now it's just super weird, and never really gives you the info that
- // you would expect from this.
- //
- // I think we should be able to get the line number, filename, etc. from lua
- // when we're executing something, and it should be easy to then save that
- // info here.
- //
- // I think it's a big loss not getting line numbers of where options, autocmds,
- // etc. are set (just getting "Sourced (lua)" or something is not that helpful.
- //
- // Once we do that, we can put these into the autocmd_info, but I don't think it's
- // useful to do that at this time.
- //
- // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
- // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
-
kvi_push(autocmd_list, DICT_OBJ(autocmd_info));
}
}
@@ -386,9 +374,9 @@ cleanup:
/// - id: (number) autocommand id
/// - event: (string) name of the triggered event |autocmd-events|
/// - group: (number|nil) autocommand group id, if any
-/// - match: (string) expanded value of [<amatch>]
-/// - buf: (number) expanded value of [<abuf>]
-/// - file: (string) expanded value of [<afile>]
+/// - file: (string) [<afile>] (not expanded to a full path)
+/// - match: (string) [<amatch>] (expanded to a full path)
+/// - buf: (number) [<abuf>]
/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
/// - command (string) optional: Vim command to execute on event. Cannot be used with
/// {callback}
@@ -406,8 +394,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
{
int64_t autocmd_id = -1;
char *desc = NULL;
- AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
- Callback cb = CALLBACK_NONE;
+ char *handler_cmd = NULL;
+ Callback handler_fn = CALLBACK_NONE;
Array event_array = unpack_string_or_array(event, "event", true, arena, err);
if (ERROR_SET(err)) {
@@ -432,13 +420,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
- cb.type = kCallbackLua;
- cb.data.luaref = callback->data.luaref;
+ handler_fn.type = kCallbackLua;
+ handler_fn.data.luaref = callback->data.luaref;
callback->data.luaref = LUA_NOREF;
break;
case kObjectTypeString:
- cb.type = kCallbackFuncref;
- cb.data.funcref = string_to_cstr(callback->data.string);
+ handler_fn.type = kCallbackFuncref;
+ handler_fn.data.funcref = string_to_cstr(callback->data.string);
break;
default:
VALIDATE_EXP(false, "callback", "Lua function or Vim function name",
@@ -446,12 +434,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
}
-
- aucmd.type = CALLABLE_CB;
- aucmd.callable.cb = cb;
} else if (HAS_KEY(opts, create_autocmd, command)) {
- aucmd.type = CALLABLE_EX;
- aucmd.callable.cmd = string_to_cstr(opts->command);
+ handler_cmd = string_to_cstr(opts->command);
} else {
VALIDATE(false, "%s", "Required: 'command' or 'callback'", {
goto cleanup;
@@ -491,7 +475,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
int retval;
FOREACH_ITEM(patterns, pat, {
- // See: TODO(sctx)
WITH_SCRIPT_CONTEXT(channel_id, {
retval = autocmd_register(autocmd_id,
event_nr,
@@ -501,7 +484,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
opts->once,
opts->nested,
desc,
- aucmd);
+ handler_cmd,
+ &handler_fn);
});
if (retval == FAIL) {
@@ -512,7 +496,11 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
});
cleanup:
- aucmd_exec_free(&aucmd);
+ if (handler_cmd) {
+ XFREE_CLEAR(handler_cmd);
+ } else {
+ callback_free(&handler_fn);
+ }
return autocmd_id;
}
@@ -631,7 +619,7 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou
FUNC_API_SINCE(9)
{
char *augroup_name = name.data;
- bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err);
+ bool clear_autocmds = GET_BOOL_OR_TRUE(opts, create_augroup, clear);
int augroup = -1;
WITH_SCRIPT_CONTEXT(channel_id, {
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 9480292d9a..aa349790b3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -23,7 +23,6 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
#include "nvim/extmark_defs.h"
@@ -360,93 +359,91 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
memchrsub(lines[i], NUL, NL, l.size);
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
+ if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
+ goto end;
+ }
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- bcount_t inserted_bytes = 0;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start + (int64_t)i;
+ bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
}
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- }
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ bcount_t inserted_bytes = 0;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start + (int64_t)i;
+
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start + (int64_t)i - 1;
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ }
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start + (int64_t)i - 1;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
- }
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
+ }
- extra++;
- }
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- linenr_T adjust = end > start ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
+ extra++;
+ }
- extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
- deleted_bytes, (int)new_len, 0, inserted_bytes,
- kExtmarkUndo);
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ linenr_T adjust = end > start ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
- changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
+ extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
+ deleted_bytes, (int)new_len, 0, inserted_bytes,
+ kExtmarkUndo);
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
- }
- }
+ changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
-end:
- try_end(err);
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
+ }
+ }
+ end:;
+ });
}
/// Sets (replaces) a range in the buffer
@@ -593,101 +590,99 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
new_byte += (bcount_t)(last_item.size) + 1;
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- // Small note about undo states: unlike set_lines, we want to save the
- // undo state of one past the end_row, since end_row is inclusive.
- if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- ptrdiff_t extra = 0; // lines added to text, can be negative
- size_t old_len = (size_t)(end_row - start_row + 1);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
-
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start_row + (int64_t)i;
-
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ // Small note about undo states: unlike set_lines, we want to save the
+ // undo state of one past the end_row, since end_row is inclusive.
+ if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
goto end;
- });
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ ptrdiff_t extra = 0; // lines added to text, can be negative
+ size_t old_len = (size_t)(end_row - start_row + 1);
+
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
}
- }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start_row + (int64_t)i - 1;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
+ }
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
- goto end;
- });
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start_row + (int64_t)i;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
}
- extra++;
- }
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start_row + (int64_t)i - 1;
- colnr_T col_extent = (colnr_T)(end_col
- - ((end_row == start_row) ? start_col : 0));
-
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- // Do not adjust any cursors. need to use column-aware logic (below)
- linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
-
- extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
- (int)(end_row - start_row), col_extent, old_byte,
- (int)new_len - 1, (colnr_T)last_item.size, new_byte,
- kExtmarkUndo);
-
- changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
-
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
- fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
- (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
- } else {
- fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
}
+
+ extra++;
}
- }
-end:
- try_end(err);
+ colnr_T col_extent = (colnr_T)(end_col
+ - ((end_row == start_row) ? start_col : 0));
+
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ // Do not adjust any cursors. need to use column-aware logic (below)
+ linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
+
+ extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
+ (int)(end_row - start_row), col_extent, old_byte,
+ (int)new_len - 1, (colnr_T)last_item.size, new_byte,
+ kExtmarkUndo);
+
+ changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
+
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
+ fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
+ (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
+ } else {
+ fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ }
+ }
+ }
+ end:;
+ });
}
/// Gets a range from the buffer.
@@ -965,26 +960,27 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
return;
}
- try_start();
-
- const bool is_curbuf = buf == curbuf;
- const int save_acd = p_acd;
- if (!is_curbuf) {
- // Temporarily disable 'autochdir' when setting file name for another buffer.
- p_acd = false;
- }
+ int ren_ret = OK;
+ TRY_WRAP(err, {
+ const bool is_curbuf = buf == curbuf;
+ const int save_acd = p_acd;
+ if (!is_curbuf) {
+ // Temporarily disable 'autochdir' when setting file name for another buffer.
+ p_acd = false;
+ }
- // Using aucmd_*: autocommands will be executed by rename_buffer
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- int ren_ret = rename_buffer(name.data);
- aucmd_restbuf(&aco);
+ // Using aucmd_*: autocommands will be executed by rename_buffer
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ ren_ret = rename_buffer(name.data);
+ aucmd_restbuf(&aco);
- if (!is_curbuf) {
- p_acd = save_acd;
- }
+ if (!is_curbuf) {
+ p_acd = save_acd;
+ }
+ });
- if (try_end(err)) {
+ if (ERROR_SET(err)) {
return;
}
@@ -1184,12 +1180,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena,
/// 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, then one of these windows will be set as current window temporarily.
+/// buffer, then one of those windows will be set as current window temporarily.
/// Otherwise a temporary scratch window (called the "autocmd window" for
/// historical reasons) will be used.
///
/// This is useful e.g. to call Vimscript functions that only work with the
-/// current buffer/window currently, like |termopen()|.
+/// current buffer/window currently, like `jobstart(…, {'term': v:true})`.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param fun Function to call inside the buffer (currently Lua callable
@@ -1204,15 +1200,18 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
if (!buf) {
return NIL;
}
- try_start();
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- Array args = ARRAY_DICT_INIT;
- Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ Object res = OBJECT_INIT;
+ TRY_WRAP(err, {
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+
+ aucmd_restbuf(&aco);
+ });
- aucmd_restbuf(&aco);
- try_end(err);
return res;
}
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index ab57d5c009..23e08bd3fe 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -26,6 +26,7 @@
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/ops.h"
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
@@ -226,8 +227,8 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err
addr = "?";
break;
}
- PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr));
- PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd));
+ PUT_KEY(result, cmd, addr, cstr_as_string(addr));
+ PUT_KEY(result, cmd, nextcmd, cstr_as_string(ea.nextcmd));
// TODO(bfredl): nested keydict would be nice..
Dict mods = arena_dict(arena, 20);
@@ -930,6 +931,9 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
gap = &ucmds;
} else {
buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
gap = &buf->b_ucmds;
}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index b38a7d4173..1d81b21be6 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,3 +1,5 @@
+// Island of misfit toys.
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -7,6 +9,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/vimscript.h"
@@ -18,14 +21,14 @@
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/marktree.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/channel_defs.h"
-#include "nvim/msgpack_rpc/unpacker.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -82,6 +85,17 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
return buf->b_fnum;
}
+static uint32_t src2ns(Integer *src_id)
+{
+ if (*src_id == 0) {
+ *src_id = nvim_create_namespace((String)STRING_INIT);
+ }
+ if (*src_id < 0) {
+ return (((uint32_t)1) << 31) - 1;
+ }
+ return (uint32_t)(*src_id);
+}
+
/// Clears highlights and virtual text from namespace and range of lines
///
/// @deprecated use |nvim_buf_clear_namespace()|.
@@ -100,6 +114,80 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err);
}
+/// Adds a highlight to buffer.
+///
+/// @deprecated use |nvim_buf_set_extmark()| or |vim.hl.range()|
+///
+/// Namespaces are used for batch deletion/updating of a set of highlights. To
+/// 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
+/// 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.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id namespace to use or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line Line to highlight (zero-indexed)
+/// @param col_start Start of (byte-indexed) column range to highlight
+/// @param col_end End of (byte-indexed) column range to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Error details, if any
+/// @return The ns_id that was used
+Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
+ Integer col_start, Integer col_end, Error *err)
+ FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(13)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
+ return 0;
+ });
+ VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
+ return 0;
+ });
+
+ if (col_end < 0 || col_end > MAXCOL) {
+ col_end = MAXCOL;
+ }
+
+ uint32_t ns = src2ns(&ns_id);
+
+ if (!(line < buf->b_ml.ml_line_count)) {
+ // safety check, we can't add marks outside the range
+ return ns_id;
+ }
+
+ int hl_id = 0;
+ if (hl_group.size > 0) {
+ hl_id = syn_check_group(hl_group.data, hl_group.size);
+ } else {
+ return ns_id;
+ }
+
+ int end_line = (int)line;
+ if (col_end == MAXCOL) {
+ col_end = 0;
+ end_line++;
+ }
+
+ DecorInline decor = DECOR_INLINE_INIT;
+ decor.data.hl.hl_id = hl_id;
+
+ extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
+ decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
+ return ns_id;
+}
/// Set the virtual text (annotation) for a buffer line.
///
/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality.
@@ -636,23 +724,27 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param[out] err Details of an error that may have occurred.
///
/// @return the option value.
-static Object get_option_from(void *from, OptScope req_scope, String name, Error *err)
+static Object get_option_from(void *from, OptScope scope, String name, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return (Object)OBJECT_INIT;
});
OptIndex opt_idx = find_option(name.data);
+ VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, {
+ return (Object)OBJECT_INIT;
+ });
+
OptVal value = NIL_OPTVAL;
- if (option_has_scope(opt_idx, req_scope)) {
- value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
- req_scope, from, err);
+ if (option_has_scope(opt_idx, scope)) {
+ value = get_option_value_for(opt_idx, scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
+ scope, from, err);
if (ERROR_SET(err)) {
return (Object)OBJECT_INIT;
}
@@ -668,12 +760,12 @@ static Object get_option_from(void *from, OptScope req_scope, String name, Error
/// Sets the value of a global or local (buffer, window) option.
///
/// @param[in] to Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param value New option value.
/// @param[out] err Details of an error that may have occurred.
-static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name,
- Object value, Error *err)
+static void set_option_to(uint64_t channel_id, void *to, OptScope scope, String name, Object value,
+ Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return;
@@ -698,12 +790,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, Str
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
const int opt_flags
- = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
+ = (scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
? 0
- : ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
+ : ((scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -798,7 +890,8 @@ theend:
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_subscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
@@ -808,7 +901,105 @@ void nvim_subscribe(uint64_t channel_id, String event)
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_unsubscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
+
+enum { LINE_BUFFER_MIN_SIZE = 4096, };
+
+/// Writes a message to vim output or error buffer. The string is split
+/// and flushed after each newline. Incomplete lines are kept for writing
+/// later.
+///
+/// @param message Message to write
+/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
+/// @param writeln Append a trailing newline
+static void write_msg(String message, bool to_err, bool writeln)
+{
+ static StringBuilder out_line_buf = KV_INITIAL_VALUE;
+ static StringBuilder err_line_buf = KV_INITIAL_VALUE;
+ StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
+
+#define PUSH_CHAR(c) \
+ if (kv_max(*line_buf) == 0) { \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } \
+ if (c == NL) { \
+ kv_push(*line_buf, NUL); \
+ if (to_err) { \
+ emsg(line_buf->items); \
+ } else { \
+ msg(line_buf->items, 0); \
+ } \
+ if (msg_silent == 0) { \
+ msg_didout = true; \
+ } \
+ kv_drop(*line_buf, kv_size(*line_buf)); \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } else if (c == NUL) { \
+ kv_push(*line_buf, NL); \
+ } else { \
+ kv_push(*line_buf, c); \
+ }
+
+ no_wait_return++;
+ for (uint32_t i = 0; i < message.size; i++) {
+ if (got_int) {
+ break;
+ }
+ PUSH_CHAR(message.data[i]);
+ }
+ if (writeln) {
+ PUSH_CHAR(NL);
+ }
+ no_wait_return--;
+ msg_end();
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_out_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, false, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_writeln(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, true);
+}
+
+/// @deprecated
+///
+/// Use `nvim_echo` or `nvim_exec_lua("vim.notify(...)", ...)` instead.
+///
+/// @param msg Message to display to the user
+/// @param log_level The log level
+/// @param opts Reserved for future use.
+/// @param[out] err Error details, if any
+Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
+ FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(13)
+{
+ MAXSIZE_TEMP_ARRAY(args, 3);
+ ADD_C(args, STRING_OBJ(msg));
+ ADD_C(args, INTEGER_OBJ(log_level));
+ ADD_C(args, DICT_OBJ(opts));
+
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
+}
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index c94b8df9ea..8b31196eef 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -27,6 +27,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -48,7 +49,7 @@ void api_extmark_free_all_mem(void)
/// Creates a new namespace or gets an existing one. [namespace]()
///
/// Namespaces are used for buffer highlights and virtual text, see
-/// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|.
+/// |nvim_buf_set_extmark()|.
///
/// Namespaces can be named or anonymous. If `name` matches an existing
/// namespace, the associated id is returned. If `name` is an empty string
@@ -61,7 +62,7 @@ Integer nvim_create_namespace(String name)
{
handle_T id = map_get(String, int)(&namespace_ids, name);
if (id > 0) {
- return id;
+ return (Integer)id;
}
id = next_namespace_id++;
if (name.size > 0) {
@@ -384,6 +385,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - hl_group : highlight group used for the text range. This and below
/// highlight groups can be supplied either as a string or as an integer,
/// the latter of which can be obtained using |nvim_get_hl_id_by_name()|.
+///
+/// Multiple highlight groups can be stacked by passing an array (highest
+/// priority last).
/// - hl_eol : when true, for a multiline highlight covering the
/// EOL of a line, continue the highlight for the rest
/// of the screen line (just like for diff and
@@ -396,6 +400,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// (highest priority last).
/// - virt_text_pos : position of virtual text. Possible values:
/// - "eol": right after eol character (default).
+/// - "eol_right_align": display right aligned in the window
+/// unless the virtual text is longer than
+/// the space available. If the virtual
+/// text is too long, it is truncated to
+/// fit in the window after the EOL
+/// character. If the line is wrapped, the
+/// virtual text is shown after the end of
+/// the line rather than the previous
+/// screen line.
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
/// - "right_align": display right aligned in the window.
@@ -498,6 +511,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
char *url = NULL;
bool has_hl = false;
+ bool has_hl_multiple = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -550,8 +564,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = (int)val;
}
- hl.hl_id = (int)opts->hl_group;
- has_hl = hl.hl_id > 0;
+ if (HAS_KEY(opts, set_extmark, hl_group)) {
+ if (opts->hl_group.type == kObjectTypeArray) {
+ Array arr = opts->hl_group.data.array;
+ if (arr.size >= 1) {
+ hl.hl_id = object_to_hl_id(arr.items[0], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ for (size_t i = 1; i < arr.size; i++) {
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ if (hl_id) {
+ has_hl_multiple = true;
+ }
+ }
+ } else {
+ hl.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ has_hl = hl.hl_id > 0;
+ }
+
sign.hl_id = (int)opts->sign_hl_group;
sign.cursorline_hl_id = (int)opts->cursorline_hl_group;
sign.number_hl_id = (int)opts->number_hl_group;
@@ -590,6 +629,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
virt_text.pos = kVPosOverlay;
} else if (strequal("right_align", str.data)) {
virt_text.pos = kVPosRightAlign;
+ } else if (strequal("eol_right_align", str.data)) {
+ virt_text.pos = kVPosEndOfLineRightAlign;
} else if (strequal("inline", str.data)) {
virt_text.pos = kVPosInline;
} else {
@@ -793,6 +834,21 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
+ if (has_hl_multiple) {
+ Array arr = opts->hl_group.data.array;
+ for (size_t i = arr.size - 1; i > 0; i--) { // skip hl_group[0], handled as hl.hl_id below
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (hl_id > 0) {
+ DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT;
+ sh.hl_id = hl_id;
+ sh.flags = opts->hl_eol ? kSHHlEol : 0;
+ sh.next = decor_indexed;
+ decor_indexed = decor_put_sh(sh);
+ decor_flags |= MT_FLAG_DECOR_HL;
+ }
+ }
+ }
+
DecorInline decor = DECOR_INLINE_INIT;
if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL
|| schar_high(hl.conceal_char)) {
@@ -856,95 +912,6 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id);
}
-uint32_t src2ns(Integer *src_id)
-{
- if (*src_id == 0) {
- *src_id = nvim_create_namespace((String)STRING_INIT);
- }
- if (*src_id < 0) {
- return (((uint32_t)1) << 31) - 1;
- }
- return (uint32_t)(*src_id);
-}
-
-/// Adds a highlight to buffer.
-///
-/// Useful for plugins that dynamically generate highlights to a buffer
-/// (like a semantic highlighter or linter). The function adds a single
-/// highlight to a buffer. Unlike |matchaddpos()| highlights follow changes to
-/// line numbering (as lines are inserted/removed above the highlighted line),
-/// 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
-/// 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
-/// 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.
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param ns_id namespace to use or -1 for ungrouped highlight
-/// @param hl_group Name of the highlight group to use
-/// @param line Line to highlight (zero-indexed)
-/// @param col_start Start of (byte-indexed) column range to highlight
-/// @param col_end End of (byte-indexed) column range to highlight,
-/// or -1 to highlight to end of line
-/// @param[out] err Error details, if any
-/// @return The ns_id that was used
-Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
- Integer col_start, Integer col_end, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (!buf) {
- return 0;
- }
-
- VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
- return 0;
- });
- VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
- return 0;
- });
-
- if (col_end < 0 || col_end > MAXCOL) {
- col_end = MAXCOL;
- }
-
- uint32_t ns = src2ns(&ns_id);
-
- if (!(line < buf->b_ml.ml_line_count)) {
- // safety check, we can't add marks outside the range
- return ns_id;
- }
-
- int hl_id = 0;
- if (hl_group.size > 0) {
- hl_id = syn_check_group(hl_group.data, hl_group.size);
- } else {
- return ns_id;
- }
-
- int end_line = (int)line;
- if (col_end == MAXCOL) {
- col_end = 0;
- end_line++;
- }
-
- DecorInline decor = DECOR_INLINE_INIT;
- decor.data.hl.hl_id = hl_id;
-
- extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
- decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
- return ns_id;
-}
-
/// Clears |namespace|d objects (highlights, |extmarks|, virtual text) from
/// a region.
///
@@ -1012,8 +979,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// ```
/// ["start", tick]
/// ```
-/// - on_buf: called for each buffer being redrawn (before
-/// window callbacks)
+/// - on_buf: called for each buffer being redrawn (once per edit,
+/// before window callbacks)
/// ```
/// ["buf", bufnr, tick]
/// ```
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index af2d51c95c..0b4d84110b 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -1,5 +1,6 @@
#pragma once
+#include <stdbool.h>
#include <stdint.h> // IWYU pragma: keep
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 96aabb851f..6625908cda 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -8,18 +8,19 @@ typedef struct {
typedef struct {
OptionalKeys is_set__context_;
- Array types;
+ ArrayOf(String) types;
} Dict(context);
typedef struct {
OptionalKeys is_set__set_decoration_provider_;
- LuaRef on_start;
- LuaRef on_buf;
- LuaRef on_win;
- LuaRef on_line;
- LuaRef on_end;
- LuaRef _on_hl_def;
- LuaRef _on_spell_nav;
+ LuaRefOf(("start" _, Integer tick), *Boolean) on_start;
+ LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
+ LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
+ *Boolean) on_win;
+ LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
+ LuaRefOf(("end" _, Integer tick)) on_end;
+ LuaRefOf(("hl_def" _)) _on_hl_def;
+ LuaRefOf(("spell_nav" _)) _on_spell_nav;
} Dict(set_decoration_provider);
typedef struct {
@@ -28,7 +29,7 @@ typedef struct {
Integer end_line;
Integer end_row;
Integer end_col;
- HLGroupID hl_group;
+ Object hl_group;
Array virt_text;
String virt_text_pos;
Integer virt_text_win_col;
@@ -116,7 +117,7 @@ typedef struct {
String relative;
String split;
Window win;
- Array bufpos;
+ ArrayOf(Integer) bufpos;
Boolean external;
Boolean focusable;
Boolean mouse;
@@ -172,17 +173,17 @@ typedef struct {
Boolean altfont;
Boolean nocombine;
Boolean default_ DictKey(default);
- Object cterm;
- Object foreground;
- Object fg;
- Object background;
- Object bg;
- Object ctermfg;
- Object ctermbg;
- Object special;
- Object sp;
- Object link;
- Object global_link;
+ Union(Integer, String) cterm;
+ Union(Integer, String) foreground;
+ Union(Integer, String) fg;
+ Union(Integer, String) background;
+ Union(Integer, String) bg;
+ Union(Integer, String) ctermfg;
+ Union(Integer, String) ctermbg;
+ Union(Integer, String) special;
+ Union(Integer, String) sp;
+ HLGroupID link;
+ HLGroupID global_link;
Boolean fallback;
Integer blend;
Boolean fg_indexed;
@@ -230,9 +231,9 @@ typedef struct {
typedef struct {
OptionalKeys is_set__clear_autocmds_;
Buffer buffer;
- Object event;
- Object group;
- Object pattern;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
} Dict(clear_autocmds);
typedef struct {
@@ -241,31 +242,33 @@ typedef struct {
Object callback;
String command;
String desc;
- Object group;
+ Union(Integer, String) group;
Boolean nested;
Boolean once;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
} Dict(create_autocmd);
typedef struct {
OptionalKeys is_set__exec_autocmds_;
Buffer buffer;
- Object group;
+ Union(Integer, String) group;
Boolean modeline;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
Object data;
} Dict(exec_autocmds);
typedef struct {
OptionalKeys is_set__get_autocmds_;
- Object event;
- Object group;
- Object pattern;
- Object buffer;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
+ Union(Integer, ArrayOf(Integer)) buffer;
+ Integer id;
} Dict(get_autocmds);
typedef struct {
- Object clear;
+ OptionalKeys is_set__create_augroup_;
+ Boolean clear;
} Dict(create_augroup);
typedef struct {
@@ -275,12 +278,12 @@ typedef struct {
Integer count;
String reg;
Boolean bang;
- Array args;
+ ArrayOf(String) args;
Dict magic;
Dict mods;
- Object nargs;
- Object addr;
- Object nextcmd;
+ Union(Integer, String) nargs;
+ String addr;
+ String nextcmd;
} Dict(cmd);
typedef struct {
@@ -324,6 +327,7 @@ typedef struct {
} Dict(cmd_opts);
typedef struct {
+ Boolean err;
Boolean verbose;
} Dict(echo_opts);
@@ -333,11 +337,30 @@ typedef struct {
typedef struct {
OptionalKeys is_set__buf_attach_;
- LuaRef on_lines;
- LuaRef on_bytes;
- LuaRef on_changedtick;
- LuaRef on_detach;
- LuaRef on_reload;
+ LuaRefOf(("lines" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer first,
+ Integer last_old,
+ Integer last_new,
+ Integer byte_count,
+ Integer *deleted_codepoints,
+ Integer *deleted_codeunits), *Boolean) on_lines;
+ LuaRefOf(("bytes" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer start_row,
+ Integer start_col,
+ Integer start_byte,
+ Integer old_end_row,
+ Integer old_end_col,
+ Integer old_end_byte,
+ Integer new_end_row,
+ Integer new_end_col,
+ Integer new_end_byte), *Boolean) on_bytes;
+ LuaRefOf(("changedtick" _, Integer bufnr, Integer changedtick)) on_changedtick;
+ LuaRefOf(("detach" _, Integer bufnr)) on_detach;
+ LuaRefOf(("reload" _, Integer bufnr)) on_reload;
Boolean utf_sizes;
Boolean preview;
} Dict(buf_attach);
@@ -350,7 +373,7 @@ typedef struct {
typedef struct {
OptionalKeys is_set__open_term_;
- LuaRef on_input;
+ LuaRefOf(("input" _, Integer term, Integer bufnr, any data)) on_input;
Boolean force_crlf;
} Dict(open_term);
@@ -361,12 +384,13 @@ typedef struct {
typedef struct {
OptionalKeys is_set__xdl_diff_;
- LuaRef on_hunk;
+ LuaRefOf((Integer start_a, Integer count_a, Integer start_b, Integer count_b),
+ *Integer) on_hunk;
String result_type;
String algorithm;
Integer ctxlen;
Integer interhunkctxlen;
- Object linematch;
+ Union(Boolean, Integer) linematch;
Boolean ignore_whitespace;
Boolean ignore_whitespace_change;
Boolean ignore_whitespace_change_at_eol;
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 3289daeb6f..64f8a35d54 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -14,6 +14,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -23,15 +24,15 @@
#endif
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
- int *scope, OptScope *req_scope, void **from, char **filetype,
+ int *opt_flags, OptScope *scope, void **from, char **filetype,
Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
if (!strcmp(opts->scope.data, "local")) {
- *scope = OPT_LOCAL;
+ *opt_flags = OPT_LOCAL;
} else if (!strcmp(opts->scope.data, "global")) {
- *scope = OPT_GLOBAL;
+ *opt_flags = OPT_GLOBAL;
} else {
VALIDATE_EXP(false, "scope", "'local' or 'global'", NULL, {
return FAIL;
@@ -39,14 +40,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- *req_scope = kOptScopeGlobal;
+ *scope = kOptScopeGlobal;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY_X(opts, win)) {
- *req_scope = kOptScopeWin;
+ *scope = kOptScopeWin;
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -54,12 +55,12 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
if (HAS_KEY_X(opts, buf)) {
- VALIDATE(!(HAS_KEY_X(opts, scope) && *scope == OPT_GLOBAL), "%s",
+ VALIDATE(!(HAS_KEY_X(opts, scope) && *opt_flags == OPT_GLOBAL), "%s",
"cannot use both global 'scope' and 'buf'", {
return FAIL;
});
- *scope = OPT_LOCAL;
- *req_scope = kOptScopeBuf;
+ *opt_flags = OPT_LOCAL;
+ *scope = kOptScopeBuf;
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -81,10 +82,10 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
if (*opt_idxp == kOptInvalid) {
// unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
- } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) {
+ } else if (*scope == kOptScopeBuf || *scope == kOptScopeWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
- if (!option_has_scope(*opt_idxp, *req_scope)) {
- char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win";
+ if (!option_has_scope(*opt_idxp, *scope)) {
+ char *tgt = *scope == kOptScopeBuf ? "buf" : "win";
char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
? "buffer-local "
@@ -95,7 +96,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- return OK;
+ return ERROR_SET(err) ? FAIL : OK;
#undef HAS_KEY_X
}
@@ -151,13 +152,13 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9) FUNC_API_RET_ALLOC
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
char *filetype = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype,
- err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from,
+ &filetype, err)) {
return (Object)OBJECT_INIT;
}
@@ -181,7 +182,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
from = ftbuf;
}
- OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
+ OptVal value = get_option_value_for(opt_idx, opt_flags, scope, from, err);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -224,10 +225,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
FUNC_API_SINCE(9)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *to = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &to, NULL,
+ err)) {
return;
}
@@ -237,9 +239,9 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
// - option is global or local to window (global-local)
//
// Then force scope to local since we don't want to change the global option
- if (req_scope == kOptScopeWin && scope == 0) {
+ if (scope == kOptScopeWin && opt_flags == 0) {
if (option_has_scope(opt_idx, kOptScopeGlobal)) {
- scope = OPT_LOCAL;
+ opt_flags = OPT_LOCAL;
}
}
@@ -255,7 +257,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
});
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -310,16 +312,16 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error
FUNC_API_SINCE(11)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from, NULL,
err)) {
return (Dict)ARRAY_DICT_INIT;
}
- buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
- win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin;
+ buf_T *buf = (scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
+ win_T *win = (scope == kOptScopeWin) ? (win_T *)from : curwin;
- return get_vimoption(name, scope, buf, win, arena, err);
+ return get_vimoption(name, opt_flags, buf, win, arena, err);
}
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 59e7373f68..5f9d20ee73 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -7,7 +8,6 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/typval.h"
@@ -15,6 +15,7 @@
#include "nvim/eval/userfunc.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 26d5ac09a8..6dee86dcf5 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -21,6 +21,8 @@
# define Dict(name) KeyDict_##name
# define DictHash(name) KeyDict_##name##_get_field
# define DictKey(name)
+# define LuaRefOf(...) LuaRef
+# define Union(...) Object
# include "api/private/defs.h.inline.generated.h"
#endif
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 8ddaecc58e..c98635f8fd 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -31,19 +31,18 @@
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
-#include "nvim/ui.h"
-#include "nvim/ui_defs.h"
-#include "nvim/version.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/api_metadata.generated.h"
-# include "api/private/helpers.c.generated.h"
+# include "api/private/helpers.c.generated.h" // IWYU pragma: keep
#endif
/// Start block that may cause Vimscript exceptions while evaluating another code
///
-/// Used when caller is supposed to be operating when other Vimscript code is being
-/// processed and that “other Vimscript code” must not be affected.
+/// Used just in case caller is supposed to be operating when other Vimscript code
+/// is being processed and that “other Vimscript code” must not be affected.
+///
+/// @warning Avoid calling directly; use TRY_WRAP instead.
///
/// @param[out] tstate Location where try state should be saved.
void try_enter(TryState *const tstate)
@@ -55,74 +54,33 @@ void try_enter(TryState *const tstate)
.current_exception = current_exception,
.msg_list = (const msglist_T *const *)msg_list,
.private_msg_list = NULL,
- .trylevel = trylevel,
.got_int = got_int,
.did_throw = did_throw,
.need_rethrow = need_rethrow,
.did_emsg = did_emsg,
};
+ // `msg_list` controls the collection of abort-causing non-exception errors,
+ // which would otherwise be ignored. This pattern is from do_cmdline().
msg_list = &tstate->private_msg_list;
current_exception = NULL;
- trylevel = 1;
got_int = false;
did_throw = false;
need_rethrow = false;
did_emsg = false;
-}
-
-/// End try block, set the error message if any and restore previous state
-///
-/// @warning Return is consistent with most functions (false on error), not with
-/// try_end (true on error).
-///
-/// @param[in] tstate Previous state to restore.
-/// @param[out] err Location where error should be saved.
-///
-/// @return false if error occurred, true otherwise.
-bool try_leave(const TryState *const tstate, Error *const err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- const bool ret = !try_end(err);
- assert(trylevel == 0);
- assert(!need_rethrow);
- assert(!got_int);
- assert(!did_throw);
- assert(!did_emsg);
- assert(msg_list == &tstate->private_msg_list);
- assert(*msg_list == NULL);
- assert(current_exception == NULL);
- msg_list = (msglist_T **)tstate->msg_list;
- current_exception = tstate->current_exception;
- trylevel = tstate->trylevel;
- got_int = tstate->got_int;
- did_throw = tstate->did_throw;
- need_rethrow = tstate->need_rethrow;
- did_emsg = tstate->did_emsg;
- return ret;
-}
-
-/// Start block that may cause vimscript exceptions
-///
-/// Each try_start() call should be mirrored by try_end() call.
-///
-/// To be used as a replacement of `:try … catch … endtry` in C code, in cases
-/// when error flag could not already be set. If there may be pending error
-/// state at the time try_start() is executed which needs to be preserved,
-/// try_enter()/try_leave() pair should be used instead.
-void try_start(void)
-{
trylevel++;
}
-/// End try block, set the error message if any and return true if an error
-/// occurred.
+/// Ends a `try_enter` block; sets error message if any.
///
-/// @param err Pointer to the stack-allocated error object
-/// @return true if an error occurred
-bool try_end(Error *err)
+/// @warning Avoid calling directly; use TRY_WRAP instead.
+///
+/// @param[out] err Pointer to the stack-allocated error object
+void try_leave(const TryState *const tstate, Error *const err)
+ FUNC_ATTR_NONNULL_ALL
{
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
+ assert(trylevel > 0);
trylevel--;
// Set by emsg(), affects aborting(). See also enter_cleanup().
@@ -165,7 +123,20 @@ bool try_end(Error *err)
discard_current_exception();
}
- return ERROR_SET(err);
+ assert(msg_list == &tstate->private_msg_list);
+ assert(*msg_list == NULL);
+ assert(current_exception == NULL);
+ assert(!got_int);
+ assert(!did_throw);
+ assert(!need_rethrow);
+ assert(!did_emsg);
+ // Restore the exception context.
+ msg_list = (msglist_T **)tstate->msg_list;
+ current_exception = tstate->current_exception;
+ got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
+ need_rethrow = tstate->need_rethrow;
+ did_emsg = tstate->did_emsg;
}
/// Recursively expands a vimscript value in a dict
@@ -805,7 +776,7 @@ char *api_typename(ObjectType t)
UNREACHABLE;
}
-HlMessage parse_hl_msg(Array chunks, Error *err)
+HlMessage parse_hl_msg(Array chunks, bool is_err, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
@@ -820,7 +791,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
String str = copy_string(chunk.items[0].data.string, NULL);
- int hl_id = 0;
+ int hl_id = is_err ? HLF_E : 0;
if (chunk.size == 2) {
hl_id = object_to_hl_id(chunk.items[1], "text highlight", err);
}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index d06f5c9c65..d581c6bc10 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -1,7 +1,7 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include "klib/kvec.h"
#include "nvim/api/private/defs.h" // IWYU pragma: keep
@@ -147,27 +147,19 @@ typedef struct {
except_T *current_exception;
msglist_T *private_msg_list;
const msglist_T *const *msg_list;
- int trylevel;
int got_int;
bool did_throw;
int need_rethrow;
int did_emsg;
} TryState;
-// `msg_list` controls the collection of abort-causing non-exception errors,
-// which would otherwise be ignored. This pattern is from do_cmdline().
-//
// TODO(bfredl): prepare error-handling at "top level" (nv_event).
#define TRY_WRAP(err, code) \
do { \
- msglist_T **saved_msg_list = msg_list; \
- msglist_T *private_msg_list; \
- msg_list = &private_msg_list; \
- private_msg_list = NULL; \
- try_start(); \
+ TryState tstate; \
+ try_enter(&tstate); \
code; \
- try_end(err); \
- msg_list = saved_msg_list; /* Restore the exception context. */ \
+ try_leave(&tstate, err); \
} while (0)
// Execute code with cursor position saved and restored and textlock active.
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 56a3f1cf23..dce47cd99f 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -7,11 +7,12 @@
#include "nvim/api/vim.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
-#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
+#include "nvim/types_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/tabpage.c.generated.h"
+# include "api/tabpage.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the windows in a tabpage
@@ -146,11 +147,9 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err)
}
if (tp == curtab) {
- try_start();
- win_goto(wp);
- if (!try_end(err) && curwin != wp) {
- api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win);
- }
+ TRY_WRAP(err, {
+ win_goto(wp);
+ });
} else if (tp->tp_curwin != wp) {
tp->tp_prevwin = tp->tp_curwin;
tp->tp_curwin = wp;
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index b09a9ed253..f6f32a73ed 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -23,7 +23,6 @@
#include "nvim/event/wstream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
@@ -34,6 +33,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
@@ -537,7 +537,7 @@ static void prepare_call(RemoteUI *ui, const char *name)
ui_alloc_buf(ui);
}
- // To optimize data transfer(especially for "grid_line"), we bundle adjacent
+ // To optimize data transfer (especially for "grid_line"), we bundle adjacent
// calls to same method together, so only add a new call entry if the last
// method call is different from "name"
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index cdccc27ba4..3f996ec219 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -4,6 +4,7 @@
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/highlight_defs.h" // IWYU pragma: keep
+#include "nvim/macros_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
#include "nvim/ui_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 0ed208fc1a..74718e7ac5 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -136,13 +136,13 @@ void tabline_update(Tabpage current, Array tabs, Buffer current_buffer, Array bu
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_show(Array content, Integer pos, String firstc, String prompt, Integer indent,
- Integer level)
+ Integer level, Integer hl_id)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_pos(Integer pos, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_special_char(String c, Boolean shift, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void cmdline_hide(Integer level)
+void cmdline_hide(Integer level, Boolean abort)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_block_show(Array lines)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -158,7 +158,7 @@ void wildmenu_select(Integer selected)
void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void msg_show(String kind, Array content, Boolean replace_last)
+void msg_show(String kind, Array content, Boolean replace_last, Boolean history)
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 83f9aa573d..c103a56032 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -20,6 +20,7 @@
#include "nvim/api/vim.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
@@ -27,8 +28,8 @@
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -44,6 +45,7 @@
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/lua/executor.h"
@@ -76,7 +78,6 @@
#include "nvim/runtime.h"
#include "nvim/sign_defs.h"
#include "nvim/state.h"
-#include "nvim/state_defs.h"
#include "nvim/statusline.h"
#include "nvim/statusline_defs.h"
#include "nvim/strings.h"
@@ -86,8 +87,6 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#define LINE_BUFFER_MIN_SIZE 4096
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vim.c.generated.h"
#endif
@@ -518,26 +517,6 @@ Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err)
return nlua_exec(code, args, kRetObject, arena, err);
}
-/// Notify the user with a message
-///
-/// Relays the call to vim.notify . By default forwards your message in the
-/// echo area but can be overridden to trigger desktop notifications.
-///
-/// @param msg Message to display to the user
-/// @param log_level The log level
-/// @param opts Reserved for future use.
-/// @param[out] err Error details, if any
-Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
- FUNC_API_SINCE(7)
-{
- MAXSIZE_TEMP_ARRAY(args, 3);
- ADD_C(args, STRING_OBJ(msg));
- ADD_C(args, INTEGER_OBJ(log_level));
- ADD_C(args, DICT_OBJ(opts));
-
- return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
-}
-
/// Calculates the number of display cells occupied by `text`.
/// Control characters including [<Tab>] count as one cell.
///
@@ -594,11 +573,10 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Er
kvi_init(cookie.rv);
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
- TryState tstate;
- try_enter(&tstate);
- do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
+ });
return arena_take_arraybuilder(arena, &cookie.rv);
}
@@ -668,16 +646,9 @@ void nvim_set_current_dir(String dir, Error *err)
memcpy(string, dir.data, dir.size);
string[dir.size] = NUL;
- try_start();
-
- if (!changedir_func(string, kCdScopeGlobal)) {
- if (!try_end(err)) {
- api_set_error(err, kErrorTypeException, "Failed to change directory");
- }
- return;
- }
-
- try_end(err);
+ TRY_WRAP(err, {
+ changedir_func(string, kCdScopeGlobal);
+ });
}
/// Gets the current line.
@@ -776,20 +747,24 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, NULL, err);
}
-/// Echo a message.
+/// Prints a message given by a list of `[text, hl_group]` "chunks".
///
-/// @param chunks A list of `[text, hl_group]` arrays, each representing a
-/// text chunk with specified highlight group name or ID.
-/// `hl_group` element can be omitted for no highlight.
+/// Example:
+/// ```lua
+/// vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {})
+/// ```
+///
+/// @param chunks List of `[text, hl_group]` pairs, where each is a `text` string highlighted by
+/// the (optional) name or ID `hl_group`.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters.
-/// - verbose: Message is printed as a result of 'verbose' option.
-/// If Nvim was invoked with -V3log_file, the message will be
-/// redirected to the log_file and suppressed from direct output.
+/// - err: Treat the message like `:echoerr`. Sets `hl_group` to |hl-ErrorMsg| by default.
+/// - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
+/// will write the message to the "log" file instead of standard output.
void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
FUNC_API_SINCE(7)
{
- HlMessage hl_msg = parse_hl_msg(chunks, err);
+ HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -798,7 +773,8 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
verbose_enter();
}
- msg_multihl(hl_msg, history ? "echomsg" : "echo", history);
+ char *kind = opts->verbose ? NULL : opts->err ? "echoerr" : history ? "echomsg" : "echo";
+ msg_multihl(hl_msg, kind, history, opts->err);
if (opts->verbose) {
verbose_leave();
@@ -814,37 +790,6 @@ error:
hl_msg_free(hl_msg);
}
-/// Writes a message to the Vim output buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_out_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, false, false);
-}
-
-/// Writes a message to the Vim error buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_err_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, false);
-}
-
-/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
-/// flushed (and displayed).
-///
-/// @param str Message
-/// @see nvim_err_write()
-void nvim_err_writeln(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, true);
-}
-
/// Gets the current list of buffer handles
///
/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
@@ -892,19 +837,9 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
return;
}
- if (curwin->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to buffer %d",
- buffer);
- }
+ TRY_WRAP(err, {
+ do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ });
}
/// Gets the current list of window handles.
@@ -951,14 +886,9 @@ void nvim_set_current_win(Window window, Error *err)
return;
}
- try_start();
- goto_tabpage_win(win_find_tabpage(win), win);
- if (!try_end(err) && win != curwin) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- window);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_win(win_find_tabpage(win), win);
+ });
}
/// Creates a new, empty, unnamed buffer.
@@ -973,74 +903,76 @@ void nvim_set_current_win(Window window, Error *err)
Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
FUNC_API_SINCE(6)
{
- try_start();
- // Block autocommands for now so they don't mess with the buffer before we
- // finish configuring it.
- block_autocmds();
-
- buf_T *buf = buflist_new(NULL, NULL, 0,
- BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
- if (buf == NULL) {
- unblock_autocmds();
- goto fail;
- }
+ Buffer ret = 0;
- // Open the memline for the buffer. This will avoid spurious autocmds when
- // a later nvim_buf_set_lines call would have needed to "open" the buffer.
- if (ml_open(buf) == FAIL) {
- unblock_autocmds();
- goto fail;
- }
-
- // Set last_changedtick to avoid triggering a TextChanged autocommand right
- // after it was added.
- buf->b_last_changedtick = buf_get_changedtick(buf);
- buf->b_last_changedtick_i = buf_get_changedtick(buf);
- buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+ TRY_WRAP(err, {
+ // Block autocommands for now so they don't mess with the buffer before we
+ // finish configuring it.
+ block_autocmds();
+
+ buf_T *buf = buflist_new(NULL, NULL, 0,
+ BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
+ if (buf == NULL) {
+ unblock_autocmds();
+ goto fail;
+ }
- // Only strictly needed for scratch, but could just as well be consistent
- // and do this now. Buffer is created NOW, not when it later first happens
- // to reach a window or aucmd_prepbuf() ..
- buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+ // Open the memline for the buffer. This will avoid spurious autocmds when
+ // a later nvim_buf_set_lines call would have needed to "open" the buffer.
+ if (ml_open(buf) == FAIL) {
+ unblock_autocmds();
+ goto fail;
+ }
- if (scratch) {
- set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
- buf->b_p_swf = false;
- buf->b_p_ml = false;
- }
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ buf->b_last_changedtick = buf_get_changedtick(buf);
+ buf->b_last_changedtick_i = buf_get_changedtick(buf);
+ buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+
+ // Only strictly needed for scratch, but could just as well be consistent
+ // and do this now. Buffer is created NOW, not when it later first happens
+ // to reach a window or aucmd_prepbuf() ..
+ buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+
+ if (scratch) {
+ set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
+ buf->b_p_swf = false;
+ buf->b_p_ml = false;
+ }
- unblock_autocmds();
+ unblock_autocmds();
- bufref_T bufref;
- set_bufref(&bufref, buf);
- if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
- if (listed
- && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
+ if (listed
+ && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
- try_end(err);
- return buf->b_fnum;
+ ret = buf->b_fnum;
+ fail:;
+ });
-fail:
- if (!try_end(err)) {
+ if (ret == 0 && !ERROR_SET(err)) {
api_set_error(err, kErrorTypeException, "Failed to create buffer");
}
- return 0;
+ return ret;
}
/// 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
+/// connected to an external process. Instead, input sent on the channel
/// will be echoed directly by the terminal. This is useful to display
/// ANSI terminal sequences returned as part of a rpc message, or similar.
///
@@ -1051,6 +983,19 @@ fail:
/// Then |nvim_chan_send()| can be called immediately to process sequences
/// in a virtual terminal having the intended size.
///
+/// Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you
+/// can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]()
+/// [terminal-scrollback-pager]()
+///
+/// ```lua
+/// vim.api.nvim_create_user_command('TermHl', function()
+/// local b = vim.api.nvim_create_buf(false, true)
+/// local chan = vim.api.nvim_open_term(b, {})
+/// vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+/// vim.api.nvim_win_set_buf(0, b)
+/// end, { desc = 'Highlights ANSI termcodes in curbuf' })
+/// ```
+///
/// @param buffer the buffer to use (expected to be empty)
/// @param opts Optional parameters.
/// - on_input: Lua callback for input sent, i e keypresses in terminal
@@ -1205,14 +1150,9 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
return;
}
- try_start();
- goto_tabpage_tp(tp, true, true);
- if (!try_end(err) && tp != curtab) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to tabpage %d",
- tabpage);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_tp(tp, true, true);
+ });
}
/// Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call
@@ -1545,20 +1485,17 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
return rv;
}
-/// Self-identifies the client.
+/// Self-identifies the client. Sets the `client` object returned by |nvim_get_chan_info()|.
///
-/// The client/plugin/application should call this after connecting, to provide
-/// hints about its identity and purpose, for debugging and orchestration.
+/// Clients should call this just after connecting, to provide hints for debugging and
+/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
+/// `name`.)
///
-/// Can be called more than once; the caller should merge old info if
-/// appropriate. Example: library first identifies the channel, then a plugin
-/// using that library later identifies itself.
-///
-/// @note "Something is better than nothing". You don't need to include all the
-/// fields.
+/// Can be called more than once; the caller should merge old info if appropriate. Example: library
+/// first identifies the channel, then a plugin using that library later identifies itself.
///
/// @param channel_id
-/// @param name Short name for the connected client
+/// @param name Client short-name. Sets the `client.name` field of |nvim_get_chan_info()|.
/// @param version Dict describing the version, with these
/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
@@ -1632,6 +1569,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// Gets information about a channel.
///
+/// See |nvim_list_uis()| for an example of how to get channel info.
+///
/// @param chan channel_id, or 0 for current channel
/// @returns Channel info dict with these keys:
/// - "id" Channel id.
@@ -1649,8 +1588,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g.
/// for conpty on Windows).
/// - "buffer" (optional) Buffer connected to |terminal| instance.
-/// - "client" (optional) Info about the peer (client on the other end of the RPC channel),
-/// which it provided via |nvim_set_client_info()|.
+/// - "client" (optional) Info about the peer (client on the other end of the channel), as set
+/// by |nvim_set_client_info()|.
///
Dict nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err)
FUNC_API_SINCE(4)
@@ -1676,55 +1615,6 @@ Array nvim_list_chans(Arena *arena)
return channel_all_info(arena);
}
-/// Writes a message to vim output or error buffer. The string is split
-/// and flushed after each newline. Incomplete lines are kept for writing
-/// later.
-///
-/// @param message Message to write
-/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
-/// @param writeln Append a trailing newline
-static void write_msg(String message, bool to_err, bool writeln)
-{
- static StringBuilder out_line_buf = KV_INITIAL_VALUE;
- static StringBuilder err_line_buf = KV_INITIAL_VALUE;
- StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
-
-#define PUSH_CHAR(c) \
- if (kv_max(*line_buf) == 0) { \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } \
- if (c == NL) { \
- kv_push(*line_buf, NUL); \
- if (to_err) { \
- emsg(line_buf->items); \
- } else { \
- msg(line_buf->items, 0); \
- } \
- if (msg_silent == 0) { \
- msg_didout = true; \
- } \
- kv_drop(*line_buf, kv_size(*line_buf)); \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } else if (c == NUL) { \
- kv_push(*line_buf, NL); \
- } else { \
- kv_push(*line_buf, c); \
- }
-
- no_wait_return++;
- for (uint32_t i = 0; i < message.size; i++) {
- if (got_int) {
- break;
- }
- PUSH_CHAR(message.data[i]);
- }
- if (writeln) {
- PUSH_CHAR(NL);
- }
- no_wait_return--;
- msg_end();
-}
-
// Functions used for testing purposes
/// Returns object given as argument.
@@ -1796,6 +1686,14 @@ Dict nvim__stats(Arena *arena)
/// Gets a list of dictionaries representing attached UIs.
///
+/// Example: The Nvim builtin |TUI| sets its channel info as described in |startup-tui|. In
+/// particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by
+/// inspecting the client name of each UI:
+///
+/// ```lua
+/// vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name)
+/// ```
+///
/// @return Array of UI dictionaries, each with these keys:
/// - "height" Requested height of the UI
/// - "width" Requested width of the UI
@@ -2086,7 +1984,9 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err)
/// the "highlights" key in {opts} is true. Each element of the array is a
/// |Dict| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
-/// - group: (string) Name of highlight group.
+/// - group: (string) Name of highlight group. May be removed in the future, use
+/// `groups` instead.
+/// - groups: (array) Names of stacked highlight groups (highest priority last).
Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(8) FUNC_API_FAST
{
@@ -2138,6 +2038,7 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
});
int stc_hl_id = 0;
+ int scl_hl_id = 0;
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
@@ -2146,23 +2047,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
int cul_id = 0;
int num_id = 0;
linenr_T lnum = statuscol_lnum;
+ foldinfo_T cursorline_fi = { 0 };
decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
statuscol.sattrs = sattrs;
statuscol.foldinfo = fold_info(wp, lnum);
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ win_update_cursorline(wp, &cursorline_fi);
+ statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? cul_id : 0;
+ scl_hl_id = use_cursor_line_highlight(wp, lnum) ? HLF_CLS : HLF_SC;
- if (wp->w_p_cul) {
- if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) {
- wp->w_cursorline = statuscol.foldinfo.fi_lnum;
- }
- statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
- }
-
- statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
if (num_id) {
stc_hl_id = num_id;
- } else if (statuscol.use_cul) {
+ } else if (use_cursor_line_highlight(wp, lnum)) {
stc_hl_id = HLF_CLN;
} else if (wp->w_p_rnu) {
stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB);
@@ -2215,22 +2111,19 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
+ const char *dfltname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
+ opts->use_winbar, stc_hl_id);
if (hltab->start == NULL || (hltab->start - buf) != 0) {
- Dict hl_info = arena_dict(arena, 2);
- const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
- opts->use_winbar, stc_hl_id);
-
+ Dict hl_info = arena_dict(arena, 3);
PUT_C(hl_info, "start", INTEGER_OBJ(0));
- PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
-
+ PUT_C(hl_info, "group", CSTR_AS_OBJ(dfltname));
+ Array groups = arena_array(arena, 1);
+ ADD_C(groups, CSTR_AS_OBJ(dfltname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
- Dict hl_info = arena_dict(arena, 2);
-
- PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
-
const char *grpname;
if (sp->userhl == 0) {
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
@@ -2240,7 +2133,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = arena_memdupz(arena, user_group, strlen(user_group));
}
+
+ const char *combine = sp->item == STL_SIGNCOL ? syn_id2name(scl_hl_id)
+ : sp->item == STL_FOLDCOL ? grpname : dfltname;
+ Dict hl_info = arena_dict(arena, 3);
+ PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
+ Array groups = arena_array(arena, 1 + (combine != grpname));
+ if (combine != grpname) {
+ ADD_C(groups, CSTR_AS_OBJ(combine));
+ }
+ ADD_C(groups, CSTR_AS_OBJ(grpname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
PUT_C(result, "highlights", ARRAY_OBJ(hl_values));
@@ -2270,9 +2174,13 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
/// @return Dict containing these keys:
/// - winid: (number) floating window id
/// - bufnr: (number) buffer id in floating window
-Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena)
+Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena, Error *err)
{
Dict rv = arena_dict(arena, 2);
+ if ((get_cot_flags() & kOptCotFlagPopup) == 0) {
+ api_set_error(err, kErrorTypeException, "completeopt option does not include popup");
+ return rv;
+ }
if (HAS_KEY(opts, complete_set, info)) {
win_T *wp = pum_set_info((int)index, opts->info.data);
if (wp) {
@@ -2389,13 +2297,23 @@ void nvim__redraw(Dict(redraw) *opts, Error *err)
"%s", "Invalid 'range': Expected 2-tuple of Integers", {
return;
});
- linenr_T first = (linenr_T)kv_A(opts->range, 0).data.integer + 1;
- linenr_T last = (linenr_T)kv_A(opts->range, 1).data.integer;
+ int64_t begin_raw = kv_A(opts->range, 0).data.integer;
+ int64_t end_raw = kv_A(opts->range, 1).data.integer;
+
buf_T *rbuf = win ? win->w_buffer : (buf ? buf : curbuf);
- if (last == -1) {
- last = rbuf->b_ml.ml_line_count;
+ linenr_T line_count = rbuf->b_ml.ml_line_count;
+
+ int begin = (int)MIN(begin_raw, line_count);
+ int end;
+ if (end_raw == -1) {
+ end = line_count;
+ } else {
+ end = (int)MIN(MAX(begin, end_raw), line_count);
+ }
+
+ if (begin < end) {
+ redraw_buf_range_later(rbuf, 1 + begin, end);
}
- redraw_buf_range_later(rbuf, first, last);
}
// Redraw later types require update_screen() so call implicitly unless set to false.
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 165cc93fbe..fc7e7e1a06 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
@@ -21,6 +22,7 @@
#include "nvim/garray_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/runtime.h"
#include "nvim/vim_defs.h"
#include "nvim/viml/parser/expressions.h"
@@ -78,24 +80,24 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
capture_ga = &capture_local;
}
- try_start();
- if (opts->output) {
- msg_silent++;
- msg_col = 0; // prevent leading spaces
- }
+ TRY_WRAP(err, {
+ if (opts->output) {
+ msg_silent++;
+ msg_col = 0; // prevent leading spaces
+ }
- const sctx_T save_current_sctx = api_set_sctx(channel_id);
+ const sctx_T save_current_sctx = api_set_sctx(channel_id);
- do_source_str(src.data, "nvim_exec2()");
- if (opts->output) {
- capture_ga = save_capture_ga;
- msg_silent = save_msg_silent;
- // Put msg_col back where it was, since nothing should have been written.
- msg_col = save_msg_col;
- }
+ do_source_str(src.data, "nvim_exec2()");
+ if (opts->output) {
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
+ }
- current_sctx = save_current_sctx;
- try_end(err);
+ current_sctx = save_current_sctx;
+ });
if (ERROR_SET(err)) {
goto theend;
@@ -125,19 +127,17 @@ theend:
///
/// On execution error: fails with Vimscript error, updates v:errmsg.
///
-/// Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate multiple lines of Vim script
-/// or an Ex command directly, use |nvim_exec2()|. To construct an Ex command using a structured
-/// format and then execute it, use |nvim_cmd()|. To modify an Ex command before evaluating it, use
-/// |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
+/// Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in a structured way
+/// before executing it, modify the result of |nvim_parse_cmd()| then pass it to |nvim_cmd()|.
///
/// @param command Ex command string
/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
{
- try_start();
- do_cmdline_cmd(command.data);
- try_end(err);
+ TRY_WRAP(err, {
+ do_cmdline_cmd(command.data);
+ });
}
/// Evaluates a Vimscript |expression|. Dicts and Lists are recursively expanded.
@@ -231,10 +231,9 @@ static Object _call_function(String fn, Array args, dict_T *self, Arena *arena,
funcexe.fe_selfdict = self;
TRY_WRAP(err, {
- // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
- // (see above) to capture abort-causing non-exception errors.
- call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
- vim_args, &funcexe);
+ // call_func() retval is deceptive, ignore it. Instead TRY_WRAP sets `msg_list` to capture
+ // abort-causing non-exception errors.
+ (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, vim_args, &funcexe);
});
if (!ERROR_SET(err)) {
@@ -282,20 +281,23 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena,
typval_T rettv;
bool mustfree = false;
switch (dict.type) {
- case kObjectTypeString:
- try_start();
- if (eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE) == FAIL) {
- api_set_error(err, kErrorTypeException,
- "Failed to evaluate dict expression");
- }
- clear_evalarg(&EVALARG_EVALUATE, NULL);
- if (try_end(err)) {
+ case kObjectTypeString: {
+ int eval_ret;
+ TRY_WRAP(err, {
+ eval_ret = eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE);
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+ });
+ if (ERROR_SET(err)) {
return rv;
}
+ if (eval_ret != OK) {
+ abort(); // Should not happen.
+ }
// Evaluation of the string arg created a new dict or increased the
// refcount of a dict. Not necessary for a RPC dict.
mustfree = true;
break;
+ }
case kObjectTypeDict:
object_to_vim(dict, &rettv, err);
break;
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 6f5a9a90c0..1132452faf 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdbool.h>
#include <string.h>
@@ -7,25 +8,22 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/tabpage.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
-#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
#include "nvim/eval/window.h"
-#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
@@ -33,7 +31,6 @@
#include "nvim/syntax.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
#include "nvim/ui_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -104,10 +101,12 @@
/// @param config Map defining the window configuration. Keys:
/// - relative: Sets the window layout to "floating", placed at (row,col)
/// coordinates relative to:
-/// - "editor" The global editor grid
-/// - "win" Window given by the `win` field, or current window.
-/// - "cursor" Cursor position in current window.
-/// - "mouse" Mouse position
+/// - "cursor" Cursor position in current window.
+/// - "editor" The global editor grid.
+/// - "laststatus" 'laststatus' if present, or last row.
+/// - "mouse" Mouse position.
+/// - "tabline" Tabline if present, or first row.
+/// - "win" Window given by the `win` field, or current window.
/// - win: |window-ID| window to split, or relative window when creating a
/// float (relative="win").
/// - anchor: Decides which corner of the float to place at (row,col):
@@ -702,7 +701,9 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
FUNC_API_SINCE(6)
{
/// Keep in sync with FloatRelative in buffer_defs.h
- static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
+ static const char *const float_relative_str[] = {
+ "editor", "win", "cursor", "mouse", "tabline", "laststatus"
+ };
/// Keep in sync with WinSplit in buffer_defs.h
static const char *const win_split_str[] = { "left", "right", "above", "below" };
@@ -808,6 +809,10 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = kFloatRelativeCursor;
} else if (striequal(str, "mouse")) {
*out = kFloatRelativeMouse;
+ } else if (striequal(str, "tabline")) {
+ *out = kFloatRelativeTabline;
+ } else if (striequal(str, "laststatus")) {
+ *out = kFloatRelativeLaststatus;
} else {
return false;
}
@@ -890,7 +895,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
*is_present = true;
}
-static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
+static bool parse_bordertext_pos(win_T *wp, String bordertext_pos, BorderTextType bordertext_type,
WinConfig *fconfig, Error *err)
{
AlignTextPos *align;
@@ -904,7 +909,9 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
}
if (bordertext_pos.size == 0) {
- *align = kAlignLeft;
+ if (!wp) {
+ *align = kAlignLeft;
+ }
return true;
}
@@ -1245,7 +1252,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'title_pos' same as empty string
- if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->title_pos, kBorderTextTitle, fconfig, err)) {
goto fail;
}
} else {
@@ -1272,7 +1279,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'footer_pos' same as empty string
- if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->footer_pos, kBorderTextFooter, fconfig, err)) {
goto fail;
}
} else {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 5a4972ef23..d968af421d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -1,4 +1,3 @@
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,7 +27,7 @@
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/window.c.generated.h"
+# include "api/window.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the current buffer in a window
@@ -63,11 +62,6 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
return;
}
- if (win->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return;
@@ -187,14 +181,9 @@ void nvim_win_set_height(Window window, Integer height, Error *err)
return;
}
- if (height > INT_MAX || height < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Height value outside range");
- return;
- }
-
- try_start();
- win_setheight_win((int)height, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setheight_win((int)height, win);
+ });
}
/// Gets the window width
@@ -229,14 +218,9 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
return;
}
- if (width > INT_MAX || width < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Width value outside range");
- return;
- }
-
- try_start();
- win_setwidth_win((int)width, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setwidth_win((int)width, win);
+ });
}
/// Gets a window-scoped (w:) variable
@@ -383,19 +367,16 @@ void nvim_win_hide(Window window, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
-
- // Never close the autocommand window.
- if (is_aucmd_win(win)) {
- emsg(_(e_autocmd_close));
- } else if (tabpage == curtab) {
- win_close(win, false, false);
- } else {
- win_close_othertab(win, false, tabpage);
- }
-
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ // Never close the autocommand window.
+ if (is_aucmd_win(win)) {
+ emsg(_(e_autocmd_close));
+ } else if (tabpage == curtab) {
+ win_close(win, false, false);
+ } else {
+ win_close_othertab(win, false, tabpage);
+ }
+ });
}
/// Closes the window (like |:close| with a |window-ID|).
@@ -415,10 +396,9 @@ void nvim_win_close(Window window, Boolean force, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
- ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
+ });
}
/// Calls a function with window as temporary current window.
@@ -441,15 +421,15 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- try_start();
Object res = OBJECT_INIT;
- win_execute_T win_execute_args;
- if (win_execute_before(&win_execute_args, win, tabpage)) {
- Array args = ARRAY_DICT_INIT;
- res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
- }
- win_execute_after(&win_execute_args);
- try_end(err);
+ TRY_WRAP(err, {
+ win_execute_T win_execute_args;
+ if (win_execute_before(&win_execute_args, win, tabpage)) {
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ }
+ win_execute_after(&win_execute_args);
+ });
return res;
}
@@ -476,6 +456,7 @@ void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
}
win->w_ns_hl = (NS)ns_id;
+ win->w_ns_hl_winhl = -1;
win->w_hl_needs_update = true;
redraw_later(win, UPD_NOT_VALID);
}