From 9b8907d90508d7b66f025bbd1f5a48a78c5ce035 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 20 Oct 2024 22:18:26 +0800 Subject: feat(float): allow enabling mouse for non-focusable window (#30844) Problem: Cannot allow mouse interaction for non-focusable float window. Solution: Add a "mouse" field to float window config. --- src/nvim/api/keysets_defs.h | 1 + src/nvim/api/ui_events.in.h | 2 +- src/nvim/api/win_config.c | 13 ++++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 552612dd13..96aabb851f 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -119,6 +119,7 @@ typedef struct { Array bufpos; Boolean external; Boolean focusable; + Boolean mouse; Boolean vertical; Integer zindex; Object border; diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 2bd8792d71..865e84ab91 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -102,7 +102,7 @@ void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integ Integer height) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row, - Float anchor_col, Boolean focusable, Integer zindex) + Float anchor_col, Boolean mouse_enabled, Integer zindex) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_external_pos(Integer grid, Window win) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index f63fdc5381..16811e0cd9 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -129,7 +129,12 @@ /// fractional. /// - focusable: Enable focus by user actions (wincmds, mouse events). /// Defaults to true. Non-focusable windows can be entered by -/// |nvim_set_current_win()|. +/// |nvim_set_current_win()|, or, when the `mouse` field is set to true, +/// by mouse events. +/// - mouse: Specify how this window interacts with mouse events. +/// Defaults to `focusable` value. +/// - If false, mouse events pass through this window. +/// - If true, mouse events interact with this window normally. /// - external: GUI should display the window as an external /// top-level window. Currently accepts no other positioning /// configuration together with this. @@ -714,6 +719,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err) PUT_KEY_X(rv, focusable, config->focusable); PUT_KEY_X(rv, external, config->external); PUT_KEY_X(rv, hide, config->hide); + PUT_KEY_X(rv, mouse, config->mouse); if (wp->w_floating) { PUT_KEY_X(rv, width, config->width); @@ -1202,6 +1208,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (HAS_KEY_X(config, focusable)) { fconfig->focusable = config->focusable; + fconfig->mouse = config->focusable; + } + + if (HAS_KEY_X(config, mouse)) { + fconfig->mouse = config->mouse; } if (HAS_KEY_X(config, zindex)) { -- cgit From cbd8b2c1622e0c1cb4d38b65730e259eb6c100df Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 1 Nov 2024 17:34:19 +0800 Subject: vim-patch:9.1.0824: too many strlen() calls in register.c (#31022) Problem: too many strlen() calls in register.c Solution: refactor code, add string_T struct to keep track of string lengths (John Marriott) closes: vim/vim#15952 https://github.com/vim/vim/commit/79f6ffd388299ef3b1c95cbe658785e6e66df144 Co-authored-by: John Marriott --- src/nvim/api/private/helpers.h | 6 ++++++ src/nvim/api/vim.c | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 57932e067e..d06f5c9c65 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -111,6 +111,12 @@ typedef kvec_withinit_t(Object, 16) ArrayBuilder; #define STATIC_CSTR_AS_OBJ(s) STRING_OBJ(STATIC_CSTR_AS_STRING(s)) #define STATIC_CSTR_TO_OBJ(s) STRING_OBJ(STATIC_CSTR_TO_STRING(s)) +#define API_CLEAR_STRING(s) \ + do { \ + XFREE_CLEAR(s.data); \ + s.size = 0; \ + } while (0) + // Helpers used by the generated msgpack-rpc api wrappers #define api_init_boolean #define api_init_integer diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 8c88a19147..943c67ac8e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1317,15 +1317,15 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, return; // Nothing to do. } - reg->y_array = arena_alloc(arena, lines.size * sizeof(uint8_t *), true); + reg->y_array = arena_alloc(arena, lines.size * sizeof(String), true); reg->y_size = lines.size; for (size_t i = 0; i < lines.size; i++) { VALIDATE_T("line", kObjectTypeString, lines.items[i].type, { return; }); String line = lines.items[i].data.string; - reg->y_array[i] = arena_memdupz(arena, line.data, line.size); - memchrsub(reg->y_array[i], NUL, NL, line.size); + reg->y_array[i] = copy_string(line, arena); + memchrsub(reg->y_array[i].data, NUL, NL, line.size); } finish_yankreg_from_object(reg, false); -- cgit From 86e54734bf9e05605d8b7146e7b0e79025138ba2 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Tue, 29 Oct 2024 03:21:33 +0600 Subject: refactor(options): remove `get_option_value_strict` Problem: `get_option_value_for` can perfectly replace `get_option_value_strict`, making the latter redundant. Solution: Remove `get_option_value_strict` --- src/nvim/api/deprecated.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 6376011106..5c0d9c0cea 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -633,6 +633,40 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object set_option_to(channel_id, win, kOptReqWin, name, value, err); } +/// Check if option has a value in the requested scope. +/// +/// @param opt_idx Option index in options[] table. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// +/// @return true if option has a value in the requested scope, false otherwise. +static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) +{ + if (opt_idx == kOptInvalid) { + return false; + } + + vimoption_T *opt = get_option(opt_idx); + + // Hidden option. + if (opt->var == NULL) { + return false; + } + // TTY option. + if (is_tty_option(opt->fullname)) { + return req_scope == kOptReqGlobal; + } + + switch (req_scope) { + case kOptReqGlobal: + return opt->var != VAR_WIN; + case kOptReqBuf: + return opt->indir & PV_BUF; + case kOptReqWin: + return opt->indir & PV_WIN; + } + UNREACHABLE; +} + /// Gets the value of a global or local (buffer, window) option. /// /// @param[in] from Pointer to buffer or window for local option value. @@ -647,9 +681,15 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er return (Object)OBJECT_INIT; }); - OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err); - if (ERROR_SET(err)) { - return (Object)OBJECT_INIT; + OptIndex opt_idx = find_option(name.data); + OptVal value = NIL_OPTVAL; + + if (option_has_scope(opt_idx, req_scope)) { + value = get_option_value_for(opt_idx, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL, + req_scope, from, err); + if (ERROR_SET(err)) { + return (Object)OBJECT_INIT; + } } VALIDATE_S(value.type != kOptValTypeNil, "option name", name.data, { -- cgit From a27419f3fc540f66567f4559a796cd6758f1bb1f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 4 Nov 2024 19:00:12 +0600 Subject: feat(options)!: disallow setting hidden options #28400 Problem: There are three different ways of marking an option as hidden, `enable_if = false`, `hidden = true` and `immutable = true`. These also have different behaviors. Options hidden with `enable_if = false` can't have their value fetched using Vim script or the API, but options hidden with `hidden = true` or `immutable = true` can. On the other hand, options with `hidden = true` do not error when trying to set their value, but options with `immutable = true` do. Solution: Remove `enable_if = false`, remove the `hidden` property for options, and use `immutable = true` to mark an option as hidden instead. Also make hidden option variable pointers always point to the default value, which allows fetching the value of every hidden option using Vim script and the API. This does also mean that trying to set a hidden option will now give an error instead of just being ignored. --- src/nvim/api/deprecated.c | 4 ---- src/nvim/api/options.c | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 5c0d9c0cea..b3ba832fec 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -647,10 +647,6 @@ static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) vimoption_T *opt = get_option(opt_idx); - // Hidden option. - if (opt->var == NULL) { - return false; - } // TTY option. if (is_tty_option(opt->fullname)) { return req_scope == kOptReqGlobal; diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 1a0edd551e..96866d80ba 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -80,7 +80,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * *opt_idxp = find_option(name); int flags = get_option_attrs(*opt_idxp); if (flags == 0) { - // hidden or unknown option + // unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); } else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) { // if 'buf' or 'win' is passed, make sure the option supports it @@ -175,7 +175,6 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) } OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err); - bool hidden = is_option_hidden(opt_idx); if (ftbuf != NULL) { // restore curwin/curbuf and a few other things @@ -189,7 +188,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) goto err; } - VALIDATE_S(!hidden && value.type != kOptValTypeNil, "option", name.data, { + VALIDATE_S(value.type != kOptValTypeNil, "option", name.data, { goto err; }); -- cgit From 5cfa7a72f8c40cdcc0fa93693689915e913806f1 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 20 Feb 2024 17:25:57 +0100 Subject: refactor(message): propagate highlight id instead of attrs Problem: Highlight group id is not propagated to the end of the message call stack, where ext_messages are emitted. Solution: Refactor message functions to pass along highlight group id instead of attr id. --- src/nvim/api/private/helpers.c | 7 +++---- src/nvim/api/vim.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index e1fb4ed732..d21caf7ed0 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -824,16 +824,15 @@ HlMessage parse_hl_msg(Array chunks, Error *err) String str = copy_string(chunk.items[0].data.string, NULL); - int attr = 0; + int hl_id = 0; if (chunk.size == 2) { String hl = chunk.items[1].data.string; if (hl.size > 0) { // TODO(bfredl): use object_to_hl_id and allow integer - int hl_id = syn_check_group(hl.data, hl.size); - attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; + hl_id = syn_check_group(hl.data, hl.size); } } - kv_push(hl_msg, ((HlMessageChunk){ .text = str, .attr = attr })); + kv_push(hl_msg, ((HlMessageChunk){ .text = str, .hl_id = hl_id })); } return hl_msg; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 943c67ac8e..aacd7d754c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -796,7 +796,7 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err) verbose_enter(); } - msg_multiattr(hl_msg, history ? "echomsg" : "echo", history); + msg_multihl(hl_msg, history ? "echomsg" : "echo", history); if (opts->verbose) { verbose_leave(); -- cgit From 5a27d02584656f35d757c01f10a1d3c88910c519 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 9 Nov 2024 01:10:56 +0100 Subject: docs: misc (#30914) Co-authored-by: Ernie Rael Co-authored-by: Famiu Haque Co-authored-by: Jade Co-authored-by: glepnir Co-authored-by: zeertzjq --- src/nvim/api/vim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 943c67ac8e..97cda49a22 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -781,8 +781,8 @@ void nvim_set_vvar(String name, Object value, Error *err) /// can be omitted for no highlight. /// @param history if true, add to |message-history|. /// @param opts Optional parameters. -/// - verbose: Message was printed as a result of 'verbose' option -/// if Nvim was invoked with -V3log_file, the message will be +/// - 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. void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err) FUNC_API_SINCE(7) -- cgit From ff7518b83cb270f8fcaded19bf640cf4bdfb0ff0 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 11 Nov 2024 13:06:37 +0100 Subject: refactor(highlight): make enum of builtin highlights start with 1 This makes it possible to use HLF_ values directly as highlight id:s and avoids +1 adjustments especially around messages. --- src/nvim/api/vim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ee05ad28ac..998f911392 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2161,11 +2161,11 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, if (num_id) { stc_hl_id = num_id; } else if (statuscol.use_cul) { - stc_hl_id = HLF_CLN + 1; + stc_hl_id = HLF_CLN; } else if (wp->w_p_rnu) { - stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1; + stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB); } else { - stc_hl_id = HLF_N + 1; + stc_hl_id = HLF_N; } set_vim_var_nr(VV_LNUM, lnum); -- cgit From de48fbbd5f8800bd7f1909a6fb41e53e871cf74c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 20 Jun 2024 14:48:06 +0200 Subject: fix(messages)!: vim.ui_attach message callbacks are unsafe Problem: Lua callbacks for "msg_show" events with vim.ui_attach() are executed when it is not safe. Solution: Disallow non-fast API calls for "msg_show" event callbacks. Automatically detach callback after excessive errors. Make sure fast APIs do not modify Nvim state. --- src/nvim/api/ui_events.in.h | 2 +- src/nvim/api/vim.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 865e84ab91..0ed208fc1a 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -159,7 +159,7 @@ void wildmenu_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void msg_show(String kind, Array content, Boolean replace_last) - FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY; void msg_clear(void) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void msg_showcmd(Array content) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 998f911392..e6ec88c5e8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -594,10 +594,12 @@ 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); } -- cgit From 29ded889579a9d590e8ea885a9a402ff4bae87be Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 17 Nov 2024 02:56:16 +0600 Subject: refactor(options): remove `.indir`, redesign option scopes #31066 Problem: The way option scopes currently work is inflexible and does not allow for nested option scopes or easily finding the value of an option at any arbitrary scope without having to do long handwritten switch-case statements like in `get_varp()`. `.indir` is also confusing and redundant since option indices for each scope can be autogenerated. Solution: Expand option scopes in such a way that an option can support any amount of scopes using a set of scope flags, similarly to how it's already done for option types. Also make options contain information about its index at each scope it supports. This allows for massively simplifying `get_varp()` and `get_varp_scope()` in the future by just using a struct for options at each scope. This would be done by creating a table that stores the offset of an option's variable at a scope by using the option's index at that scope as a key. This PR also autogenerates enums for option indices at each scope to remove the need for `.indir` entirely, and also to allow easily iterating over options all options that support any scope. Ref: #29314 --- src/nvim/api/deprecated.c | 60 ++++++++++++----------------------------------- src/nvim/api/options.c | 42 ++++++++++++++++----------------- src/nvim/api/vim.c | 8 +++---- 3 files changed, 39 insertions(+), 71 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index b3ba832fec..b38a7d4173 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -533,7 +533,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { - set_option_to(channel_id, NULL, kOptReqGlobal, name, value, err); + set_option_to(channel_id, NULL, kOptScopeGlobal, name, value, err); } /// Gets the global value of an option. @@ -546,7 +546,7 @@ Object nvim_get_option(String name, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(11) { - return get_option_from(NULL, kOptReqGlobal, name, err); + return get_option_from(NULL, kOptScopeGlobal, name, err); } /// Gets a buffer option value @@ -566,7 +566,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) return (Object)OBJECT_INIT; } - return get_option_from(buf, kOptReqBuf, name, err); + return get_option_from(buf, kOptScopeBuf, name, err); } /// Sets a buffer option value. Passing `nil` as value deletes the option (only @@ -588,7 +588,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object return; } - set_option_to(channel_id, buf, kOptReqBuf, name, value, err); + set_option_to(channel_id, buf, kOptScopeBuf, name, value, err); } /// Gets a window option value @@ -608,7 +608,7 @@ Object nvim_win_get_option(Window window, String name, Error *err) return (Object)OBJECT_INIT; } - return get_option_from(win, kOptReqWin, name, err); + return get_option_from(win, kOptScopeWin, name, err); } /// Sets a window option value. Passing `nil` as value deletes the option (only @@ -630,48 +630,18 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object return; } - set_option_to(channel_id, win, kOptReqWin, name, value, err); -} - -/// Check if option has a value in the requested scope. -/// -/// @param opt_idx Option index in options[] table. -/// @param req_scope Requested option scope. See OptReqScope in option.h. -/// -/// @return true if option has a value in the requested scope, false otherwise. -static bool option_has_scope(OptIndex opt_idx, OptReqScope req_scope) -{ - if (opt_idx == kOptInvalid) { - return false; - } - - vimoption_T *opt = get_option(opt_idx); - - // TTY option. - if (is_tty_option(opt->fullname)) { - return req_scope == kOptReqGlobal; - } - - switch (req_scope) { - case kOptReqGlobal: - return opt->var != VAR_WIN; - case kOptReqBuf: - return opt->indir & PV_BUF; - case kOptReqWin: - return opt->indir & PV_WIN; - } - UNREACHABLE; + set_option_to(channel_id, win, kOptScopeWin, name, value, err); } /// 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 OptReqScope in option.h. +/// @param req_scope Requested 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, OptReqScope req_scope, String name, Error *err) +static Object get_option_from(void *from, OptScope req_scope, String name, Error *err) { VALIDATE_S(name.size > 0, "option name", "", { return (Object)OBJECT_INIT; @@ -681,7 +651,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er OptVal value = NIL_OPTVAL; if (option_has_scope(opt_idx, req_scope)) { - value = get_option_value_for(opt_idx, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL, + value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL, req_scope, from, err); if (ERROR_SET(err)) { return (Object)OBJECT_INIT; @@ -698,11 +668,11 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er /// 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 OptReqScope in option.h. +/// @param req_scope Requested 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, OptReqScope req_scope, String name, +static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name, Object value, Error *err) { VALIDATE_S(name.size > 0, "option name", "", { @@ -725,12 +695,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, return; }); - int attrs = get_option_attrs(opt_idx); // For global-win-local options -> setlocal // For win-local options -> setglobal and setlocal (opt_flags == 0) - const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL)) - ? 0 - : (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL; + const int opt_flags + = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal)) + ? 0 + : ((req_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); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 96866d80ba..bbfd00f2ea 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -23,8 +23,8 @@ #endif static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp, - int *scope, OptReqScope *req_scope, void **from, - char **filetype, Error *err) + int *scope, OptScope *req_scope, void **from, char **filetype, + Error *err) { #define HAS_KEY_X(d, v) HAS_KEY(d, option, v) if (HAS_KEY_X(opts, scope)) { @@ -39,14 +39,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * } } - *req_scope = kOptReqGlobal; + *req_scope = kOptScopeGlobal; if (filetype != NULL && HAS_KEY_X(opts, filetype)) { *filetype = opts->filetype.data; } if (HAS_KEY_X(opts, win)) { - *req_scope = kOptReqWin; + *req_scope = kOptScopeWin; *from = find_window_by_handle(opts->win, err); if (ERROR_SET(err)) { return FAIL; @@ -59,7 +59,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * return FAIL; }); *scope = OPT_LOCAL; - *req_scope = kOptReqBuf; + *req_scope = kOptScopeBuf; *from = find_buffer_by_handle(opts->buf, err); if (ERROR_SET(err)) { return FAIL; @@ -78,18 +78,17 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * }); *opt_idxp = find_option(name); - int flags = get_option_attrs(*opt_idxp); - if (flags == 0) { + if (*opt_idxp == kOptInvalid) { // unknown option api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name); - } else if (*req_scope == kOptReqBuf || *req_scope == kOptReqWin) { + } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) { // if 'buf' or 'win' is passed, make sure the option supports it - int req_flags = *req_scope == kOptReqBuf ? SOPT_BUF : SOPT_WIN; - if (!(flags & req_flags)) { - char *tgt = *req_scope & kOptReqBuf ? "buf" : "win"; - char *global = flags & SOPT_GLOBAL ? "global " : ""; - char *req = flags & SOPT_BUF ? "buffer-local " - : flags & SOPT_WIN ? "window-local " : ""; + if (!option_has_scope(*opt_idxp, *req_scope)) { + char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win"; + char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : ""; + char *req = option_has_scope(*opt_idxp, kOptScopeBuf) + ? "buffer-local " + : (option_has_scope(*opt_idxp, kOptScopeWin) ? "window-local " : ""); api_set_error(err, kErrorTypeValidation, "'%s' cannot be passed for %s%soption '%s'", tgt, global, req, name); @@ -153,7 +152,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *from = NULL; char *filetype = NULL; @@ -218,7 +217,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *to = NULL; if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) { return; @@ -230,9 +229,8 @@ 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 == kOptReqWin && scope == 0) { - int flags = get_option_attrs(opt_idx); - if (flags & SOPT_GLOBAL) { + if (req_scope == kOptScopeWin && scope == 0) { + if (option_has_scope(opt_idx, kOptScopeGlobal)) { scope = OPT_LOCAL; } } @@ -305,15 +303,15 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error { OptIndex opt_idx = 0; int scope = 0; - OptReqScope req_scope = kOptReqGlobal; + OptScope req_scope = kOptScopeGlobal; void *from = NULL; if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL, err)) { return (Dict)ARRAY_DICT_INIT; } - buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf; - win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin; + buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf; + win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin; return get_vimoption(name, scope, buf, win, arena, err); } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e6ec88c5e8..199b9a9690 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1004,10 +1004,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf, - buf); - set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf, - buf); + 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; -- cgit From f85bc41c800d7f5c0256f29aa347a53600a7c8d5 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 00:32:36 +0100 Subject: feat(ui): don't show unfocusable windows in :tabs, 'tabline' #27984 Problem: Floating windows with focusable set to false can reasonably be expected to be UI elements but are listed in some outputs that should contain only regular windows. Solution: Hide unfocusable floating windows from the default tabline and :tabs. --- src/nvim/api/win_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 16811e0cd9..6f5a9a90c0 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -130,7 +130,7 @@ /// - focusable: Enable focus by user actions (wincmds, mouse events). /// Defaults to true. Non-focusable windows can be entered by /// |nvim_set_current_win()|, or, when the `mouse` field is set to true, -/// by mouse events. +/// by mouse events. See |focusable|. /// - mouse: Specify how this window interacts with mouse events. /// Defaults to `focusable` value. /// - If false, mouse events pass through this window. -- cgit From 9c718bc2bce53b5be45061bff940f99e50c8bfcb Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 00:36:11 +0100 Subject: fix(api): validation, documentation of hl_group #31195 Problem: Documentation for "hl_group" in nvim_buf_set_extmark() is unclear. "hl_group" in nvim_echo() does not accept highlight group id. Solution: Move documentation for highlight group name/id to first mention of hl_group. Update nvim_echo() to accept highlight group id. --- src/nvim/api/extmark.c | 24 ++++++++++-------------- src/nvim/api/private/helpers.c | 23 +++++++++-------------- src/nvim/api/vim.c | 4 ++-- 3 files changed, 21 insertions(+), 30 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 7786c30624..c94b8df9ea 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -381,8 +381,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - id : id of the extmark to edit. /// - end_row : ending line of the mark, 0-based inclusive. /// - end_col : ending col of the mark, 0-based exclusive. -/// - hl_group : name of the highlight group used to highlight -/// this mark. +/// - 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()|. /// - 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 @@ -392,9 +393,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// text chunk with specified highlight. `highlight` element /// can either be a single highlight group, or an array of /// multiple highlight groups that will be stacked -/// (highest priority last). A highlight group can be supplied -/// either as a string or as an integer, the latter which -/// can be obtained using |nvim_get_hl_id_by_name()|. +/// (highest priority last). /// - virt_text_pos : position of virtual text. Possible values: /// - "eol": right after eol character (default). /// - "overlay": display over the specified column, without @@ -465,15 +464,12 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// buffer or end of the line respectively. Defaults to true. /// - sign_text: string of length 1-2 used to display in the /// sign column. -/// - sign_hl_group: name of the highlight group used to -/// highlight the sign column text. -/// - number_hl_group: name of the highlight group used to -/// highlight the number column. -/// - line_hl_group: name of the highlight group used to -/// highlight the whole line. -/// - cursorline_hl_group: name of the highlight group used to -/// highlight the sign column text when the cursor is on -/// the same line as the mark and 'cursorline' is enabled. +/// - sign_hl_group: highlight group used for the sign column text. +/// - number_hl_group: highlight group used for the number column. +/// - line_hl_group: highlight group used for the whole line. +/// - cursorline_hl_group: highlight group used for the sign +/// column text when the cursor is on the same line as the +/// mark and 'cursorline' is enabled. /// - conceal: string which should be either empty or a single /// character. Enable concealing similar to |:syn-conceal|. /// When a character is supplied it is used as |:syn-cchar|. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d21caf7ed0..88e1664c04 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -771,7 +771,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) int id = (int)obj.data.integer; return (1 <= id && id <= highlight_num_groups()) ? id : 0; } else { - api_set_error(err, kErrorTypeValidation, "Invalid highlight: %s", what); + api_set_error(err, kErrorTypeValidation, "Invalid hl_group: %s", what); return 0; } } @@ -809,27 +809,22 @@ HlMessage parse_hl_msg(Array chunks, Error *err) { HlMessage hl_msg = KV_INITIAL_VALUE; for (size_t i = 0; i < chunks.size; i++) { - if (chunks.items[i].type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); + VALIDATE_T("chunk", kObjectTypeArray, chunks.items[i].type, { goto free_exit; - } + }); Array chunk = chunks.items[i].data.array; - if (chunk.size == 0 || chunk.size > 2 - || chunk.items[0].type != kObjectTypeString - || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { - api_set_error(err, kErrorTypeValidation, - "Chunk is not an array with one or two strings"); + VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString), + "%s", "Invalid chunk: expected Array with 1 or 2 Strings", { goto free_exit; - } + }); String str = copy_string(chunk.items[0].data.string, NULL); int hl_id = 0; if (chunk.size == 2) { - String hl = chunk.items[1].data.string; - if (hl.size > 0) { - // TODO(bfredl): use object_to_hl_id and allow integer - hl_id = syn_check_group(hl.data, hl.size); + hl_id = object_to_hl_id(chunk.items[1], "text highlight", err); + if (ERROR_SET(err)) { + goto free_exit; } } kv_push(hl_msg, ((HlMessageChunk){ .text = str, .hl_id = hl_id })); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 199b9a9690..89e974c098 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -779,8 +779,8 @@ void nvim_set_vvar(String name, Object value, Error *err) /// Echo a message. /// /// @param chunks A list of `[text, hl_group]` arrays, each representing a -/// text chunk with specified highlight. `hl_group` element -/// can be omitted for no highlight. +/// text chunk with specified highlight group name or ID. +/// `hl_group` element can be omitted for no highlight. /// @param history if true, add to |message-history|. /// @param opts Optional parameters. /// - verbose: Message is printed as a result of 'verbose' option. -- cgit From 6ea45031d5841d3227c545f213d0903b951e40be Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 15:06:44 +0100 Subject: fix(api): nvim_echo free text memory with invalid highlight (#31243) Fix regression from #31195 --- src/nvim/api/private/helpers.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 88e1664c04..8ddaecc58e 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -823,9 +823,6 @@ HlMessage parse_hl_msg(Array chunks, Error *err) int hl_id = 0; if (chunk.size == 2) { hl_id = object_to_hl_id(chunk.items[1], "text highlight", err); - if (ERROR_SET(err)) { - goto free_exit; - } } kv_push(hl_msg, ((HlMessageChunk){ .text = str, .hl_id = hl_id })); } -- cgit From 235cb5bc5f2553f5a46807267b8705e53f7a14af Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Sun, 17 Nov 2024 20:43:36 +0100 Subject: fix(api): update "range" windows in nvim__redraw #31042 Problem: nvim__redraw's "range" marks a buffer range for redraw, and subsequently flushes the UI without updating the windows containing that buffer. Solution: Implicitly update the screen, unless specified otherwise. Only update the screen with the last call of the treesitter on_changedtree() callback. --- src/nvim/api/vim.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 89e974c098..0d562f2276 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2396,13 +2396,13 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) last = rbuf->b_ml.ml_line_count; } redraw_buf_range_later(rbuf, first, last); + opts->flush = HAS_KEY(opts, redraw, flush) ? opts->flush : true; } - bool flush = opts->flush; if (opts->tabline) { // Flush later in case tabline was just hidden or shown for the first time. if (redraw_tabline && firstwin->w_lines_valid == 0) { - flush = true; + opts->flush = true; } else { draw_tabline(); } @@ -2416,23 +2416,23 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) if (win == NULL) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (buf == NULL || wp->w_buffer == buf) { - redraw_status(wp, opts, &flush); + redraw_status(wp, opts, &opts->flush); } } } else { - redraw_status(win, opts, &flush); + redraw_status(win, opts, &opts->flush); } } win_T *cwin = win ? win : curwin; // Allow moving cursor to recently opened window and make sure it is drawn #28868. if (opts->cursor && (!cwin->w_grid.target || !cwin->w_grid.target->valid)) { - flush = true; + opts->flush = true; } // Redraw pending screen updates when explicitly requested or when determined // that it is necessary to properly draw other requested components. - if (flush && !cmdpreview) { + if (opts->flush && !cmdpreview) { update_screen(); } -- cgit From 40347f6e27f1594797ecb0b268076a94630e4c1b Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Mon, 18 Nov 2024 15:35:21 +0100 Subject: fix(api): only flush nvim__redraw when necessary #31250 Problem: Not possible to only set a "redraw later" type with nvim__redraw, which seems to be desired for the treesitter highlighter. Solution: Do not update the screen when "flush" is explicitly set to false and only redraw later types are present. In that case, do not call ui_flush() either. --- src/nvim/api/vim.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/nvim/api') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 0d562f2276..83f9aa573d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2396,9 +2396,16 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) last = rbuf->b_ml.ml_line_count; } redraw_buf_range_later(rbuf, first, last); + } + + // Redraw later types require update_screen() so call implicitly unless set to false. + if (HAS_KEY(opts, redraw, valid) || HAS_KEY(opts, redraw, range)) { opts->flush = HAS_KEY(opts, redraw, flush) ? opts->flush : true; } + // When explicitly set to false and only "redraw later" types are present, + // don't call ui_flush() either. + bool flush_ui = opts->flush; if (opts->tabline) { // Flush later in case tabline was just hidden or shown for the first time. if (redraw_tabline && firstwin->w_lines_valid == 0) { @@ -2406,6 +2413,7 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) } else { draw_tabline(); } + flush_ui = true; } bool save_lz = p_lz; @@ -2422,6 +2430,7 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) } else { redraw_status(win, opts, &opts->flush); } + flush_ui = true; } win_T *cwin = win ? win : curwin; @@ -2438,9 +2447,12 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) if (opts->cursor) { setcursor_mayforce(cwin, true); + flush_ui = true; } - ui_flush(); + if (flush_ui) { + ui_flush(); + } RedrawingDisabled = save_rd; p_lz = save_lz; -- cgit From e2ad251c8d01726ecd54d925b5280ab32b448c13 Mon Sep 17 00:00:00 2001 From: altermo <107814000+altermo@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:40:51 +0100 Subject: fix(api): nvim_get_option_value does not clean up on FileType error #31219 Problem: If there's an error in `FileType` autocmd, the filetype get-opt buffer doesn't get cleaned up. Solution: Call `aucmd_restbuf`. --- src/nvim/api/options.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nvim/api') diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index bbfd00f2ea..3289daeb6f 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -165,6 +165,14 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) buf_T *ftbuf = do_ft_buf(filetype, &aco, err); if (ERROR_SET(err)) { + if (ftbuf != NULL) { + // restore curwin/curbuf and a few other things + aucmd_restbuf(&aco); + + assert(curbuf != ftbuf); // safety check + wipe_buffer(ftbuf, false); + } + return (Object)OBJECT_INIT; } -- cgit