aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/vim.c
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/vim.c
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.gz
rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.bz2
rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
Diffstat (limited to 'src/nvim/api/vim.c')
-rw-r--r--src/nvim/api/vim.c416
1 files changed, 167 insertions, 249 deletions
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.