diff options
Diffstat (limited to 'src/nvim/api')
-rw-r--r-- | src/nvim/api/autocmd.c | 10 | ||||
-rw-r--r-- | src/nvim/api/buffer.c | 28 | ||||
-rw-r--r-- | src/nvim/api/command.c | 40 | ||||
-rw-r--r-- | src/nvim/api/deprecated.c | 12 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 191 | ||||
-rw-r--r-- | src/nvim/api/extmark.h | 15 | ||||
-rw-r--r-- | src/nvim/api/keysets_defs.h | 53 | ||||
-rw-r--r-- | src/nvim/api/options.c | 23 | ||||
-rw-r--r-- | src/nvim/api/private/converter.c | 43 | ||||
-rw-r--r-- | src/nvim/api/private/defs.h | 24 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 86 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 10 | ||||
-rw-r--r-- | src/nvim/api/private/validate.c | 2 | ||||
-rw-r--r-- | src/nvim/api/private/validate.h | 8 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 52 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 233 | ||||
-rw-r--r-- | src/nvim/api/vimscript.c | 48 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 101 | ||||
-rw-r--r-- | src/nvim/api/window.c | 8 |
19 files changed, 537 insertions, 450 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index ca8367b7ce..22932fd1a2 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -67,7 +67,7 @@ static int64_t next_autocmd_id = 1; /// NOTE: When multiple patterns or events are provided, it will find all the autocommands that /// match any combination of them. /// -/// @param opts Dictionary with at least one of the following: +/// @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|. @@ -270,7 +270,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err) } } - Dictionary autocmd_info = arena_dict(arena, 11); + Dict autocmd_info = arena_dict(arena, 11); if (ap->group != AUGROUP_DEFAULT) { PUT_C(autocmd_info, "group", INTEGER_OBJ(ap->group)); @@ -334,7 +334,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err) // 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, DICTIONARY_OBJ(autocmd_info)); + kvi_push(autocmd_list, DICT_OBJ(autocmd_info)); } } @@ -621,7 +621,7 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Arena *arena, Error *err) /// ``` /// /// @param name String: The name of the group -/// @param opts Dictionary Parameters +/// @param opts Dict Parameters /// - clear (bool) optional: defaults to true. Clear existing /// commands if the group already exists |autocmd-groups|. /// @return Integer id of the created group. @@ -686,7 +686,7 @@ void nvim_del_augroup_by_name(String name, Error *err) /// Execute all autocommands for {event} that match the corresponding /// {opts} |autocmd-execute|. /// @param event (String|Array) The event or events to execute -/// @param opts Dictionary of autocommand options: +/// @param opts Dict of autocommand options: /// - group (string|integer) optional: the autocommand group name or /// id to match against. |autocmd-groups|. /// - pattern (string|array) optional: defaults to "*" |autocmd-pattern|. Cannot be used diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7e64808709..9480292d9a 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -462,12 +462,8 @@ end: /// = row` and `start_col = end_col = col`. To delete the text in a range, use /// `replacement = {}`. /// -/// Prefer |nvim_buf_set_lines()| if you are only adding or deleting entire lines. -/// -/// Prefer |nvim_put()| if you want to insert text at the cursor position. -/// -/// @see |nvim_buf_set_lines()| -/// @see |nvim_put()| +/// @note Prefer |nvim_buf_set_lines()| (for performance) to add or delete entire lines. +/// @note Prefer |nvim_paste()| or |nvim_put()| to insert (instead of replace) text at cursor. /// /// @param channel_id /// @param buffer Buffer handle, or 0 for current buffer @@ -866,7 +862,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @param[out] err Error details, if any /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key holds the associated buffer handle. -ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Arena *arena, Error *err) +ArrayOf(Dict) nvim_buf_get_keymap(Buffer buffer, String mode, Arena *arena, Error *err) FUNC_API_SINCE(3) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1183,12 +1179,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, return rv; } -/// call a function with buffer as temporary current buffer +/// Call a function with buffer as temporary current buffer. /// /// This temporarily switches current buffer to "buffer". -/// If the current window already shows "buffer", the window is not switched +/// If the current window already shows "buffer", the window is not switched. /// If a window inside the current tabpage (including a float) already shows the -/// buffer One of these windows will be set as current window temporarily. +/// buffer, then one of these windows will be set as current window temporarily. /// Otherwise a temporary scratch window (called the "autocmd window" for /// historical reasons) will be used. /// @@ -1221,14 +1217,14 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) } /// @nodoc -Dictionary nvim__buf_stats(Buffer buffer, Arena *arena, Error *err) +Dict nvim__buf_stats(Buffer buffer, Arena *arena, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } - Dictionary rv = arena_dict(arena, 7); + Dict rv = arena_dict(arena, 7); // Number of times the cached line was flushed. // This should generally not increase while editing the same // line in the same mode. @@ -1375,7 +1371,7 @@ static inline void init_line_array(lua_State *lstate, Array *a, size_t size, Are /// @param s String to push /// @param len Size of string /// @param idx 0-based index to place s (only used for Lua) -/// @param replace_nl Replace newlines ('\n') with null ('\0') +/// @param replace_nl Replace newlines ('\n') with null (NUL) static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx, bool replace_nl, Arena *arena) { @@ -1384,7 +1380,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, if (s && replace_nl && strchr(s, '\n')) { // TODO(bfredl): could manage scratch space in the arena, for the NUL case char *tmp = xmemdupz(s, len); - strchrsub(tmp, '\n', '\0'); + strchrsub(tmp, '\n', NUL); lua_pushlstring(lstate, tmp, len); xfree(tmp); } else { @@ -1397,7 +1393,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, str = CBUF_TO_ARENA_STR(arena, s, len); if (replace_nl) { // Vim represents NULs as NLs, but this may confuse clients. - strchrsub(str.data, '\n', '\0'); + strchrsub(str.data, '\n', NUL); } } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 779e216c74..ab57d5c009 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -46,7 +46,7 @@ /// @param str Command line string to parse. Cannot contain "\n". /// @param opts Optional parameters. Reserved for future use. /// @param[out] err Error details, if any. -/// @return Dictionary containing command information, with these keys: +/// @return Dict containing command information, with these keys: /// - cmd: (string) Command name. /// - range: (array) (optional) Command range ([<line1>] [<line2>]). /// Omitted if command doesn't accept a range. @@ -63,13 +63,13 @@ /// - nargs: (string) Value of |:command-nargs|. /// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|. /// Empty if there isn't a next command. -/// - magic: (dictionary) Which characters have special meaning in the command arguments. +/// - magic: (dict) Which characters have special meaning in the command arguments. /// - file: (boolean) The command expands filenames. Which means characters such as "%", /// "#" and wildcards are expanded. /// - bar: (boolean) The "|" character is treated as a command separator and the double /// quote character (") is treated as the start of a comment. -/// - mods: (dictionary) |:command-modifiers|. -/// - filter: (dictionary) |:filter|. +/// - mods: (dict) |:command-modifiers|. +/// - filter: (dict) |:filter|. /// - pattern: (string) Filter pattern. Empty string if there is no filter. /// - force: (boolean) Whether filter is inverted or not. /// - silent: (boolean) |:silent|. @@ -193,7 +193,7 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err } else { nargs[0] = '0'; } - nargs[1] = '\0'; + nargs[1] = NUL; PUT_KEY(result, cmd, nargs, CSTR_TO_ARENA_OBJ(arena, nargs)); char *addr; @@ -230,12 +230,12 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd)); // TODO(bfredl): nested keydict would be nice.. - Dictionary mods = arena_dict(arena, 20); + Dict mods = arena_dict(arena, 20); - Dictionary filter = arena_dict(arena, 2); + Dict filter = arena_dict(arena, 2); PUT_C(filter, "pattern", CSTR_TO_ARENA_OBJ(arena, cmdinfo.cmdmod.cmod_filter_pat)); PUT_C(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force)); - PUT_C(mods, "filter", DICTIONARY_OBJ(filter)); + PUT_C(mods, "filter", DICT_OBJ(filter)); PUT_C(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT)); PUT_C(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT)); @@ -272,7 +272,7 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err PUT_KEY(result, cmd, mods, mods); - Dictionary magic = arena_dict(arena, 2); + Dict magic = arena_dict(arena, 2); PUT_C(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file)); PUT_C(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar)); PUT_KEY(result, cmd, magic, magic); @@ -284,7 +284,7 @@ end: /// Executes an Ex command. /// -/// Unlike |nvim_command()| this command takes a structured Dictionary instead of a String. This +/// Unlike |nvim_command()| this command takes a structured Dict instead of a String. This /// allows for easier construction and manipulation of an Ex command. This also allows for things /// such as having spaces inside a command argument, expanding filenames in a command that otherwise /// doesn't expand filenames, etc. Command arguments may also be Number, Boolean or String. @@ -298,7 +298,7 @@ end: /// @see |nvim_exec2()| /// @see |nvim_command()| /// -/// @param cmd Command to execute. Must be a Dictionary that can contain the same values as +/// @param cmd Command to execute. Must be a Dict that can contain the same values as /// the return value of |nvim_parse_cmd()| except "addr", "nargs" and "nextcmd" /// which are ignored if provided. All values except for "cmd" are optional. /// @param opts Optional parameters. @@ -391,7 +391,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena case kObjectTypeBoolean: data_str = arena_alloc(arena, 2, false); data_str[0] = elem.data.boolean ? '1' : '0'; - data_str[1] = '\0'; + data_str[1] = NUL; ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeBuffer: @@ -515,7 +515,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena if (HAS_KEY(cmd, cmd, magic)) { Dict(cmd_magic) magic[1] = KEYDICT_INIT; - if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) { + if (!api_dict_to_keydict(magic, DictHash(cmd_magic), cmd->magic, err)) { goto end; } @@ -533,14 +533,14 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena if (HAS_KEY(cmd, cmd, mods)) { Dict(cmd_mods) mods[1] = KEYDICT_INIT; - if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) { + if (!api_dict_to_keydict(mods, DictHash(cmd_mods), cmd->mods, err)) { goto end; } if (HAS_KEY(mods, cmd_mods, filter)) { Dict(cmd_mods_filter) filter[1] = KEYDICT_INIT; - if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field, + if (!api_dict_to_keydict(&filter, DictHash(cmd_mods_filter), mods->filter, err)) { goto end; } @@ -1166,7 +1166,7 @@ err: /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err) +Dict nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { return nvim_buf_get_commands(-1, opts, arena, err); @@ -1179,25 +1179,25 @@ Dictionary nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Arena *arena, Error *err) +Dict nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Arena *arena, Error *err) FUNC_API_SINCE(4) { bool global = (buffer == -1); if (ERROR_SET(err)) { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } if (global) { if (opts->builtin) { api_set_error(err, kErrorTypeValidation, "builtin=true not implemented"); - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } return commands_array(NULL, arena); } buf_T *buf = find_buffer_by_handle(buffer, err); if (opts->builtin || !buf) { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } return commands_array(buf, arena); } diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index af3bfe2c03..6376011106 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -170,7 +170,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID }; extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true, - false, false, false, false, NULL); + false, false, false, NULL); return src_id; } @@ -183,11 +183,11 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A /// @param[out] err Error details, if any /// @return Highlight definition map /// @see nvim_get_hl_by_name -Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Arena *arena, Error *err) +Dict nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Arena *arena, Error *err) FUNC_API_SINCE(3) FUNC_API_DEPRECATED_SINCE(9) { - Dictionary dic = ARRAY_DICT_INIT; + Dict dic = ARRAY_DICT_INIT; VALIDATE_INT((syn_get_final_id((int)hl_id) != 0), "highlight id", hl_id, { return dic; }); @@ -204,11 +204,11 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Arena *arena, Error *er /// @param[out] err Error details, if any /// @return Highlight definition map /// @see nvim_get_hl_by_id -Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *err) +Dict nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *err) FUNC_API_SINCE(3) FUNC_API_DEPRECATED_SINCE(9) { - Dictionary result = ARRAY_DICT_INIT; + Dict result = ARRAY_DICT_INIT; int id = syn_name2id(name.data); VALIDATE_S((id != 0), "highlight name", name.data, { @@ -515,7 +515,7 @@ static int64_t convert_index(int64_t index) /// @param name Option name /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info(String name, Arena *arena, Error *err) +Dict nvim_get_option_info(String name, Arena *arena, Error *err) FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11) { diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 85cce45560..7786c30624 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -18,6 +18,7 @@ #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/extmark.h" +#include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/highlight_group.h" #include "nvim/map_defs.h" @@ -41,6 +42,7 @@ void api_extmark_free_all_mem(void) xfree(name.data); }) map_destroy(String, &namespace_ids); + set_destroy(uint32_t, &namespace_localscope); } /// Creates a new namespace or gets an existing one. [namespace]() @@ -72,10 +74,10 @@ Integer nvim_create_namespace(String name) /// Gets existing, non-anonymous |namespace|s. /// /// @return dict that maps from names to namespace ids. -Dictionary nvim_get_namespaces(Arena *arena) +Dict nvim_get_namespaces(Arena *arena) FUNC_API_SINCE(5) { - Dictionary retval = arena_dict(arena, map_size(&namespace_ids)); + Dict retval = arena_dict(arena, map_size(&namespace_ids)); String name; handle_T id; @@ -156,7 +158,7 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na if (add_dict) { // TODO(bfredl): coding the size like this is a bit fragile. // We want ArrayOf(Dict(set_extmark)) as the return type.. - Dictionary dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table)); + Dict dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table)); PUT_C(dict, "ns_id", INTEGER_OBJ((Integer)start.ns)); @@ -179,13 +181,9 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na PUT_C(dict, "invalid", BOOLEAN_OBJ(true)); } - if (mt_scoped(start)) { - PUT_C(dict, "scoped", BOOLEAN_OBJ(true)); - } - decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena); - ADD_C(rv, DICTIONARY_OBJ(dict)); + ADD_C(rv, DICT_OBJ(dict)); } return rv; @@ -489,8 +487,6 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// used together with virt_text. /// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control /// sequence is used to generate a clickable hyperlink to this URL. -/// - scoped: boolean (EXPERIMENTAL) enables "scoping" for the extmark. See -/// |nvim__win_add_ns()| /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -575,7 +571,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer String c = opts->conceal; if (c.size > 0) { int ch; - hl.conceal_char = utfc_ptr2schar_len(c.data, (int)c.size, &ch); + hl.conceal_char = utfc_ptr2schar(c.data, &ch); if (!hl.conceal_char || !vim_isprintc(ch)) { api_set_error(err, kErrorTypeValidation, "conceal char has to be printable"); goto error; @@ -691,6 +687,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (HAS_KEY(opts, set_extmark, url)) { url = string_to_cstr(opts->url); + has_hl = true; } if (opts->ui_watched) { @@ -749,11 +746,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) { - if (opts->scoped) { - api_set_error(err, kErrorTypeException, "not yet implemented"); - goto error; - } - int r = (int)line; int c = (int)col; if (line2 == -1) { @@ -767,13 +759,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (kv_size(virt_lines.data.virt_lines)) { decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true); } - if (url != NULL) { - DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; - sh.url = url; - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0); - } if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); + sh.url = url; decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); } } else { @@ -797,12 +785,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } uint32_t decor_indexed = DECOR_ID_INVALID; - if (url != NULL) { - DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; - sh.url = url; - sh.next = decor_indexed; - decor_indexed = decor_put_sh(sh); - } + if (sign.flags & kSHIsSign) { sign.next = decor_indexed; decor_indexed = decor_put_sh(sign); @@ -815,9 +798,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } DecorInline decor = DECOR_INLINE_INIT; - if (decor_alloc || decor_indexed != DECOR_ID_INVALID || schar_high(hl.conceal_char)) { + if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL + || schar_high(hl.conceal_char)) { if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); + sh.url = url; sh.next = decor_indexed; decor_indexed = decor_put_sh(sh); } @@ -834,7 +819,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, decor, decor_flags, right_gravity, opts->end_right_gravity, !GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore), - opts->invalidate, opts->scoped, err); + opts->invalidate, err); if (ERROR_SET(err)) { decor_free(decor); return 0; @@ -960,7 +945,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In 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, false, NULL); + decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL); return ns_id; } @@ -1011,7 +996,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// Note: this function should not be called often. Rather, the callbacks /// themselves can be used to throttle unneeded callbacks. the `on_start` /// callback can return `false` to disable the provider until the next redraw. -/// Similarly, return `false` in `on_win` will skip the `on_lines` calls +/// Similarly, return `false` in `on_win` will skip the `on_line` calls /// for that window (but any extmarks set in `on_win` will still be used). /// A plugin managing multiple sources of decoration should ideally only set /// one provider, and merge the sources internally. You can use multiple `ns_id` @@ -1020,10 +1005,10 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// Note: doing anything other than setting extmarks is considered experimental. /// Doing things like changing options are not explicitly forbidden, but is /// likely to have unexpected consequences (such as 100% CPU consumption). -/// doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious +/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious /// for the moment. /// -/// Note: It is not allowed to remove or update extmarks in 'on_line' callbacks. +/// Note: It is not allowed to remove or update extmarks in `on_line` callbacks. /// /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param opts Table of callbacks: @@ -1038,7 +1023,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// ``` /// - on_win: called when starting to redraw a specific window. /// ``` -/// ["win", winid, bufnr, topline, botline] +/// ["win", winid, bufnr, toprow, botrow] /// ``` /// - on_line: called for each buffer line being redrawn. /// (The interaction with fold lines is subject to change) @@ -1217,77 +1202,119 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error /// EXPERIMENTAL: this API will change in the future. /// -/// Scopes a namespace to the a window, so extmarks in the namespace will be active only in the -/// given window. +/// Set some properties for namespace /// -/// @param window Window handle, or 0 for current window /// @param ns_id Namespace -/// @return true if the namespace was added, else false -Boolean nvim__win_add_ns(Window window, Integer ns_id, Error *err) +/// @param opts Optional parameters to set: +/// - wins: a list of windows to be scoped in +/// +void nvim__ns_set(Integer ns_id, Dict(ns_opts) *opts, Error *err) { - win_T *win = find_window_by_handle(window, err); - if (!win) { - return false; - } - VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { - return false; + return; }); - set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + bool set_scoped = true; - if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { - changed_window_setting(win); - } + if (HAS_KEY(opts, ns_opts, wins)) { + if (opts->wins.size == 0) { + set_scoped = false; + } - return true; -} + Set(ptr_t) windows = SET_INIT; + for (size_t i = 0; i < opts->wins.size; i++) { + Integer win = opts->wins.items[i].data.integer; -/// EXPERIMENTAL: this API will change in the future. -/// -/// Gets the namespace scopes for a given window. -/// -/// @param window Window handle, or 0 for current window -/// @return a list of namespaces ids -ArrayOf(Integer) nvim__win_get_ns(Window window, Arena *arena, Error *err) -{ - win_T *win = find_window_by_handle(window, err); - if (!win) { - return (Array)ARRAY_DICT_INIT; + win_T *wp = find_window_by_handle((Window)win, err); + if (!wp) { + return; + } + + set_put(ptr_t, &windows, wp); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(ptr_t, &windows, wp) && !set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + set_put(uint32_t, &wp->w_ns_set, (uint32_t)ns_id); + + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id) && !set_has(ptr_t, &windows, wp)) { + set_del(uint32_t, &wp->w_ns_set, (uint32_t)ns_id); + + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } + + set_destroy(ptr_t, &windows); } - Array rv = arena_array(arena, set_size(&win->w_ns_set)); - uint32_t i; - set_foreach(&win->w_ns_set, i, { - ADD_C(rv, INTEGER_OBJ((Integer)(i))); - }); + if (set_scoped && !set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + set_put(uint32_t, &namespace_localscope, (uint32_t)ns_id); - return rv; + // When a namespace becomes scoped, any window which contains + // elements associated with namespace needs to be redrawn + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } else if (!set_scoped && set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + set_del(uint32_t, &namespace_localscope, (uint32_t)ns_id); + + // When a namespace becomes unscoped, any window which does not + // contain elements associated with namespace needs to be redrawn + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (map_has(uint32_t, wp->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(wp); + } + } + } } /// EXPERIMENTAL: this API will change in the future. /// -/// Unscopes a namespace (un-binds it from the given scope). +/// Get the properties for namespace /// -/// @param window Window handle, or 0 for current window -/// @param ns_id the namespace to remove -/// @return true if the namespace was removed, else false -Boolean nvim__win_del_ns(Window window, Integer ns_id, Error *err) +/// @param ns_id Namespace +/// @return Map defining the namespace properties, see |nvim__ns_set()| +Dict(ns_opts) nvim__ns_get(Integer ns_id, Arena *arena, Error *err) { - win_T *win = find_window_by_handle(window, err); - if (!win) { - return false; + Dict(ns_opts) opts = KEYDICT_INIT; + + Array windows = ARRAY_DICT_INIT; + + PUT_KEY(opts, ns_opts, wins, windows); + + VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, { + return opts; + }); + + if (!set_has(uint32_t, &namespace_localscope, (uint32_t)ns_id)) { + return opts; } - if (!set_has(uint32_t, &win->w_ns_set, (uint32_t)ns_id)) { - return false; + size_t count = 0; + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + count++; + } } - set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); + windows = arena_array(arena, count); - if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { - changed_window_setting(win); + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (set_has(uint32_t, &wp->w_ns_set, (uint32_t)ns_id)) { + ADD(windows, INTEGER_OBJ(wp->handle)); + } } - return true; + PUT_KEY(opts, ns_opts, wins, windows); + + return opts; } diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h index 124feaabfb..af2d51c95c 100644 --- a/src/nvim/api/extmark.h +++ b/src/nvim/api/extmark.h @@ -4,14 +4,29 @@ #include "nvim/api/keysets_defs.h" // IWYU pragma: keep #include "nvim/api/private/defs.h" // IWYU pragma: keep +#include "nvim/buffer_defs.h" #include "nvim/decoration_defs.h" // IWYU pragma: keep #include "nvim/macros_defs.h" #include "nvim/map_defs.h" #include "nvim/types_defs.h" EXTERN Map(String, int) namespace_ids INIT( = MAP_INIT); +/// Non-global namespaces. A locally-scoped namespace may be "orphaned" if all +/// window(s) it was scoped to, are destroyed. Such orphans are tracked here to +/// avoid being mistaken as "global scope". +EXTERN Set(uint32_t) namespace_localscope INIT( = SET_INIT); EXTERN handle_T next_namespace_id INIT( = 1); +/// Returns true if the namespace is global or scoped in the given window. +static inline bool ns_in_win(uint32_t ns_id, win_T *wp) +{ + if (!set_has(uint32_t, &namespace_localscope, ns_id)) { + return true; + } + + return set_has(uint32_t, &wp->w_ns_set, ns_id); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/extmark.h.generated.h" #endif diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index 00d8aa8428..552612dd13 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -103,7 +103,7 @@ typedef struct { Object nargs; Object preview; Object range; - Boolean register_; + Boolean register_ DictKey(register); } Dict(user_command); typedef struct { @@ -170,7 +170,7 @@ typedef struct { Boolean reverse; Boolean altfont; Boolean nocombine; - Boolean default_; + Boolean default_ DictKey(default); Object cterm; Object foreground; Object fg; @@ -275,8 +275,8 @@ typedef struct { String reg; Boolean bang; Array args; - Dictionary magic; - Dictionary mods; + Dict magic; + Dict mods; Object nargs; Object addr; Object nextcmd; @@ -293,7 +293,7 @@ typedef struct { Boolean silent; Boolean emsg_silent; Boolean unsilent; - Dictionary filter; + Dict filter; Boolean sandbox; Boolean noautocmd; Boolean browse; @@ -387,3 +387,46 @@ typedef struct { Window win; Buffer buf; } Dict(redraw); + +typedef struct { + OptionalKeys is_set__ns_opts_; + Array wins; +} Dict(ns_opts); + +typedef struct { + OptionalKeys is_set___shada_search_pat_; + Boolean magic DictKey(sm); + Boolean smartcase DictKey(sc); + Boolean has_line_offset DictKey(sl); + Boolean place_cursor_at_end DictKey(se); + Boolean is_last_used DictKey(su); + Boolean is_substitute_pattern DictKey(ss); + Boolean highlighted DictKey(sh); + Boolean search_backward DictKey(sb); + Integer offset DictKey(so); + String pat DictKey(sp); +} Dict(_shada_search_pat); + +typedef struct { + OptionalKeys is_set___shada_mark_; + Integer n; + Integer l; + Integer c; + String f; +} Dict(_shada_mark); + +typedef struct { + OptionalKeys is_set___shada_register_; + StringArray rc; + Boolean ru; + Integer rt; + Integer n; + Integer rw; +} Dict(_shada_register); + +typedef struct { + OptionalKeys is_set___shada_buflist_item_; + Integer l; + Integer c; + String f; +} Dict(_shada_buflist_item); diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index d9bc0ccc92..1a0edd551e 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -54,6 +54,10 @@ 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", + "cannot use both global 'scope' and 'buf'", { + return FAIL; + }); *scope = OPT_LOCAL; *req_scope = kOptReqBuf; *from = find_buffer_by_handle(opts->buf, err); @@ -68,11 +72,6 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex * return FAIL; }); - VALIDATE((!HAS_KEY_X(opts, scope) || !HAS_KEY_X(opts, buf)), "%s", - "cannot use both 'scope' and 'buf'", { - return FAIL; - }); - VALIDATE((!HAS_KEY_X(opts, win) || !HAS_KEY_X(opts, buf)), "%s", "cannot use both 'buf' and 'win'", { return FAIL; @@ -257,13 +256,13 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( /// Gets the option information for all options. /// -/// The dictionary has the full option names as keys and option metadata -/// dictionaries as detailed at |nvim_get_option_info2()|. +/// The dict has the full option names as keys and option metadata dicts as detailed at +/// |nvim_get_option_info2()|. /// /// @see |nvim_get_commands()| /// -/// @return dictionary of all options -Dictionary nvim_get_all_options_info(Arena *arena, Error *err) +/// @return dict of all options +Dict nvim_get_all_options_info(Arena *arena, Error *err) FUNC_API_SINCE(7) { return get_all_vimoptions(arena); @@ -271,7 +270,7 @@ Dictionary nvim_get_all_options_info(Arena *arena, Error *err) /// Gets the option information for one option from arbitrary buffer or window /// -/// Resulting dictionary has keys: +/// Resulting dict has keys: /// - name: Name of the option (like 'filetype') /// - shortname: Shortened name of the option (like 'ft') /// - type: type of option ("string", "number" or "boolean") @@ -302,7 +301,7 @@ Dictionary nvim_get_all_options_info(Arena *arena, Error *err) /// Implies {scope} is "local". /// @param[out] err Error details, if any /// @return Option Information -Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error *err) +Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error *err) FUNC_API_SINCE(11) { OptIndex opt_idx = 0; @@ -311,7 +310,7 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, void *from = NULL; if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL, err)) { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf; diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index a78d78c057..59e7373f68 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -7,7 +7,9 @@ #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" #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" @@ -28,6 +30,7 @@ typedef struct { #endif #define TYPVAL_ENCODE_ALLOW_SPECIALS false +#define TYPVAL_ENCODE_CHECK_BEFORE #define TYPVAL_ENCODE_CONV_NIL(tv) \ kvi_push(edata->stack, NIL) @@ -91,8 +94,7 @@ static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t le kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - kvi_push(edata->stack, \ - DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) + kvi_push(edata->stack, DICT_OBJ(((Dict) { .capacity = 0, .size = 0 }))) static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL @@ -134,7 +136,7 @@ static inline void typval_encode_list_end(EncodedData *const edata) static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len))); + kvi_push(edata->stack, DICT_OBJ(arena_dict(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ @@ -149,13 +151,13 @@ static inline void typval_encode_after_key(EncodedData *const edata) { Object key = kv_pop(edata->stack); Object *const dict = &kv_last(edata->stack); - assert(dict->type == kObjectTypeDictionary); - assert(dict->data.dictionary.size < dict->data.dictionary.capacity); + assert(dict->type == kObjectTypeDict); + assert(dict->data.dict.size < dict->data.dict.capacity); if (key.type == kObjectTypeString) { - dict->data.dictionary.items[dict->data.dictionary.size].key + dict->data.dict.items[dict->data.dict.size].key = key.data.string; } else { - dict->data.dictionary.items[dict->data.dictionary.size].key + dict->data.dict.items[dict->data.dict.size].key = STATIC_CSTR_AS_STRING("__INVALID_KEY__"); } } @@ -168,9 +170,9 @@ static inline void typval_encode_between_dict_items(EncodedData *const edata) { Object val = kv_pop(edata->stack); Object *const dict = &kv_last(edata->stack); - assert(dict->type == kObjectTypeDictionary); - assert(dict->data.dictionary.size < dict->data.dictionary.capacity); - dict->data.dictionary.items[dict->data.dictionary.size++].value = val; + assert(dict->type == kObjectTypeDict); + assert(dict->data.dict.size < dict->data.dict.capacity); + dict->data.dict.items[dict->data.dict.size++].value = val; } #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \ @@ -182,7 +184,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata) typval_encode_between_dict_items(edata); #ifndef NDEBUG const Object *const dict = &kv_last(edata->stack); - assert(dict->data.dictionary.size == dict->data.dictionary.capacity); + assert(dict->data.dict.size == dict->data.dict.capacity); #endif } @@ -217,6 +219,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_LIST_START #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CHECK_BEFORE #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER @@ -300,15 +303,11 @@ void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Erro tv->vval.v_float = obj->data.floating; break; - case kObjectTypeString: - tv->v_type = VAR_STRING; - if (obj->data.string.data == NULL) { - tv->vval.v_string = NULL; - } else { - tv->vval.v_string = xmemdupz(obj->data.string.data, - obj->data.string.size); - } + case kObjectTypeString: { + String s = obj->data.string; + *tv = decode_string(s.data, s.size, false, false); break; + } case kObjectTypeArray: { list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size); @@ -325,11 +324,11 @@ void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Erro break; } - case kObjectTypeDictionary: { + case kObjectTypeDict: { dict_T *const dict = tv_dict_alloc(); - for (uint32_t i = 0; i < obj->data.dictionary.size; i++) { - KeyValuePair *item = &obj->data.dictionary.items[i]; + for (uint32_t i = 0; i < obj->data.dict.size; i++) { + KeyValuePair *item = &obj->data.dict.items[i]; String key = item->key; dictitem_T *const di = tv_dict_item_alloc(key.data); object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err); diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index ca088d7a55..26d5ac09a8 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -5,7 +5,6 @@ #include <string.h> #include "klib/kvec.h" -#include "nvim/func_attr.h" #include "nvim/types_defs.h" #define ARRAY_DICT_INIT KV_INITIAL_VALUE @@ -18,8 +17,11 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # define ArrayOf(...) Array -# define DictionaryOf(...) Dictionary +# define DictOf(...) Dict # define Dict(name) KeyDict_##name +# define DictHash(name) KeyDict_##name##_get_field +# define DictKey(name) +# include "api/private/defs.h.inline.generated.h" #endif // Basic types @@ -47,15 +49,13 @@ typedef enum { /// Internal call from Lua code #define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1) -static inline bool is_internal_call(uint64_t channel_id) - REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; - /// Check whether call is internal /// /// @param[in] channel_id Channel id. /// /// @return true if channel_id refers to internal channel. static inline bool is_internal_call(const uint64_t channel_id) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST { return !!(channel_id & INTERNAL_CALL_MASK); } @@ -88,7 +88,9 @@ typedef struct object Object; typedef kvec_t(Object) Array; typedef struct key_value_pair KeyValuePair; -typedef kvec_t(KeyValuePair) Dictionary; +typedef kvec_t(KeyValuePair) Dict; + +typedef kvec_t(String) StringArray; typedef enum { kObjectTypeNil = 0, @@ -97,7 +99,7 @@ typedef enum { kObjectTypeFloat, kObjectTypeString, kObjectTypeArray, - kObjectTypeDictionary, + kObjectTypeDict, kObjectTypeLuaRef, // EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer, @@ -105,6 +107,10 @@ typedef enum { kObjectTypeTabpage, } ObjectType; +typedef enum { + kUnpackTypeStringArray = -1, +} UnpackType; + /// Value by which objects represented as EXT type are shifted /// /// Subtracted when packing, added when unpacking. Used to allow moving @@ -121,7 +127,7 @@ struct object { Float floating; String string; Array array; - Dictionary dictionary; + Dict dict; LuaRef luaref; } data; }; @@ -142,7 +148,7 @@ typedef struct { typedef struct { char *str; size_t ptr_off; - ObjectType type; // kObjectTypeNil == untyped + int type; // ObjectType or UnpackType. kObjectTypeNil == untyped int opt_index; bool is_hlgroup; } KeySetLink; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a17e78cc31..e1fb4ed732 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1,6 +1,5 @@ #include <assert.h> #include <limits.h> -#include <msgpack/unpack.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> @@ -199,7 +198,7 @@ dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) api_set_error(err, kErrorTypeException, "Key is fixed: %s", key.data); } } else if (dict->dv_lock) { - api_set_error(err, kErrorTypeException, "Dictionary is locked"); + api_set_error(err, kErrorTypeException, "Dict is locked"); } else if (key.size == 0) { api_set_error(err, kErrorTypeValidation, "Key name is empty"); } else if (key.size > INT_MAX) { @@ -529,29 +528,19 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col start_col = start_col < 0 ? line_length + start_col + 1 : start_col; end_col = end_col < 0 ? line_length + end_col + 1 : end_col; - if (start_col >= MAXCOL || end_col >= MAXCOL) { - api_set_error(err, kErrorTypeValidation, "Column index is too high"); - return rv; - } + start_col = MIN(MAX(0, start_col), line_length); + end_col = MIN(MAX(0, end_col), line_length); if (start_col > end_col) { - api_set_error(err, kErrorTypeValidation, "start_col must be less than end_col"); - return rv; - } - - if (start_col >= line_length) { + api_set_error(err, kErrorTypeValidation, "start_col must be less than or equal to end_col"); return rv; } - return cstrn_as_string(&bufstr[start_col], (size_t)(end_col - start_col)); + return cbuf_as_string(bufstr + start_col, (size_t)(end_col - start_col)); } void api_free_string(String value) { - if (!value.data) { - return; - } - xfree(value.data); } @@ -562,9 +551,9 @@ Array arena_array(Arena *arena, size_t max_size) return arr; } -Dictionary arena_dict(Arena *arena, size_t max_size) +Dict arena_dict(Arena *arena, size_t max_size) { - Dictionary dict = ARRAY_DICT_INIT; + Dict dict = ARRAY_DICT_INIT; kv_fixsize_arena(arena, dict, max_size); return dict; } @@ -607,8 +596,8 @@ void api_free_object(Object value) api_free_array(value.data.array); break; - case kObjectTypeDictionary: - api_free_dictionary(value.data.dictionary); + case kObjectTypeDict: + api_free_dict(value.data.dict); break; case kObjectTypeLuaRef: @@ -626,7 +615,7 @@ void api_free_array(Array value) xfree(value.items); } -void api_free_dictionary(Dictionary value) +void api_free_dict(Dict value) { for (size_t i = 0; i < value.size; i++) { api_free_string(value.items[i].key); @@ -659,7 +648,7 @@ Object api_metadata(void) Arena arena = ARENA_EMPTY; Error err = ERROR_INIT; metadata = unpack((char *)packed_api_metadata, sizeof(packed_api_metadata), &arena, &err); - if (ERROR_SET(&err) || metadata.type != kObjectTypeDictionary) { + if (ERROR_SET(&err) || metadata.type != kObjectTypeDict) { abort(); } mem_for_metadata = arena_finish(&arena); @@ -695,9 +684,9 @@ Array copy_array(Array array, Arena *arena) return rv; } -Dictionary copy_dictionary(Dictionary dict, Arena *arena) +Dict copy_dict(Dict dict, Arena *arena) { - Dictionary rv = arena_dict(arena, dict.size); + Dict rv = arena_dict(arena, dict.size); for (size_t i = 0; i < dict.size; i++) { KeyValuePair item = dict.items[i]; PUT_C(rv, copy_string(item.key, arena).data, copy_object(item.value, arena)); @@ -724,8 +713,8 @@ Object copy_object(Object obj, Arena *arena) case kObjectTypeArray: return ARRAY_OBJ(copy_array(obj.data.array, arena)); - case kObjectTypeDictionary: - return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary, arena)); + case kObjectTypeDict: + return DICT_OBJ(copy_dict(obj.data.dict, arena)); case kObjectTypeLuaRef: return LUAREF_OBJ(api_new_luaref(obj.data.luaref)); @@ -779,7 +768,8 @@ int object_to_hl_id(Object obj, const char *what, Error *err) String str = obj.data.string; return str.size ? syn_check_group(str.data, str.size) : 0; } else if (obj.type == kObjectTypeInteger) { - return MAX((int)obj.data.integer, 0); + 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); return 0; @@ -801,7 +791,7 @@ char *api_typename(ObjectType t) return "String"; case kObjectTypeArray: return "Array"; - case kObjectTypeDictionary: + case kObjectTypeDict: return "Dict"; case kObjectTypeLuaRef: return "Function"; @@ -854,7 +844,7 @@ free_exit: } // see also nlua_pop_keydict for the lua specific implementation -bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error *err) +bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dict dict, Error *err) { for (size_t i = 0; i < dict.size; i++) { String k = dict.items[i].key; @@ -918,23 +908,25 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error return false; }); *(Array *)mem = value->data.array; - } else if (field->type == kObjectTypeDictionary) { - Dictionary *val = (Dictionary *)mem; + } else if (field->type == kObjectTypeDict) { + Dict *val = (Dict *)mem; // allow empty array as empty dict for lua (directly or via lua-client RPC) if (value->type == kObjectTypeArray && value->data.array.size == 0) { - *val = (Dictionary)ARRAY_DICT_INIT; - } else if (value->type == kObjectTypeDictionary) { - *val = value->data.dictionary; + *val = (Dict)ARRAY_DICT_INIT; + } else if (value->type == kObjectTypeDict) { + *val = value->data.dict; } else { - api_err_exp(err, field->str, api_typename(field->type), api_typename(value->type)); + api_err_exp(err, field->str, api_typename((ObjectType)field->type), + api_typename(value->type)); return false; } } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { - if (value->type == kObjectTypeInteger || value->type == field->type) { + if (value->type == kObjectTypeInteger || value->type == (ObjectType)field->type) { *(handle_T *)mem = (handle_T)value->data.integer; } else { - api_err_exp(err, field->str, api_typename(field->type), api_typename(value->type)); + api_err_exp(err, field->str, api_typename((ObjectType)field->type), + api_typename(value->type)); return false; } } else if (field->type == kObjectTypeLuaRef) { @@ -949,9 +941,9 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error return true; } -Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena) +Dict api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena) { - Dictionary rv = arena_dict(arena, max_size); + Dict rv = arena_dict(arena, max_size); for (size_t i = 0; table[i].str; i++) { KeySetLink *field = &table[i]; bool is_set = true; @@ -979,12 +971,12 @@ Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, val = STRING_OBJ(*(String *)mem); } else if (field->type == kObjectTypeArray) { val = ARRAY_OBJ(*(Array *)mem); - } else if (field->type == kObjectTypeDictionary) { - val = DICTIONARY_OBJ(*(Dictionary *)mem); + } else if (field->type == kObjectTypeDict) { + val = DICT_OBJ(*(Dict *)mem); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { val.data.integer = *(handle_T *)mem; - val.type = field->type; + val.type = (ObjectType)field->type; } else if (field->type == kObjectTypeLuaRef) { // do nothing } else { @@ -1010,8 +1002,8 @@ void api_luarefs_free_object(Object value) api_luarefs_free_array(value.data.array); break; - case kObjectTypeDictionary: - api_luarefs_free_dict(value.data.dictionary); + case kObjectTypeDict: + api_luarefs_free_dict(value.data.dict); break; default: @@ -1027,8 +1019,8 @@ void api_luarefs_free_keydict(void *dict, KeySetLink *table) api_luarefs_free_object(*(Object *)mem); } else if (table[i].type == kObjectTypeLuaRef) { api_free_luaref(*(LuaRef *)mem); - } else if (table[i].type == kObjectTypeDictionary) { - api_luarefs_free_dict(*(Dictionary *)mem); + } else if (table[i].type == kObjectTypeDict) { + api_luarefs_free_dict(*(Dict *)mem); } } } @@ -1040,7 +1032,7 @@ void api_luarefs_free_array(Array value) } } -void api_luarefs_free_dict(Dictionary value) +void api_luarefs_free_dict(Dict value) { for (size_t i = 0; i < value.size; i++) { api_luarefs_free_object(value.items[i].value); diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 7eda8ffaf6..57932e067e 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -53,9 +53,9 @@ .type = kObjectTypeArray, \ .data.array = a }) -#define DICTIONARY_OBJ(d) ((Object) { \ - .type = kObjectTypeDictionary, \ - .data.dictionary = d }) +#define DICT_OBJ(d) ((Object) { \ + .type = kObjectTypeDict, \ + .data.dict = d }) #define LUAREF_OBJ(r) ((Object) { \ .type = kObjectTypeLuaRef, \ @@ -90,7 +90,7 @@ name.items = name##__items; \ #define MAXSIZE_TEMP_DICT(name, maxsize) \ - Dictionary name = ARRAY_DICT_INIT; \ + Dict name = ARRAY_DICT_INIT; \ KeyValuePair name##__items[maxsize]; \ name.capacity = maxsize; \ name.items = name##__items; \ @@ -121,7 +121,7 @@ typedef kvec_withinit_t(Object, 16) ArrayBuilder; #define api_init_tabpage #define api_init_object = NIL #define api_init_array = ARRAY_DICT_INIT -#define api_init_dictionary = ARRAY_DICT_INIT +#define api_init_dict = ARRAY_DICT_INIT #define KEYDICT_INIT { 0 } diff --git a/src/nvim/api/private/validate.c b/src/nvim/api/private/validate.c index e198c671eb..9fd7d3bfa6 100644 --- a/src/nvim/api/private/validate.c +++ b/src/nvim/api/private/validate.c @@ -17,7 +17,7 @@ void api_err_invalid(Error *err, const char *name, const char *val_s, int64_t va char *has_space = strchr(name, ' '); // No value. - if (val_s && val_s[0] == '\0') { + if (val_s && val_s[0] == NUL) { api_set_error(err, errtype, has_space ? "Invalid %s" : "Invalid '%s'", name); return; } diff --git a/src/nvim/api/private/validate.h b/src/nvim/api/private/validate.h index 2c1d1a241d..67af8adea8 100644 --- a/src/nvim/api/private/validate.h +++ b/src/nvim/api/private/validate.h @@ -42,7 +42,7 @@ #define VALIDATE_T(name, expected_t, actual_t, code) \ do { \ - STATIC_ASSERT(expected_t != kObjectTypeDictionary, "use VALIDATE_T_DICT"); \ + STATIC_ASSERT(expected_t != kObjectTypeDict, "use VALIDATE_T_DICT"); \ if (expected_t != actual_t) { \ api_err_exp(err, name, api_typename(expected_t), api_typename(actual_t)); \ code; \ @@ -52,7 +52,7 @@ /// Checks that `obj_` has type `expected_t`. #define VALIDATE_T2(obj_, expected_t, code) \ do { \ - STATIC_ASSERT(expected_t != kObjectTypeDictionary, "use VALIDATE_T_DICT"); \ + STATIC_ASSERT(expected_t != kObjectTypeDict, "use VALIDATE_T_DICT"); \ if ((obj_).type != expected_t) { \ api_err_exp(err, STR(obj_), api_typename(expected_t), api_typename((obj_).type)); \ code; \ @@ -62,11 +62,11 @@ /// Checks that `obj_` has Dict type. Also allows empty Array in a Lua context. #define VALIDATE_T_DICT(name, obj_, code) \ do { \ - if ((obj_).type != kObjectTypeDictionary \ + if ((obj_).type != kObjectTypeDict \ && !(channel_id == LUA_INTERNAL_CALL \ && (obj_).type == kObjectTypeArray \ && (obj_).data.array.size == 0)) { \ - api_err_exp(err, name, api_typename(kObjectTypeDictionary), api_typename((obj_).type)); \ + api_err_exp(err, name, api_typename(kObjectTypeDict), api_typename((obj_).type)); \ code; \ } \ } while (0) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index fdf25c75d7..b09a9ed253 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1,6 +1,5 @@ #include <assert.h> #include <inttypes.h> -#include <msgpack/pack.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -94,15 +93,15 @@ void remote_ui_free_all_mem(void) } #endif -/// Wait until ui has connected on stdio channel if only_stdio -/// is true, otherwise any channel. +/// Wait until UI has connected. +/// +/// @param only_stdio UI is expected to connect on stdio. void remote_ui_wait_for_attach(bool only_stdio) { if (only_stdio) { Channel *channel = find_channel(CHAN_STDIO); if (!channel) { - // this function should only be called in --embed mode, stdio channel - // can be assumed. + // `only_stdio` implies --embed mode, thus stdio channel can be assumed. abort(); } @@ -129,8 +128,7 @@ void remote_ui_wait_for_attach(bool only_stdio) /// @param height Requested screen rows /// @param options |ui-option| map /// @param[out] err Error details, if any -void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictionary options, - Error *err) +void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dict options, Error *err) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (map_has(uint64_t, &connected_uis, channel_id)) { @@ -688,8 +686,8 @@ void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAtt PUT_C(rgb, "url", CSTR_AS_OBJ(url)); } - ADD_C(args, DICTIONARY_OBJ(rgb)); - ADD_C(args, DICTIONARY_OBJ(cterm)); + ADD_C(args, DICT_OBJ(rgb)); + ADD_C(args, DICT_OBJ(cterm)); if (ui->ui_ext[kUIHlState]) { ADD_C(args, ARRAY_OBJ(info)); @@ -710,7 +708,7 @@ void remote_ui_highlight_set(RemoteUI *ui, int id) MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); MAXSIZE_TEMP_ARRAY(args, 1); - ADD_C(args, DICTIONARY_OBJ(dict)); + ADD_C(args, DICT_OBJ(dict)); push_call(ui, "highlight_set", args); } @@ -778,16 +776,26 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + MAX_SCHAR_SIZE + 5 + 5) + 1 + if ( + // Close to overflowing the redraw buffer. Finish this event, flush, + // and start a new "grid_line" event at the current position. + // For simplicity leave place for the final "clear" element as well, + // hence the factor of 2 in the check. + UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + MAX_SCHAR_SIZE + 5 + 5) + 1 + // Also if there is a lot of packed cells, pass them off to the UI to + // let it start processing them. || ui->ncells_pending >= 500) { - // close to overflowing the redraw buffer. finish this event, - // flush, and start a new "grid_line" event at the current position. - // For simplicity leave place for the final "clear" element - // as well, hence the factor of 2 in the check. - // Also if there is a lot of packed cells, pass them of to the UI to - // let it start processing them + // If the last chunk was all spaces, add an empty clearing chunk, + // so it's clear that the last chunk wasn't a clearing chunk. + if (was_space) { + nelem++; + ui->ncells_pending += 1; + mpack_array(buf, 3); + mpack_str_small(buf, S_LEN(" ")); + mpack_uint(buf, (uint32_t)clearattr); + mpack_uint(buf, 0); + } mpack_w2(&lenpos, nelem); - // We only ever set the wrap field on the final "grid_line" event for the line. mpack_bool(buf, false); ui_flush_buf(ui); @@ -838,7 +846,7 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco char sc_buf[MAX_SCHAR_SIZE]; schar_get(sc_buf, chunk[i]); remote_ui_put(ui, sc_buf); - if (utf_ambiguous_width(utf_ptr2char(sc_buf))) { + if (utf_ambiguous_width(sc_buf)) { ui->client_col = -1; // force cursor update } } @@ -911,11 +919,11 @@ static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena) Array new_item = arena_array(arena, 2); int attr = (int)item.items[0].data.integer; if (attr) { - Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); + Dict rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false); - ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs)); + ADD_C(new_item, DICT_OBJ(rgb_attrs)); } else { - ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD_C(new_item, DICT_OBJ((Dict)ARRAY_DICT_INIT)); } ADD_C(new_item, item.items[1]); ADD_C(new_contents, ARRAY_OBJ(new_item)); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index fc780e1248..8c88a19147 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -28,6 +28,8 @@ #include "nvim/cursor.h" #include "nvim/decoration.h" #include "nvim/drawscreen.h" +#include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -68,7 +70,7 @@ #include "nvim/optionstr.h" #include "nvim/os/input.h" #include "nvim/os/os_defs.h" -#include "nvim/os/process.h" +#include "nvim/os/proc.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" #include "nvim/runtime.h" @@ -115,7 +117,7 @@ Integer nvim_get_hl_id_by_name(String name) /// @param[out] err Error details, if any. /// @return Highlight groups as a map from group name to a highlight definition map as in |nvim_set_hl()|, /// or only a single highlight definition map if requested by name or id. -Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, Error *err) +Dict nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, Error *err) FUNC_API_SINCE(11) { return ns_get_hl_defs((NS)ns_id, opts, arena, err); @@ -313,7 +315,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) keys_esc = keys.data; } if (lowlevel) { - input_enqueue_raw(cstr_as_string(keys_esc)); + input_enqueue_raw(keys_esc, strlen(keys_esc)); } else { ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), insert ? 0 : typebuf.tb_len, !typed, false); @@ -342,9 +344,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) } } -/// Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a low-level -/// input buffer and the call is non-blocking (input is processed -/// asynchronously by the eventloop). +/// Queues raw user-input. Unlike |nvim_feedkeys()|, this uses a low-level input buffer and the call +/// is non-blocking (input is processed asynchronously by the eventloop). +/// +/// To input blocks of text, |nvim_paste()| is much faster and should be preferred. /// /// On execution error: does not fail, but updates v:errmsg. /// @@ -524,13 +527,13 @@ Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err) /// @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, Dictionary opts, Arena *arena, Error *err) +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, DICTIONARY_OBJ(opts)); + ADD_C(args, DICT_OBJ(opts)); return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err); } @@ -571,10 +574,10 @@ typedef struct { Arena *arena; } RuntimeCookie; -/// Find files in runtime directories +/// Finds files in runtime directories, in 'runtimepath' order. /// /// "name" can contain wildcards. For example -/// nvim_get_runtime_file("colors/*.vim", true) will return all color +/// `nvim_get_runtime_file("colors/*.{vim,lua}", true)` will return all color /// scheme files. Always use forward slashes (/) in the search pattern for /// subdirectories regardless of platform. /// @@ -1210,17 +1213,30 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) } } -/// Pastes at cursor, in any mode. +/// Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call +/// this to implement "paste", but it's also intended for use by scripts to input large, +/// dot-repeatable blocks of text (as opposed to |nvim_input()| which is subject to mappings/events +/// and is thus much slower). +/// +/// Invokes the |vim.paste()| handler, which handles each mode appropriately. /// -/// Invokes the `vim.paste` handler, which handles each mode appropriately. -/// Sets redo/undo. Faster than |nvim_input()|. Lines break at LF ("\n"). +/// Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` but do not affect the +/// return value (which is strictly decided by `vim.paste()`). On error or cancel, subsequent calls +/// are ignored ("drained") until the next paste is initiated (phase 1 or -1). /// -/// Errors ('nomodifiable', `vim.paste()` failure, …) are reflected in `err` -/// but do not affect the return value (which is strictly decided by -/// `vim.paste()`). On error, subsequent calls are ignored ("drained") until -/// the next paste is initiated (phase 1 or -1). +/// Useful in mappings and scripts to insert multiline text. Example: +/// +/// ```lua +/// vim.keymap.set('n', 'x', function() +/// vim.api.nvim_paste([[ +/// line1 +/// line2 +/// line3 +/// ]], false, -1) +/// end, { buffer = true }) +/// ``` /// -/// @param data Multiline input. May be binary (containing NUL bytes). +/// @param data Multiline input. Lines break at LF ("\n"). May be binary (containing NUL bytes). /// @param crlf Also break lines at CR and CRLF. /// @param phase -1: paste in a single call (i.e. without streaming). /// To "stream" a paste, call `nvim_paste` sequentially with @@ -1231,20 +1247,20 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) /// @param[out] err Error details, if any /// @return /// - true: Client may continue pasting. -/// - false: Client must cancel the paste. -Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error *err) +/// - false: Client should cancel the paste. +Boolean nvim_paste(uint64_t channel_id, String data, Boolean crlf, Integer phase, Arena *arena, + Error *err) FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - static bool draining = false; - bool cancel = false; + static bool cancelled = false; VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, { return false; }); if (phase == -1 || phase == 1) { // Start of paste-stream. - draining = false; - } else if (draining) { + cancelled = false; + } else if (cancelled) { // Skip remaining chunks. Report error only once per "stream". goto theend; } @@ -1253,40 +1269,29 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error ADD_C(args, ARRAY_OBJ(lines)); ADD_C(args, INTEGER_OBJ(phase)); Object rv = NLUA_EXEC_STATIC("return vim.paste(...)", args, kRetNilBool, arena, err); - if (ERROR_SET(err)) { - draining = true; - goto theend; + // vim.paste() decides if client should cancel. + if (ERROR_SET(err) || (rv.type == kObjectTypeBoolean && !rv.data.boolean)) { + cancelled = true; } - if (!(State & (MODE_CMDLINE | MODE_INSERT)) && (phase == -1 || phase == 1)) { - ResetRedobuff(); - AppendCharToRedobuff('a'); // Dot-repeat. - } - // vim.paste() decides if client should cancel. Errors do NOT cancel: we - // want to drain remaining chunks (rather than divert them to main input). - cancel = (rv.type == kObjectTypeBoolean && !rv.data.boolean); - if (!cancel && !(State & MODE_CMDLINE)) { // Dot-repeat. - for (size_t i = 0; i < lines.size; i++) { - String s = lines.items[i].data.string; - assert(s.size <= INT_MAX); - AppendToRedobuffLit(s.data, (int)s.size); - // readfile()-style: "\n" is indicated by presence of N+1 item. - if (i + 1 < lines.size) { - AppendCharToRedobuff(NL); - } - } + if (!cancelled && (phase == -1 || phase == 1)) { + paste_store(channel_id, kFalse, NULL_STRING, crlf); + } + if (!cancelled) { + paste_store(channel_id, kNone, data, crlf); } - if (!(State & (MODE_CMDLINE | MODE_INSERT)) && (phase == -1 || phase == 3)) { - AppendCharToRedobuff(ESC); // Dot-repeat. + if (phase == 3 || phase == (cancelled ? 2 : -1)) { + paste_store(channel_id, kTrue, NULL_STRING, crlf); } theend: - if (cancel || phase == -1 || phase == 3) { // End of paste-stream. - draining = false; + ; + bool retval = !cancelled; + if (phase == -1 || phase == 3) { // End of paste-stream. + cancelled = false; } - - return !cancel; + return retval; } -/// Puts text at cursor, in any mode. +/// Puts text at cursor, in any mode. For dot-repeatable input, use |nvim_paste()|. /// /// Compare |:put| and |p| which are always linewise. /// @@ -1359,10 +1364,10 @@ Integer nvim_get_color_by_name(String name) /// (e.g. 65535). /// /// @return Map of color names and RGB values. -Dictionary nvim_get_color_map(Arena *arena) +Dict nvim_get_color_map(Arena *arena) FUNC_API_SINCE(1) { - Dictionary colors = arena_dict(arena, ARRAY_SIZE(color_name_table)); + Dict colors = arena_dict(arena, ARRAY_SIZE(color_name_table)); for (int i = 0; color_name_table[i].name != NULL; i++) { PUT_C(colors, color_name_table[i].name, INTEGER_OBJ(color_name_table[i].color)); @@ -1378,7 +1383,7 @@ Dictionary nvim_get_color_map(Arena *arena) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) +Dict nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; @@ -1405,7 +1410,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) int_types |= kCtxFuncs; } else { VALIDATE_S(false, "type", s, { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; }); } } @@ -1414,7 +1419,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) Context ctx = CONTEXT_INIT; ctx_save(&ctx, int_types); - Dictionary dict = ctx_to_dict(&ctx, arena); + Dict dict = ctx_to_dict(&ctx, arena); ctx_free(&ctx); return dict; } @@ -1422,7 +1427,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) /// Sets the current editor state from the given |context| map. /// /// @param dict |Context| map. -Object nvim_load_context(Dictionary dict, Error *err) +Object nvim_load_context(Dict dict, Error *err) FUNC_API_SINCE(6) { Context ctx = CONTEXT_INIT; @@ -1444,11 +1449,11 @@ Object nvim_load_context(Dictionary dict, Error *err) /// Gets the current mode. |mode()| /// "blocking" is true if Nvim is waiting for input. /// -/// @returns Dictionary { "mode": String, "blocking": Boolean } -Dictionary nvim_get_mode(Arena *arena) +/// @returns Dict { "mode": String, "blocking": Boolean } +Dict nvim_get_mode(Arena *arena) FUNC_API_SINCE(2) FUNC_API_FAST { - Dictionary rv = arena_dict(arena, 2); + Dict rv = arena_dict(arena, 2); char *modestr = arena_alloc(arena, MODE_MAX_LENGTH, false); get_mode(modestr); bool blocked = input_blocking(); @@ -1464,7 +1469,7 @@ Dictionary nvim_get_mode(Arena *arena) /// @param mode Mode short-name ("n", "i", "v", ...) /// @returns Array of |maparg()|-like dictionaries describing mappings. /// The "buffer" key is always zero. -ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena) +ArrayOf(Dict) nvim_get_keymap(String mode, Arena *arena) FUNC_API_SINCE(3) { return keymap_array(mode, NULL, arena); @@ -1523,7 +1528,7 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err) } /// Returns a 2-tuple (Array), where item 0 is the current channel id and item -/// 1 is the |api-metadata| map (Dictionary). +/// 1 is the |api-metadata| map (Dict). /// /// @returns 2-tuple `[{channel-id}, {api-metadata}]` Array nvim_get_api_info(uint64_t channel_id, Arena *arena) @@ -1552,7 +1557,7 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// /// @param channel_id /// @param name Short name for the connected client -/// @param version Dictionary describing the version, with these +/// @param version Dict describing the version, with these /// (optional) keys: /// - "major" major version (defaults to 0 if not set, for no release yet) /// - "minor" minor version @@ -1584,14 +1589,15 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena) /// /// @param attributes Arbitrary string:string map of informal client properties. /// Suggested keys: +/// - "pid": Process id. /// - "website": Client homepage URL (e.g. GitHub repository) /// - "license": License description ("Apache 2", "GPLv3", "MIT", …) /// - "logo": URI or path to image, preferably small logo or icon. /// .png or .svg format is preferred. /// /// @param[out] err Error details, if any -void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, String type, - Dictionary methods, Dictionary attributes, Arena *arena, Error *err) +void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String type, Dict methods, + Dict attributes, Arena *arena, Error *err) FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY { MAXSIZE_TEMP_DICT(info, 5); @@ -1605,7 +1611,7 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, } } if (!has_major) { - Dictionary v = arena_dict(arena, version.size + 1); + Dict v = arena_dict(arena, version.size + 1); if (version.size) { memcpy(v.items, version.items, version.size * sizeof(v.items[0])); v.size = version.size; @@ -1613,19 +1619,19 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, PUT_C(v, "major", INTEGER_OBJ(0)); version = v; } - PUT_C(info, "version", DICTIONARY_OBJ(version)); + PUT_C(info, "version", DICT_OBJ(version)); PUT_C(info, "type", STRING_OBJ(type)); - PUT_C(info, "methods", DICTIONARY_OBJ(methods)); - PUT_C(info, "attributes", DICTIONARY_OBJ(attributes)); + PUT_C(info, "methods", DICT_OBJ(methods)); + PUT_C(info, "attributes", DICT_OBJ(attributes)); - rpc_set_client_info(channel_id, copy_dictionary(info, NULL)); + rpc_set_client_info(channel_id, copy_dict(info, NULL)); } /// Gets information about a channel. /// /// @param chan channel_id, or 0 for current channel -/// @returns Dictionary describing a channel, with these keys: +/// @returns Channel info dict with these keys: /// - "id" Channel id. /// - "argv" (optional) Job arguments list. /// - "stream" Stream underlying the channel. @@ -1637,20 +1643,18 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, /// - "bytes" Send and receive raw bytes. /// - "terminal" |terminal| instance interprets ASCII sequences. /// - "rpc" |RPC| communication on the channel is active. -/// - "pty" (optional) Name of pseudoterminal. On a POSIX system this -/// is a device path like "/dev/pts/1". If the name is unknown, -/// the key will still be present if a pty is used (e.g. for -/// conpty on Windows). -/// - "buffer" (optional) Buffer with connected |terminal| instance. -/// - "client" (optional) Info about the peer (client on the other end of -/// the RPC channel), if provided by it via -/// |nvim_set_client_info()|. -/// -Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err) +/// - "pty" (optional) Name of pseudoterminal. On a POSIX system this is a device path like +/// "/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()|. +/// +Dict nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err) FUNC_API_SINCE(4) { if (chan < 0) { - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } if (chan == 0 && !is_internal_call(channel_id)) { @@ -1747,17 +1751,17 @@ Array nvim__id_array(Array arr, Arena *arena) return copy_array(arr, arena); } -/// Returns dictionary given as argument. +/// Returns dict given as argument. /// /// This API function is used for testing. One should not rely on its presence /// in plugins. /// -/// @param[in] dct Dictionary to return. +/// @param[in] dct Dict to return. /// /// @return its argument. -Dictionary nvim__id_dictionary(Dictionary dct, Arena *arena) +Dict nvim__id_dict(Dict dct, Arena *arena) { - return copy_dictionary(dct, arena); + return copy_dict(dct, arena); } /// Returns floating-point value given as argument. @@ -1776,9 +1780,9 @@ Float nvim__id_float(Float flt) /// Gets internal stats. /// /// @return Map of various internal stats. -Dictionary nvim__stats(Arena *arena) +Dict nvim__stats(Arena *arena) { - Dictionary rv = arena_dict(arena, 6); + Dict rv = arena_dict(arena, 6); PUT_C(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); PUT_C(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); PUT_C(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); @@ -1855,8 +1859,8 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) }); #ifdef MSWIN - rvobj = DICTIONARY_OBJ(os_proc_info((int)pid, arena)); - if (rvobj.data.dictionary.size == 0) { // Process not found. + rvobj = DICT_OBJ(os_proc_info((int)pid, arena)); + if (rvobj.data.dict.size == 0) { // Process not found. return NIL; } #else @@ -1866,7 +1870,7 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, kRetObject, arena, err); if (o.type == kObjectTypeArray && o.data.array.size == 0) { return NIL; // Process not found. - } else if (o.type == kObjectTypeDictionary) { + } else if (o.type == kObjectTypeDict) { rvobj = o; } else if (!ERROR_SET(err)) { api_set_error(err, kErrorTypeException, @@ -1930,7 +1934,7 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E schar_get(sc_buf, g->chars[off]); ADD_C(ret, CSTR_AS_OBJ(sc_buf)); int attr = g->attrs[off]; - ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err))); + ADD_C(ret, DICT_OBJ(hl_get_attr_by_id(attr, true, arena, err))); // will not work first time if (!highlight_use_hlstate()) { ADD_C(ret, ARRAY_OBJ(hl_inspect(attr, arena))); @@ -2073,18 +2077,18 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err) /// - use_statuscol_lnum: (number) Evaluate statuscolumn for this line number instead of statusline. /// /// @param[out] err Error details, if any. -/// @return Dictionary containing statusline information, with these keys: +/// @return Dict containing statusline information, with these keys: /// - str: (string) Characters that will be displayed on the statusline. /// - width: (number) Display width of the statusline. /// - highlights: Array containing highlight information of the statusline. Only included when /// the "highlights" key in {opts} is true. Each element of the array is a -/// |Dictionary| with these keys: +/// |Dict| with these keys: /// - start: (number) Byte index (0-based) of first character that uses the highlight. /// - group: (string) Name of highlight group. -Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err) +Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) FUNC_API_FAST { - Dictionary result = ARRAY_DICT_INIT; + Dict result = ARRAY_DICT_INIT; int maxwidth; schar_T fillchar = 0; @@ -2210,18 +2214,18 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * // If first character doesn't have a defined highlight, // add the default highlight at the beginning of the highlight list if (hltab->start == NULL || (hltab->start - buf) != 0) { - Dictionary hl_info = arena_dict(arena, 2); + 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); PUT_C(hl_info, "start", INTEGER_OBJ(0)); PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); - ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); + ADD_C(hl_values, DICT_OBJ(hl_info)); } for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) { - Dictionary hl_info = arena_dict(arena, 2); + Dict hl_info = arena_dict(arena, 2); PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf)); @@ -2235,7 +2239,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena * grpname = arena_memdupz(arena, user_group, strlen(user_group)); } PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname)); - ADD_C(hl_values, DICTIONARY_OBJ(hl_info)); + ADD_C(hl_values, DICT_OBJ(hl_info)); } PUT_C(result, "highlights", ARRAY_OBJ(hl_values)); } @@ -2261,12 +2265,12 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data) /// @param index Completion candidate index /// @param opts Optional parameters. /// - info: (string) info text. -/// @return Dictionary containing these keys: +/// @return Dict containing these keys: /// - winid: (number) floating window id /// - bufnr: (number) buffer id in floating window -Dictionary nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) +Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) { - Dictionary rv = arena_dict(arena, 2); + Dict rv = arena_dict(arena, 2); if (HAS_KEY(opts, complete_set, info)) { win_T *wp = pum_set_info((int)index, opts->info.data); if (wp) { @@ -2354,8 +2358,8 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) } } - int count = (win != NULL) + (buf != NULL); - VALIDATE(popcount(opts->is_set__redraw_) > count, "%s", "at least one action required", { + unsigned count = (win != NULL) + (buf != NULL); + VALIDATE(xpopcount(opts->is_set__redraw_) > count, "%s", "at least one action required", { return; }); @@ -2392,10 +2396,6 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) redraw_buf_range_later(rbuf, first, last); } - if (opts->cursor) { - setcursor_mayforce(win ? win : curwin, true); - } - bool flush = opts->flush; if (opts->tabline) { // Flush later in case tabline was just hidden or shown for the first time. @@ -2422,11 +2422,22 @@ void nvim__redraw(Dict(redraw) *opts, Error *err) } } - // Flush pending screen updates if "flush" or "clear" is true, or when - // redrawing a status component may have changed the grid dimensions. + 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; + } + + // Redraw pending screen updates when explicitly requested or when determined + // that it is necessary to properly draw other requested components. if (flush && !cmdpreview) { update_screen(); } + + if (opts->cursor) { + setcursor_mayforce(cwin, true); + } + ui_flush(); RedrawingDisabled = save_rd; diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 477cbe2428..165cc93fbe 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -48,12 +48,12 @@ /// - output: (boolean, default false) Whether to capture and return /// all (non-error, non-shell |:!|) output. /// @param[out] err Error details (Vim error), if any -/// @return Dictionary containing information about execution, with these keys: +/// @return Dict containing information about execution, with these keys: /// - output: (string|nil) Output if `opts.output` is true. -Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err) +Dict nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err) FUNC_API_SINCE(11) FUNC_API_RET_ALLOC { - Dictionary result = ARRAY_DICT_INIT; + Dict result = ARRAY_DICT_INIT; String output = exec_impl(channel_id, src, opts, err); if (ERROR_SET(err)) { @@ -109,7 +109,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error * // redir usually (except :echon) prepends a newline. if (s.data[0] == '\n') { memmove(s.data, s.data + 1, s.size - 1); - s.data[s.size - 1] = '\0'; + s.data[s.size - 1] = NUL; s.size = s.size - 1; } return s; // Caller will free the memory. @@ -140,8 +140,7 @@ void nvim_command(String command, Error *err) try_end(err); } -/// Evaluates a Vimscript |expression|. -/// Dictionaries and Lists are recursively expanded. +/// Evaluates a Vimscript |expression|. Dicts and Lists are recursively expanded. /// /// On execution error: fails with Vimscript error, updates v:errmsg. /// @@ -270,7 +269,7 @@ Object nvim_call_function(String fn, Array args, Arena *arena, Error *err) /// /// On execution error: fails with Vimscript error, updates v:errmsg. /// -/// @param dict Dictionary, or String evaluating to a Vimscript |self| dict +/// @param dict Dict, or String evaluating to a Vimscript |self| dict /// @param fn Name of the function defined on the Vimscript dict /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any @@ -297,12 +296,11 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, // refcount of a dict. Not necessary for a RPC dict. mustfree = true; break; - case kObjectTypeDictionary: + case kObjectTypeDict: object_to_vim(dict, &rettv, err); break; default: - api_set_error(err, kErrorTypeValidation, - "dict argument type must be String or Dictionary"); + api_set_error(err, kErrorTypeValidation, "dict argument type must be String or Dict"); return rv; } dict_T *self_dict = rettv.vval.v_dict; @@ -311,7 +309,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, goto end; } - if (fn.data && fn.size > 0 && dict.type != kObjectTypeDictionary) { + if (fn.data && fn.size > 0 && dict.type != kObjectTypeDict) { dictitem_T *const di = tv_dict_find(self_dict, fn.data, (ptrdiff_t)fn.size); if (di == NULL) { api_set_error(err, kErrorTypeValidation, "Not found: %s", fn.data); @@ -377,8 +375,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// one should highlight region [start_col, end_col)). /// /// @return -/// - AST: top-level dictionary with these keys: -/// - "error": Dictionary with error, present only if parser saw some +/// - AST: top-level dict with these keys: +/// - "error": Dict with error, present only if parser saw some /// error. Contains the following keys: /// - "message": String, error message in printf format, translated. /// Must contain exactly one "%.*s". @@ -387,7 +385,7 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// that should be equal to the length of expr string. /// ("Successfully parsed" here means "participated in AST /// creation", not "till the first error".) -/// - "ast": AST, either nil or a dictionary with these keys: +/// - "ast": AST, either nil or a dict with these keys: /// - "type": node type, one of the value names from ExprASTNodeType /// stringified without "kExprNode" prefix. /// - "start": a pair `[line, column]` describing where node is "started" @@ -427,8 +425,7 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "svalue": String, value for "SingleQuotedString" and /// "DoubleQuotedString" nodes. /// @param[out] err Error details, if any -Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena, - Error *err) +Dict nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena, Error *err) FUNC_API_SINCE(4) FUNC_API_FAST { int pflags = 0; @@ -443,11 +440,11 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, A case NUL: api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)", (unsigned)flags.data[i]); - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; default: api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)", flags.data[i], (unsigned)flags.data[i]); - return (Dictionary)ARRAY_DICT_INIT; + return (Dict)ARRAY_DICT_INIT; } } ParserLine parser_lines[] = { @@ -471,15 +468,15 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, A + (size_t)highlight // "highlight" + 0); - Dictionary ret = arena_dict(arena, ret_size); + Dict ret = arena_dict(arena, ret_size); PUT_C(ret, "len", INTEGER_OBJ((Integer)(pstate.pos.line == 1 ? parser_lines[0].size : pstate.pos.col))); if (east.err.msg != NULL) { - Dictionary err_dict = arena_dict(arena, 2); + Dict err_dict = arena_dict(arena, 2); PUT_C(err_dict, "message", CSTR_TO_ARENA_OBJ(arena, east.err.msg)); PUT_C(err_dict, "arg", CBUF_TO_ARENA_OBJ(arena, east.err.arg, (size_t)east.err.arg_len)); - PUT_C(ret, "error", DICTIONARY_OBJ(err_dict)); + PUT_C(ret, "error", DICT_OBJ(err_dict)); } if (highlight) { Array hl = arena_array(arena, kv_size(colors)); @@ -530,10 +527,10 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, A || node->type == kExprNodeSingleQuotedString) // "svalue" + (node->type == kExprNodeAssignment) // "augmentation" + 0); - Dictionary ret_node = arena_dict(arena, items_size); - *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node); + Dict ret_node = arena_dict(arena, items_size); + *cur_item.ret_node_p = DICT_OBJ(ret_node); } - Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary; + Dict *ret_node = &cur_item.ret_node_p->data.dict; if (node->children != NULL) { const size_t num_children = 1 + (node->children->next != NULL); Array children_array = arena_array(arena, num_children); @@ -638,8 +635,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, A case kExprNodeMod: break; } - assert(cur_item.ret_node_p->data.dictionary.size - == cur_item.ret_node_p->data.dictionary.capacity); + assert(cur_item.ret_node_p->data.dict.size == cur_item.ret_node_p->data.dict.capacity); xfree(*cur_item.node_p); *cur_item.node_p = NULL; } diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 3a9986a7d1..f63fdc5381 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -17,6 +17,7 @@ #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" @@ -189,13 +190,13 @@ /// ``` /// - title: Title (optional) in window border, string or list. /// List should consist of `[text, highlight]` tuples. -/// If string, the default highlight group is `FloatTitle`. +/// If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`. /// - title_pos: Title position. Must be set with `title` option. /// Value can be one of "left", "center", or "right". /// Default is `"left"`. /// - footer: Footer (optional) in window border, string or list. /// List should consist of `[text, highlight]` tuples. -/// If string, the default highlight group is `FloatFooter`. +/// If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`. /// - footer_pos: Footer position. Must be set with `footer` option. /// Value can be one of "left", "center", or "right". /// Default is `"left"`. @@ -447,7 +448,7 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) } } } - win->w_config = fconfig; + merge_win_config(&win->w_config, fconfig); // If there's no "vertical" or "split" set, or if "split" is unchanged, // then we can just change the size of the window. @@ -851,27 +852,16 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, bool *is_present; VirtText *chunks; int *width; - int default_hl_id; switch (bordertext_type) { case kBorderTextTitle: - if (fconfig->title) { - clear_virttext(&fconfig->title_chunks); - } - is_present = &fconfig->title; chunks = &fconfig->title_chunks; width = &fconfig->title_width; - default_hl_id = syn_check_group(S_LEN("FloatTitle")); break; case kBorderTextFooter: - if (fconfig->footer) { - clear_virttext(&fconfig->footer_chunks); - } - is_present = &fconfig->footer; chunks = &fconfig->footer_chunks; width = &fconfig->footer_width; - default_hl_id = syn_check_group(S_LEN("FloatFooter")); break; } @@ -880,8 +870,9 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, *is_present = false; return; } + kv_init(*chunks); kv_push(*chunks, ((VirtTextChunk){ .text = xstrdup(bordertext.data.string.data), - .hl_id = default_hl_id })); + .hl_id = -1 })); *width = (int)mb_string2cells(bordertext.data.string.data); *is_present = true; return; @@ -1040,7 +1031,7 @@ static void parse_border_style(Object style, WinConfig *fconfig, Error *err) static void generate_api_error(win_T *wp, const char *attribute, Error *err) { - if (wp->w_floating) { + if (wp != NULL && wp->w_floating) { api_set_error(err, kErrorTypeValidation, "Missing 'relative' field when reconfiguring floating window %d", wp->handle); @@ -1057,13 +1048,13 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (config->relative.size > 0) { if (!parse_float_relative(config->relative, &fconfig->relative)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); - return false; + goto fail; } if (config->relative.size > 0 && !(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) { api_set_error(err, kErrorTypeValidation, "'relative' requires 'row'/'col' or 'bufpos'"); - return false; + goto fail; } has_relative = true; @@ -1078,39 +1069,39 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco } else if (wp == NULL) { // new win api_set_error(err, kErrorTypeValidation, "Must specify 'relative' or 'external' when creating a float"); - return false; + goto fail; } } if (HAS_KEY_X(config, vertical)) { if (!is_split) { api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'vertical'"); - return false; + goto fail; } } if (HAS_KEY_X(config, split)) { if (!is_split) { api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'split'"); - return false; + goto fail; } if (!parse_config_split(config->split, &fconfig->split)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'split' key"); - return false; + goto fail; } } if (HAS_KEY_X(config, anchor)) { if (!parse_float_anchor(config->anchor, &fconfig->anchor)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key"); - return false; + goto fail; } } if (HAS_KEY_X(config, row)) { if (!has_relative || is_split) { generate_api_error(wp, "row", err); - return false; + goto fail; } fconfig->row = config->row; } @@ -1118,7 +1109,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (HAS_KEY_X(config, col)) { if (!has_relative || is_split) { generate_api_error(wp, "col", err); - return false; + goto fail; } fconfig->col = config->col; } @@ -1126,11 +1117,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (HAS_KEY_X(config, bufpos)) { if (!has_relative || is_split) { generate_api_error(wp, "bufpos", err); - return false; + goto fail; } else { if (!parse_float_bufpos(config->bufpos, &fconfig->bufpos)) { api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key"); - return false; + goto fail; } if (!HAS_KEY_X(config, row)) { @@ -1147,11 +1138,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco fconfig->width = (int)config->width; } else { api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); - return false; + goto fail; } } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); - return false; + goto fail; } if (HAS_KEY_X(config, height)) { @@ -1159,23 +1150,23 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco fconfig->height = (int)config->height; } else { api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); - return false; + goto fail; } } else if (!reconf && !is_split) { api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); - return false; + goto fail; } if (relative_is_win || is_split) { if (reconf && relative_is_win) { win_T *target_win = find_window_by_handle(config->win, err); if (!target_win) { - return false; + goto fail; } if (target_win == wp) { api_set_error(err, kErrorTypeException, "floating window cannot be relative to itself"); - return false; + goto fail; } } fconfig->window = curwin->handle; @@ -1188,11 +1179,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (has_relative) { api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win' and relative=''"); - return false; + goto fail; } else if (!is_split) { api_set_error(err, kErrorTypeValidation, "non-float with 'win' requires at least 'split' or 'vertical'"); - return false; + goto fail; } } @@ -1201,11 +1192,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (has_relative && fconfig->external) { api_set_error(err, kErrorTypeValidation, "Only one of 'relative' and 'external' must be used"); - return false; + goto fail; } if (fconfig->external && !ui_has(kUIMultigrid)) { api_set_error(err, kErrorTypeValidation, "UI doesn't support external windows"); - return false; + goto fail; } } @@ -1216,78 +1207,78 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco if (HAS_KEY_X(config, zindex)) { if (is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'zindex'"); - return false; + goto fail; } if (config->zindex > 0) { fconfig->zindex = (int)config->zindex; } else { api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer"); - return false; + goto fail; } } if (HAS_KEY_X(config, title)) { if (is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'title'"); - return false; + goto fail; } // title only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "title requires border to be set"); - return false; + goto fail; } parse_bordertext(config->title, kBorderTextTitle, fconfig, err); if (ERROR_SET(err)) { - return false; + goto fail; } // handles unset 'title_pos' same as empty string if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) { - return false; + goto fail; } } else { if (HAS_KEY_X(config, title_pos)) { api_set_error(err, kErrorTypeException, "title_pos requires title to be set"); - return false; + goto fail; } } if (HAS_KEY_X(config, footer)) { if (is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'footer'"); - return false; + goto fail; } // footer only work with border if (!HAS_KEY_X(config, border) && !fconfig->border) { api_set_error(err, kErrorTypeException, "footer requires border to be set"); - return false; + goto fail; } parse_bordertext(config->footer, kBorderTextFooter, fconfig, err); if (ERROR_SET(err)) { - return false; + goto fail; } // handles unset 'footer_pos' same as empty string if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) { - return false; + goto fail; } } else { if (HAS_KEY_X(config, footer_pos)) { api_set_error(err, kErrorTypeException, "footer_pos requires footer to be set"); - return false; + goto fail; } } if (HAS_KEY_X(config, border)) { if (is_split) { api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'"); - return false; + goto fail; } parse_border_style(config->border, fconfig, err); if (ERROR_SET(err)) { - return false; + goto fail; } } @@ -1298,14 +1289,14 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco fconfig->style = kWinStyleMinimal; } else { api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key"); - return false; + goto fail; } } if (HAS_KEY_X(config, noautocmd)) { if (wp) { api_set_error(err, kErrorTypeValidation, "'noautocmd' cannot be used with existing windows"); - return false; + goto fail; } fconfig->noautocmd = config->noautocmd; } @@ -1319,5 +1310,9 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco } return true; + +fail: + merge_win_config(fconfig, wp != NULL ? wp->w_config : WIN_CONFIG_INIT); + return false; #undef HAS_KEY_X } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 54a19513db..5a4972ef23 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -13,6 +13,7 @@ #include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" +#include "nvim/errors.h" #include "nvim/eval/window.h" #include "nvim/ex_docmd.h" #include "nvim/gettext_defs.h" @@ -502,16 +503,15 @@ void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err) /// - end_vcol: Ending virtual column index on "end_row", /// 0-based exclusive, rounded up to full screen lines. /// When omitted include the whole line. -/// @return Dictionary containing text height information, with these keys: +/// @return Dict containing text height information, with these keys: /// - all: The total number of screen lines occupied by the range. /// - fill: The number of diff filler or virtual lines among them. /// /// @see |virtcol()| for text width. -Dictionary nvim_win_text_height(Window window, Dict(win_text_height) *opts, Arena *arena, - Error *err) +Dict nvim_win_text_height(Window window, Dict(win_text_height) *opts, Arena *arena, Error *err) FUNC_API_SINCE(12) { - Dictionary rv = arena_dict(arena, 2); + Dict rv = arena_dict(arena, 2); win_T *const win = find_window_by_handle(window, err); if (!win) { |