From c8656e44d85502a1733df839b3cb3e8f239c5505 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 9 Jan 2022 09:02:02 +0800 Subject: feat(api, lua): more conversions between LuaRef and Vim Funcref --- src/nvim/api/private/converter.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 36da6c13a9..e370c0d4d4 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -10,6 +10,9 @@ #include "nvim/api/private/helpers.h" #include "nvim/assert.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" /// Helper structure for vim_to_object typedef struct { @@ -228,6 +231,13 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { + if (obj->v_type == VAR_FUNC) { + ufunc_T *fp = find_func(obj->vval.v_string); + if (fp->uf_cb == nlua_CFunction_func_call) { + LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); + return LUAREF_OBJ(ref); + } + } EncodedData edata; kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, @@ -340,6 +350,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) tv->vval.v_dict = dict; break; } + + case kObjectTypeLuaRef: { + LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); + state->lua_callable.func_ref = api_new_luaref(obj.data.luaref); + char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + default: abort(); } -- cgit From 95ab979fde66d8f9f97fceb943bfe9422739a0f8 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 25 Oct 2021 21:51:29 +0200 Subject: refactor(extmarks): use a more efficient representation marktree.c was originally constructed as a "generic" datatype, to make the prototyping of its internal logic as simple as possible and also as the usecases for various kinds of extmarks/decorations was not yet decided. As a consequence of this, various extra indirections and allocations was needed to use marktree to implement extmarks (ns/id pairs) and decorations of different kinds (some which is just a single highlight id, other an allocated list of virtual text/lines) This change removes a lot of indirection, by making Marktree specialized for the usecase. In particular, the namespace id and mark id is stored directly, instead of the 64-bit global id particular to the Marktree struct. This removes the two maps needed to convert between global and per-ns ids. Also, "small" decorations are stored inline, i.e. those who doesn't refer to external heap memory anyway. That is highlights (with priority+flags) are stored inline, while virtual text, which anyway occurs a lot of heap allocations, do not. (previously a hack was used to elide heap allocations for highlights with standard prio+flags) TODO(bfredl): the functionaltest-lua CI version of gcc is having severe issues with uint16_t bitfields, so splitting up compound assignments and redundant casts are needed. Clean this up once we switch to a working compiler version. --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f9603acbda..f777fa1d27 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1141,7 +1141,7 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int return false; } - ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id); + ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); if (extmark.row >= 0) { *row = extmark.row; *col = extmark.col; -- cgit From 0c541ab1f661f17aef317232483d1464ad13ef36 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 19 Jan 2022 21:42:10 -0500 Subject: refactor(coverity/345582): assert fp is non-NULL Since we already have a typval, we know the lookup will succeed. --- src/nvim/api/private/converter.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index e370c0d4d4..3d4ff202fe 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -233,6 +233,7 @@ Object vim_to_object(typval_T *obj) { if (obj->v_type == VAR_FUNC) { ufunc_T *fp = find_func(obj->vval.v_string); + assert(fp != NULL); if (fp->uf_cb == nlua_CFunction_func_call) { LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); return LUAREF_OBJ(ref); -- cgit From 8e84d1b93043f33d997627f99a181159aa66434d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:18:18 +0800 Subject: vim-patch:8.2.3584: "verbose set efm" reports location of the :compiler command Problem: "verbose set efm" reports the location of the :compiler command. (Gary Johnson) Solution: Add the "-keepscript" argument to :command and use it when defining CompilerSet. https://github.com/vim/vim/commit/58ef8a31d7087d495ab1582be5b7a22796ac2451 --- src/nvim/api/private/helpers.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f777fa1d27..f540f8f7ee 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1510,6 +1510,12 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int goto err; } + if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) { + argt |= EX_KEEPSCRIPT; + } else if (ERROR_SET(err)) { + goto err; + } + bool force = api_object_to_bool(opts->force, "force", true, err); if (ERROR_SET(err)) { goto err; -- cgit From 2793fcae0ac2aef14c8f22636d579c68a97c0851 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Fri, 28 Jan 2022 22:54:03 +0100 Subject: vim-patch:8.2.4241: some type casts are redundant Problem: Some type casts are redundant. Solution: Remove the type casts. (closes vim/vim#9643) https://github.com/vim/vim/commit/420fabcd4ffeaf79082a6e43db91e1d363f88f27 This is not a literal port but an equivalent one. --- src/nvim/api/private/helpers.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f540f8f7ee..4ddd90f3c9 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -139,10 +139,10 @@ bool try_end(Error *err) got_int = false; } else if (msg_list != NULL && *msg_list != NULL) { int should_free; - char *msg = (char *)get_exception_string(*msg_list, - ET_ERROR, - NULL, - &should_free); + char *msg = get_exception_string(*msg_list, + ET_ERROR, + NULL, + &should_free); api_set_error(err, kErrorTypeException, "%s", msg); free_global_msglist(); @@ -720,7 +720,6 @@ fail_and_free: xfree(parsed_args.rhs); xfree(parsed_args.orig_rhs); XFREE_CLEAR(parsed_args.desc); - return; } /// Collects `n` buffer lines into array `l`, optionally replacing newlines -- cgit From f326c9a77da3aef052ce6af0f851344f2479c844 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 6 Jan 2022 13:48:37 +0000 Subject: vim-patch:8.2.4018: ml_get error when win_execute redraws with Visual selection Problem: ml_get error when win_execute redraws with Visual selection. Solution: Disable Visual area temporarily. (closes vim/vim#9479) https://github.com/vim/vim/commit/18f4740f043b353abe47b7a00131317052457686 {switch_to/restore}_win_for_buf is N/A (marked as such in v8.0.0860; currently only used in Vim's if_py). Add a modeline to test_execute_func.vim. --- src/nvim/api/private/helpers.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 4ddd90f3c9..ddcfff0097 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -989,18 +989,16 @@ Object copy_object(Object obj) static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags, int opt_type, void *from, Error *err) { - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; + switchwin_T switchwin; aco_save_T aco; try_start(); switch (opt_type) { case SREQ_WIN: - if (switch_win_noblock(&save_curwin, &save_curtab, (win_T *)from, - win_find_tabpage((win_T *)from), true) + if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) == FAIL) { - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); if (try_end(err)) { return; } @@ -1010,7 +1008,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt return; } set_option_value_err(key, numval, stringval, opt_flags, err); - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); break; case SREQ_BUF: aucmd_prepbuf(&aco, (buf_T *)from); -- cgit From 238b944e58d12a28245be996e69bf36a2a452a90 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 15 Feb 2022 13:08:40 -0700 Subject: fix(api): validate command names in nvim_add_user_command (#17406) This uses the same validation used when defining commands with `:command`. --- src/nvim/api/private/helpers.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ddcfff0097..2b107a3f27 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1384,6 +1384,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + if (!uc_validate_name(name.data)) { + api_set_error(err, kErrorTypeValidation, "Invalid command name"); + goto err; + } + if (mb_islower(name.data[0])) { api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); goto err; -- cgit From 11f7aeed7aa83d342d19897d9a69ba9f32ece7f7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 22 Feb 2022 13:19:21 -0700 Subject: feat(api): implement nvim_buf_get_text (#15181) nvim_buf_get_text is the mirror of nvim_buf_set_text. It differs from nvim_buf_get_lines in that it allows retrieving only portions of lines. While this can typically be done easily enough by API clients, implementing this function provides symmetry between the get/set text/lines APIs, and also provides a nice convenience that saves API clients the work of having to slice the result of nvim_buf_get_lines themselves. --- src/nvim/api/private/helpers.c | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2b107a3f27..971fa1cb0f 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -411,7 +411,6 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object current_sctx = save_current_sctx; } - buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -758,6 +757,52 @@ bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Arr return true; } +/// Returns a substring of a buffer line +/// +/// @param buf Buffer handle +/// @param lnum Line number (1-based) +/// @param start_col Starting byte offset into line (0-based) +/// @param end_col Ending byte offset into line (0-based, exclusive) +/// @param replace_nl Replace newlines ('\n') with null ('\0') +/// @param err Error object +/// @return The text between start_col and end_col on line lnum of buffer buf +String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, bool replace_nl, + Error *err) +{ + String rv = STRING_INIT; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Line index is too high"); + return rv; + } + + const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false); + size_t line_length = strlen(bufstr); + + start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col; + end_col = end_col < 0 ? (int64_t)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; + } + + if (start_col > end_col) { + api_set_error(err, kErrorTypeValidation, "start_col must be less than end_col"); + return rv; + } + + if ((size_t)start_col >= line_length) { + return rv; + } + + rv = cstrn_to_string(&bufstr[start_col], (size_t)(end_col - start_col)); + if (replace_nl) { + strchrsub(rv.data, '\n', '\0'); + } + + return rv; +} void api_free_string(String value) { -- cgit From f6cc604af2a4d95ec9bcaa5bee705cec2e06d541 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 26 Feb 2022 13:27:43 +0000 Subject: fix(api): convert blob to NUL-terminated API string Looks like I did an oopsie; although API strings carry a size field, they should still be usable as C-strings! (even though they may contain embedded NULs) --- src/nvim/api/private/converter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 3d4ff202fe..49e3cf7df7 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -57,7 +57,7 @@ typedef struct { const size_t len_ = (size_t)(len); \ const blob_T *const blob_ = (blob); \ kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \ + .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \ .size = len_ \ }))); \ } while (0) -- cgit From 991e472881bf29805982b402c1a010cde051ded3 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Fri, 28 May 2021 15:45:34 -0400 Subject: feat(lua): add api and lua autocmds --- src/nvim/api/private/dispatch.c | 20 ++++++++++++++------ src/nvim/api/private/helpers.c | 34 +++++++++++++++++++++------------- src/nvim/api/private/helpers.h | 17 +++++++++++++++++ 3 files changed, 52 insertions(+), 19 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 8ab7743e01..f670f06357 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -6,22 +6,30 @@ #include #include -#include "nvim/api/buffer.h" #include "nvim/api/deprecated.h" -#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" +#include "nvim/log.h" +#include "nvim/map.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/vim.h" + +// =========================================================================== +// NEW API FILES MUST GO HERE. +// +// When creating a new API file, you must include it here, +// so that the dispatcher can find the C functions that you are creating! +// =========================================================================== +#include "nvim/api/autocmd.h" +#include "nvim/api/buffer.h" +#include "nvim/api/extmark.h" #include "nvim/api/tabpage.h" #include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" -#include "nvim/log.h" -#include "nvim/map.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/vim.h" static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 971fa1cb0f..3d4a04f096 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -396,19 +396,14 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object stringval = value.data.string.data; } - const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_sid = - channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; - current_sctx.sc_lnum = 0; - current_channel_id = channel_id; - - const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) - ? 0 : (type == SREQ_GLOBAL) - ? OPT_GLOBAL : OPT_LOCAL; - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); - - current_sctx = save_current_sctx; + WITH_SCRIPT_CONTEXT(channel_id, { + const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) + ? 0 : (type == SREQ_GLOBAL) + ? OPT_GLOBAL : OPT_LOCAL; + + set_option_value_for(name.data, numval, stringval, + opt_flags, type, to, err); + }); } buf_T *find_buffer_by_handle(Buffer buffer, Error *err) @@ -1614,3 +1609,16 @@ err: NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); } + +int find_sid(uint64_t channel_id) +{ + switch (channel_id) { + case VIML_INTERNAL_CALL: + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; + case LUA_INTERNAL_CALL: + return SID_LUA; + default: + return SID_API_CLIENT; + } +} diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 6d0aec9c90..6969994c3b 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -138,10 +138,27 @@ typedef struct { msg_list = saved_msg_list; /* Restore the exception context. */ \ } while (0) +// Useful macro for executing some `code` for each item in an array. +#define FOREACH_ITEM(a, __foreach_item, code) \ + for (size_t __foreach_i = 0; __foreach_i < (a).size; __foreach_i++) { \ + Object __foreach_item = (a).items[__foreach_i]; \ + code; \ + } + + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" # include "keysets.h.generated.h" #endif +#define WITH_SCRIPT_CONTEXT(channel_id, code) \ + const sctx_T save_current_sctx = current_sctx; \ + current_sctx.sc_sid = \ + (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ + current_sctx.sc_lnum = 0; \ + current_channel_id = channel_id; \ + code; \ + current_sctx = save_current_sctx; + #endif // NVIM_API_PRIVATE_HELPERS_H -- cgit From ebfe083337701534887ac3ea3d8e7ad47f7a206a Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sat, 8 Jan 2022 00:39:44 +0600 Subject: feat(lua): show proper verbose output for lua configuration `:verbose` didn't work properly with lua configs (For example: options or keymaps are set from lua, just say that they were set from lua, doesn't say where they were set at. This fixes that issue. Now `:verbose` will provide filename and line no when option/keymap is set from lua. Changes: - compiles lua/vim/keymap.lua as vim/keymap.lua - When souring a lua file current_sctx.sc_sid is set to SID_LUA - Moved finding scripts SID out of `do_source()` to `get_current_script_id()`. So it can be reused for lua files. - Added new function `nlua_get_sctx` that extracts current lua scripts name and line no with debug library. And creates a sctx for it. NOTE: This function ignores C functions and blacklist which currently contains only vim/_meta.lua so vim.o/opt wrappers aren't targeted. - Added function `nlua_set_sctx` that changes provided sctx to current lua scripts sctx if a lua file is being executed. - Added tests in tests/functional/lua/verbose_spec.lua - add primary support for additional types (:autocmd, :function, :syntax) to lua verbose Note: These can't yet be directly set from lua but once that's possible :verbose should work for them hopefully :D - add :verbose support for nvim_exec & nvim_command within lua Currently auto commands/commands/functions ... can only be defined by nvim_exec/nvim_command this adds support for them. Means if those Are defined within lua with vim.cmd/nvim_exec :verbose will show their location . Though note it'll show the line no on which nvim_exec call was made. --- src/nvim/api/private/helpers.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3d4a04f096..5198b00f65 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -585,8 +585,8 @@ Array string_to_array(const String input, bool crlf) /// @param buffer Buffer handle for a specific buffer, or 0 for the current /// buffer, or -1 to signify global behavior ("all buffers") /// @param is_unmap When true, removes the mapping that matches {lhs}. -void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs, - Dict(keymap) *opts, Error *err) +void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs, + String rhs, Dict(keymap) *opts, Error *err) { LuaRef lua_funcref = LUA_NOREF; bool global = (buffer == -1); @@ -599,6 +599,8 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String return; } + const sctx_T save_current_sctx = api_set_sctx(channel_id); + if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) { lua_funcref = opts->callback.data.luaref; opts->callback.data.luaref = LUA_NOREF; @@ -710,6 +712,7 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success fail_and_free: + current_sctx = save_current_sctx; NLUA_CLEAR_REF(parsed_args.rhs_lua); xfree(parsed_args.rhs); xfree(parsed_args.orig_rhs); @@ -1622,3 +1625,20 @@ int find_sid(uint64_t channel_id) return SID_API_CLIENT; } } + +/// Sets sctx for API calls. +/// +/// @param channel_id api clients id. Used to determine if it's a internal +/// call or a rpc call. +/// @return returns previous value of current_sctx. To be used +/// to be used for restoring sctx to previous state. +sctx_T api_set_sctx(uint64_t channel_id) +{ + sctx_T old_current_sctx = current_sctx; + if (channel_id != VIML_INTERNAL_CALL) { + current_sctx.sc_sid = + channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; + current_sctx.sc_lnum = 0; + } + return old_current_sctx; +} -- cgit From 0f613482b389ad259dd53d893907b024a115352e Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Fri, 28 May 2021 15:45:34 -0400 Subject: feat(lua): add missing changes to autocmds lost in the rebase Note: some of these changes are breaking, like change of API signatures --- src/nvim/api/private/helpers.c | 5 +++-- src/nvim/api/private/helpers.h | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3d4a04f096..c2106855d3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -406,6 +406,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } + buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -1614,8 +1615,8 @@ int find_sid(uint64_t channel_id) { switch (channel_id) { case VIML_INTERNAL_CALL: - // TODO(autocmd): Figure out what this should be - // return SID_API_CLIENT; + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; case LUA_INTERNAL_CALL: return SID_LUA; default: diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 6969994c3b..bc7c2e6a60 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -152,13 +152,15 @@ typedef struct { #endif #define WITH_SCRIPT_CONTEXT(channel_id, code) \ - const sctx_T save_current_sctx = current_sctx; \ - current_sctx.sc_sid = \ - (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ - current_sctx.sc_lnum = 0; \ - current_channel_id = channel_id; \ - code; \ - current_sctx = save_current_sctx; + do { \ + const sctx_T save_current_sctx = current_sctx; \ + current_sctx.sc_sid = \ + (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ + current_sctx.sc_lnum = 0; \ + current_channel_id = channel_id; \ + code; \ + current_sctx = save_current_sctx; \ + } while (0); #endif // NVIM_API_PRIVATE_HELPERS_H -- cgit From 30e4cc3b3f2133e9a7170da9da8175832681f39a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 3 Jan 2022 12:22:13 +0000 Subject: feat(decorations): support signs Add the following options to extmarks: - sign_text - sign_hl_group - number_hl_group - line_hl_group - cursorline_hl_group Note: ranges are unsupported and decorations are only applied to start_row --- src/nvim/api/private/helpers.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 35e8589255..8056950e26 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id) } return old_current_sctx; } + +// adapted from sign.c:sign_define_init_text. +// TODO(lewis6991): Consider merging +int init_sign_text(char_u **sign_text, char_u *text) +{ + char_u *s; + + char_u *endp = text + (int)STRLEN(text); + + // Count cells and check for non-printable chars + int cells = 0; + for (s = text; s < endp; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { + return FAIL; + } + if (cells < 1) { + return OK; + } + + // Allocate one byte more if we need to pad up + // with a space. + size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); + *sign_text = vim_strnsave(text, len); + + if (cells == 1) { + STRCPY(*sign_text + len - 1, " "); + } + + return OK; +} -- cgit From a4400bf8cda8ace4c4aab67bc73a1820478f46f1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 12 Mar 2022 13:47:50 +0100 Subject: feat(ui): connect to remote ui (only debug messages for now) co-authored-by: hlpr98 --- src/nvim/api/private/dispatch.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index f670f06357..ba2e560d63 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -30,6 +30,7 @@ #include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" +#include "nvim/ui_client.h" static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; @@ -38,6 +39,13 @@ static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandl map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); } +void msgpack_rpc_add_redraw(void) +{ + msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"), + (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw, + .fast = true }); +} + /// @param name API method name /// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, -- cgit From 4ba12b3dda34472c193c9fa8ffd7d3bd5b6c04d6 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 13 Mar 2022 15:11:17 +0100 Subject: refactor: fix clint warnings (#17682) --- src/nvim/api/private/helpers.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8056950e26..9f41393c6b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -111,7 +111,7 @@ bool try_leave(const TryState *const tstate, Error *const err) /// try_enter()/try_leave() pair should be used instead. void try_start(void) { - ++trylevel; + trylevel++; } /// End try block, set the error message if any and return true if an error @@ -1037,8 +1037,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt aco_save_T aco; try_start(); - switch (opt_type) - { + switch (opt_type) { case SREQ_WIN: if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) == FAIL) { -- cgit From cac90d2de728181edce7ba38fb9ad588d231651b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Mar 2022 03:21:47 +0800 Subject: feat(api, lua): support converting nested Funcref back to LuaRef (#17749) --- src/nvim/api/private/converter.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 49e3cf7df7..82ec1ad0d8 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -64,7 +64,14 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ + ufunc_T *fp = find_func(fun); \ + assert(fp != NULL); \ + if (fp->uf_cb == nlua_CFunction_func_call) { \ + LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + kvi_push(edata->stack, LUAREF_OBJ(ref)); \ + } else { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + } \ goto typval_encode_stop_converting_one_item; \ } while (0) @@ -231,14 +238,6 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { - if (obj->v_type == VAR_FUNC) { - ufunc_T *fp = find_func(obj->vval.v_string); - assert(fp != NULL); - if (fp->uf_cb == nlua_CFunction_func_call) { - LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); - return LUAREF_OBJ(ref); - } - } EncodedData edata; kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, -- cgit From 00effff56944d5b59440dcdb5e3496d49a76d3e2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 04:47:08 +0000 Subject: vim-patch:8.1.1693: syntax coloring and highlighting is in one big file (#17721) Problem: Syntax coloring and highlighting is in one big file. Solution: Move the highlighting to a separate file. (Yegappan Lakshmanan, closes vim/vim#4674) https://github.com/vim/vim/commit/f9cc9f209ede9f15959e4c2351e970477c139614 Name the new file highlight_group.c instead. Co-authored-by: zeertzjq --- src/nvim/api/private/helpers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9f41393c6b..88954a1aa2 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -22,6 +22,7 @@ #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/lua/executor.h" #include "nvim/map.h" @@ -32,7 +33,6 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/option_defs.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim.h" @@ -1293,7 +1293,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) { if (obj.type == kObjectTypeString) { String str = obj.data.string; - return str.size ? syn_check_group(str.data, (int)str.size) : 0; + return str.size ? syn_check_group(str.data, str.size) : 0; } else if (obj.type == kObjectTypeInteger) { return MAX((int)obj.data.integer, 0); } else { @@ -1327,7 +1327,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err) String hl = chunk.items[1].data.string; if (hl.size > 0) { // TODO(bfredl): use object_to_hl_id and allow integer - int hl_id = syn_check_group(hl.data, (int)hl.size); + int hl_id = syn_check_group(hl.data, hl.size); attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; } } -- cgit From 77eb6f9dc75ebe00aa835441ad623ba46d7108bb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 20 Mar 2022 08:08:50 +0800 Subject: fix(api, lua): return NIL on failure to find converted function (#17779) --- src/nvim/api/private/converter.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 82ec1ad0d8..a26383ec7d 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -65,8 +65,7 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ ufunc_T *fp = find_func(fun); \ - assert(fp != NULL); \ - if (fp->uf_cb == nlua_CFunction_func_call) { \ + if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \ LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ kvi_push(edata->stack, LUAREF_OBJ(ref)); \ } else { \ -- cgit From b80651eda9c50d4e438f02af9311b18c5c202656 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Sat, 12 Mar 2022 15:12:02 -0500 Subject: feat(api): nvim_clear_autocmd Co-authored-by: Christian Clason --- src/nvim/api/private/helpers.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index bc7c2e6a60..650349cde7 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -140,8 +140,9 @@ typedef struct { // Useful macro for executing some `code` for each item in an array. #define FOREACH_ITEM(a, __foreach_item, code) \ - for (size_t __foreach_i = 0; __foreach_i < (a).size; __foreach_i++) { \ - Object __foreach_item = (a).items[__foreach_i]; \ + for (size_t (__foreach_item ## _index) = 0; (__foreach_item ## _index) < (a).size; \ + (__foreach_item ## _index)++) { \ + Object __foreach_item = (a).items[__foreach_item ## _index]; \ code; \ } -- cgit From f94f75dc0512def7fbfdfe100eea2dab3352d61f Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Sun, 10 Apr 2022 19:12:41 -0600 Subject: refactor!: rename nvim_add_user_command to nvim_create_user_command --- src/nvim/api/private/helpers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 88954a1aa2..5ba4700f62 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1416,7 +1416,8 @@ const char *get_default_stl_hl(win_T *wp) } } -void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err) +void create_user_command(String name, Object command, Dict(user_command) *opts, int flags, + Error *err) { uint32_t argt = 0; long def = -1; -- cgit From dde4f09f51ffaf8df5cc2a81eed935e31e1f94ba Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Mar 2022 15:47:53 +0800 Subject: vim-patch:8.1.2145: cannot map when modifyOtherKeys is enabled Problem: Cannot map when modifyOtherKeys is enabled. Solution: Add the mapping twice, both with modifier and as 0x08. Use only the first one when modifyOtherKeys has been detected. https://github.com/vim/vim/commit/459fd785e4a8d044147a3f83a5fca8748528aa84 Add REPTERM_NO_SPECIAL instead of REPTERM_SPECIAL because the meaning of "special" is different between Vim and Nvim. Omit seenModifyOtherKeys as Nvim supports attaching multiple UIs. Omit tests as they send terminal codes. Keep the behavior of API functions. --- src/nvim/api/private/helpers.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 5ba4700f62..8383f29400 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -632,7 +632,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod } else { parsed_args.desc = NULL; } - if (parsed_args.lhs_len > MAXMAPLEN) { + if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) { api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data); goto fail_and_free; } @@ -1128,6 +1128,9 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) for (const mapblock_T *current_maphash = get_maphash(i, buf); current_maphash; current_maphash = current_maphash->m_next) { + if (current_maphash->m_simplified) { + continue; + } // Check for correct mode if (int_mode & current_maphash->m_mode) { mapblock_fill_dict(dict, current_maphash, buffer_value, false); -- cgit From 0b3ae64480ea28bb57783c2269a61f0a60ffc55e Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Fri, 29 Apr 2022 13:52:43 +0200 Subject: refactor(uncrustify): format all c code under /src/nvim/ --- src/nvim/api/private/helpers.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 650349cde7..bbbc3de7d5 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -140,9 +140,9 @@ typedef struct { // Useful macro for executing some `code` for each item in an array. #define FOREACH_ITEM(a, __foreach_item, code) \ - for (size_t (__foreach_item ## _index) = 0; (__foreach_item ## _index) < (a).size; \ - (__foreach_item ## _index)++) { \ - Object __foreach_item = (a).items[__foreach_item ## _index]; \ + for (size_t (__foreach_item##_index) = 0; (__foreach_item##_index) < (a).size; \ + (__foreach_item##_index)++) { \ + Object __foreach_item = (a).items[__foreach_item##_index]; \ code; \ } -- cgit From eef8de4df0247157e57f306062b1b86e01a41454 Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Fri, 29 Apr 2022 13:53:42 +0200 Subject: refactor(uncrustify): change rules to better align with the style guide Add space around arithmetic operators '+' and '-'. Remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. Remove space between '((' or '))' of control statements. Add space between ')' and '{' of control statements. Remove space between function name and '(' on function declaration. Collapse empty blocks between '{' and '}'. Remove newline at the end of the file. Remove newline between 'enum' and '{'. Remove newline between '}' and ')' in a function invocation. Remove newline between '}' and 'while' of 'do' statement. --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8383f29400..1c43564dca 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1245,7 +1245,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) if (ERROR_SET(err)) { goto free_exit; } - if (j < arr.size-1) { + if (j < arr.size - 1) { kv_push(virt_text, ((VirtTextChunk){ .text = NULL, .hl_id = hl_id })); } -- cgit From 3c23100130725bb79c04e933c505bbeda96fb3bb Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 30 Apr 2022 16:48:00 +0200 Subject: refactor: replace char_u variables and functions with char (#18288) Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/api/private/helpers.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1c43564dca..a07b5b6e3a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1526,7 +1526,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, } if (opts->addr.type == kObjectTypeString) { - if (parse_addr_type_arg((char_u *)opts->addr.data.string.data, (int)opts->addr.data.string.size, + if (parse_addr_type_arg(opts->addr.data.string.data, (int)opts->addr.data.string.size, &addr_type_arg) != OK) { api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'"); goto err; @@ -1574,9 +1574,9 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, compl = EXPAND_USER_LUA; compl_luaref = api_new_luaref(opts->complete.data.luaref); } else if (opts->complete.type == kObjectTypeString) { - if (parse_compl_arg((char_u *)opts->complete.data.string.data, + if (parse_compl_arg(opts->complete.data.string.data, (int)opts->complete.data.string.size, &compl, &argt, - (char_u **)&compl_arg) != OK) { + &compl_arg) != OK) { api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'"); goto err; } @@ -1603,9 +1603,8 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, goto err; } - if (uc_add_command((char_u *)name.data, name.size, (char_u *)rep, argt, def, flags, - compl, (char_u *)compl_arg, compl_luaref, addr_type_arg, luaref, - force) != OK) { + if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref, + addr_type_arg, luaref, force) != OK) { api_set_error(err, kErrorTypeException, "Failed to create user command"); goto err; } -- cgit From 13520aae163bfc243fc050cf16b89082c0896eaf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 3 May 2022 09:29:55 +0800 Subject: fix(coverity): use xstrndup() instead of vim_strsave() (#18363) --- src/nvim/api/private/helpers.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a07b5b6e3a..a89a254f20 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -488,6 +488,16 @@ String cstr_to_string(const char *str) }; } +/// Copies a String to an allocated, NUL-terminated C string. +/// +/// @param str the String to copy +/// @return the resulting C string +char *string_to_cstr(String str) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + return xstrndup(str.data, str.size); +} + /// Copies buffer to an allocated String. /// The resulting string is also NUL-terminated, to facilitate interoperating /// with code using C strings. @@ -628,7 +638,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod (char_u *)rhs.data, rhs.size, lua_funcref, CPO_TO_CPO_FLAGS, &parsed_args); if (opts != NULL && opts->desc.type == kObjectTypeString) { - parsed_args.desc = xstrdup(opts->desc.data.string.data); + parsed_args.desc = string_to_cstr(opts->desc.data.string); } else { parsed_args.desc = NULL; } -- cgit From 9a671e6a24243a5ff2879599d91ab5aec8b4e77d Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Wed, 4 May 2022 18:27:22 +0200 Subject: refactor: replace char_u variables and functions with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/api/private/converter.c | 5 +++-- src/nvim/api/private/helpers.c | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index a26383ec7d..8724ef4432 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -353,9 +353,10 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeLuaRef: { LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); state->lua_callable.func_ref = api_new_luaref(obj.data.luaref); - char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state); + char *name = + (char *)register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state); tv->v_type = VAR_FUNC; - tv->vval.v_string = vim_strsave(name); + tv->vval.v_string = xstrdup(name); break; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a89a254f20..9f894d5533 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -634,8 +634,8 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod } parsed_args.buffer = !global; - set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size, - (char_u *)rhs.data, rhs.size, lua_funcref, + set_maparg_lhs_rhs(lhs.data, lhs.size, + rhs.data, rhs.size, lua_funcref, CPO_TO_CPO_FLAGS, &parsed_args); if (opts != NULL && opts->desc.type == kObjectTypeString) { parsed_args.desc = string_to_cstr(opts->desc.data.string); @@ -652,16 +652,16 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod goto fail_and_free; } int mode_val; // integer value of the mapping mode, to be passed to do_map() - char_u *p = (char_u *)((mode.size) ? mode.data : "m"); + char *p = (mode.size) ? mode.data : "m"; if (STRNCMP(p, "!", 2) == 0) { - mode_val = get_map_mode(&p, true); // mapmode-ic + mode_val = get_map_mode((char_u **)&p, true); // mapmode-ic } else { - mode_val = get_map_mode(&p, false); + mode_val = get_map_mode((char_u **)&p, false); if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING) && mode.size > 0) { // get_map_mode() treats unrecognized mode shortnames as ":map". // This is an error unless the given shortname was empty string "". - api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", (char *)p); + api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p); goto fail_and_free; } } @@ -1269,7 +1269,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) } char *text = transstr(str.size > 0 ? str.data : "", false); // allocates - w += (int)mb_string2cells((char_u *)text); + w += (int)mb_string2cells(text); kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); } @@ -1658,19 +1658,19 @@ sctx_T api_set_sctx(uint64_t channel_id) // adapted from sign.c:sign_define_init_text. // TODO(lewis6991): Consider merging -int init_sign_text(char_u **sign_text, char_u *text) +int init_sign_text(char **sign_text, char *text) { - char_u *s; + char *s; - char_u *endp = text + (int)STRLEN(text); + char *endp = text + (int)STRLEN(text); // Count cells and check for non-printable chars int cells = 0; - for (s = text; s < endp; s += utfc_ptr2len(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { + for (s = text; s < endp; s += utfc_ptr2len((char_u *)s)) { + if (!vim_isprintc(utf_ptr2char((char_u *)s))) { break; } - cells += utf_ptr2cells(s); + cells += utf_ptr2cells((char_u *)s); } // Currently must be empty, one or two display cells if (s != endp || cells > 2) { @@ -1683,7 +1683,7 @@ int init_sign_text(char_u **sign_text, char_u *text) // Allocate one byte more if we need to pad up // with a space. size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); - *sign_text = vim_strnsave(text, len); + *sign_text = xstrnsave(text, len); if (cells == 1) { STRCPY(*sign_text + len - 1, " "); -- cgit From 2a378e6e82cececb12339f2df51ffe107039d867 Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Wed, 4 May 2022 22:35:50 +0200 Subject: refactor: replace char_u variables and functions with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/api/private/helpers.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9f894d5533..a2ee4f91bc 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -654,9 +654,9 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod int mode_val; // integer value of the mapping mode, to be passed to do_map() char *p = (mode.size) ? mode.data : "m"; if (STRNCMP(p, "!", 2) == 0) { - mode_val = get_map_mode((char_u **)&p, true); // mapmode-ic + mode_val = get_map_mode(&p, true); // mapmode-ic } else { - mode_val = get_map_mode((char_u **)&p, false); + mode_val = get_map_mode(&p, false); if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING) && mode.size > 0) { // get_map_mode() treats unrecognized mode shortnames as ":map". @@ -1128,7 +1128,7 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) // Convert the string mode to the integer mode // that is stored within each mapblock - char_u *p = (char_u *)mode.data; + char *p = mode.data; int int_mode = get_map_mode(&p, 0); // Determine the desired buffer value @@ -1666,11 +1666,11 @@ int init_sign_text(char **sign_text, char *text) // Count cells and check for non-printable chars int cells = 0; - for (s = text; s < endp; s += utfc_ptr2len((char_u *)s)) { - if (!vim_isprintc(utf_ptr2char((char_u *)s))) { + for (s = text; s < endp; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { break; } - cells += utf_ptr2cells((char_u *)s); + cells += utf_ptr2cells(s); } // Currently must be empty, one or two display cells if (s != endp || cells > 2) { -- cgit From 9aa5647e686e5420e5b9b51828ec7d55631f98ed Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 10 May 2022 07:58:58 +0800 Subject: vim-patch:8.2.4911: the mode #defines are not clearly named (#18499) Problem: The mode #defines are not clearly named. Solution: Prepend MODE_. Renumber them to put the mapped modes first. https://github.com/vim/vim/commit/249591057b4840785c50e41dd850efb8a8faf435 A hunk from the patch depends on patch 8.2.4861, which hasn't been ported yet, but that should be easy to notice. --- src/nvim/api/private/helpers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a2ee4f91bc..eb86964a72 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -657,8 +657,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod mode_val = get_map_mode(&p, true); // mapmode-ic } else { mode_val = get_map_mode(&p, false); - if ((mode_val == VISUAL + SELECTMODE + NORMAL + OP_PENDING) - && mode.size > 0) { + if (mode_val == (MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING) && mode.size > 0) { // get_map_mode() treats unrecognized mode shortnames as ":map". // This is an error unless the given shortname was empty string "". api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p); -- cgit From dfcc58466505f7fb5f62d636a67facaeea143285 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 8 May 2022 17:39:45 +0600 Subject: feat(api): add `nvim_cmd` Adds the API function `nvim_cmd` which allows executing an Ex-command through a Dictionary which can have the same values as the return value of `nvim_parse_cmd()`. This makes it much easier to do things like passing arguments with a space to commands that otherwise may not allow it, or to make commands interpret certain characters literally when they otherwise would not. --- src/nvim/api/private/helpers.c | 172 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a2ee4f91bc..725033a4c5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -19,6 +19,7 @@ #include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/getchar.h" @@ -1691,3 +1692,174 @@ int init_sign_text(char **sign_text, char *text) return OK; } + +/// Check if a string contains only whitespace characters. +bool string_iswhite(String str) +{ + for (size_t i = 0; i < str.size; i++) { + if (!ascii_iswhite(str.data[i])) { + // Found a non-whitespace character + return false; + } else if (str.data[i] == NUL) { + // Terminate at first occurence of a NUL character + break; + } + } + return true; +} + +// Add modifier string for command into the command line. Includes trailing whitespace if non-empty. +// @return OK or FAIL. +static int add_cmd_modifier_str(char *cmdline, size_t *pos, const size_t bufsize, + CmdParseInfo *cmdinfo) +{ +#define APPEND_MODIFIER(...) \ + do { \ + if (*pos < bufsize) { \ + *pos += (size_t)snprintf(cmdline + *pos, bufsize - *pos, __VA_ARGS__); \ + } \ + if (*pos < bufsize) { \ + cmdline[*pos] = ' '; \ + *pos += 1; \ + } else { \ + goto err; \ + } \ + } while (0) + +#define APPEND_MODIFIER_IF(cond, mod) \ + do { \ + if (cond) { \ + APPEND_MODIFIER(mod); \ + } \ + } while (0) + + if (cmdinfo->cmdmod.tab != 0) { + APPEND_MODIFIER("%dtab", cmdinfo->cmdmod.tab - 1); + } + if (cmdinfo->verbose != -1) { + APPEND_MODIFIER("%ldverbose", cmdinfo->verbose); + } + + switch (cmdinfo->cmdmod.split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) { + case WSP_ABOVE: + APPEND_MODIFIER("aboveleft"); + break; + case WSP_BELOW: + APPEND_MODIFIER("belowright"); + break; + case WSP_TOP: + APPEND_MODIFIER("topleft"); + break; + case WSP_BOT: + APPEND_MODIFIER("botright"); + break; + default: + break; + } + + APPEND_MODIFIER_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical"); + + if (cmdinfo->emsg_silent) { + APPEND_MODIFIER("silent!"); + } else if (cmdinfo->silent) { + APPEND_MODIFIER("silent"); + } + + APPEND_MODIFIER_IF(cmdinfo->sandbox, "sandbox"); + APPEND_MODIFIER_IF(cmdinfo->noautocmd, "noautocmd"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.browse, "browse"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.confirm, "confirm"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.hide, "hide"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepalt, "keepalt"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepjumps, "keepjumps"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepmarks, "keepmarks"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.lockmarks, "lockmarks"); + APPEND_MODIFIER_IF(cmdinfo->cmdmod.noswapfile, "noswapfile"); + + return OK; +err: + return FAIL; + +#undef APPEND_MODIFIER +#undef APPEND_MODIFIER_IF +} + +/// Build cmdline string for command, used by `nvim_cmd()`. +/// +/// @return OK or FAIL. +int build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, + size_t argc) +{ + const size_t bufsize = IOSIZE; + size_t pos = 0; + char *cmdline = xcalloc(bufsize, sizeof(char)); + +#define CMDLINE_APPEND(...) \ + do { \ + if (pos < bufsize) { \ + pos += (size_t)snprintf(cmdline + pos, bufsize - pos, __VA_ARGS__); \ + } else { \ + goto err; \ + } \ + } while (0) + + // Command modifiers. + if (add_cmd_modifier_str(cmdline, &pos, bufsize, cmdinfo) == FAIL) { + goto err; + } + + // Command range / count. + if (eap->argt & EX_RANGE) { + if (eap->addr_count == 1) { + CMDLINE_APPEND("%ld", eap->line2); + } else if (eap->addr_count > 1) { + CMDLINE_APPEND("%ld,%ld", eap->line1, eap->line2); + eap->addr_count = 2; // Make sure address count is not greater than 2 + } + } + + // Keep the index of the position where command name starts, so eap->cmd can point to it. + size_t cmdname_idx = pos; + CMDLINE_APPEND("%s", eap->cmd); + eap->cmd = cmdline + cmdname_idx; + + // Command bang. + if (eap->argt & EX_BANG && eap->forceit) { + CMDLINE_APPEND("!"); + } + + // Command register. + if (eap->argt & EX_REGSTR && eap->regname) { + CMDLINE_APPEND(" %c", eap->regname); + } + + // Iterate through each argument and store the starting position and length of each argument in + // the cmdline string in `eap->args` and `eap->arglens`, respectively. + eap->args = xcalloc(argc, sizeof(char *)); + eap->arglens = xcalloc(argc, sizeof(size_t)); + for (size_t i = 0; i < argc; i++) { + eap->args[i] = cmdline + pos + 1; // add 1 to skip the leading space. + eap->arglens[i] = STRLEN(args[i]); + CMDLINE_APPEND(" %s", args[i]); + } + eap->argc = argc; + eap->arg = argc > 0 ? eap->args[0] : NULL; + + // Replace, :make and :grep with 'makeprg' and 'grepprg'. + char *p = replace_makeprg(eap, eap->arg, cmdlinep); + if (p != eap->arg) { + // If replace_makeprg modified the cmdline string, correct the argument pointers. + assert(argc == 1); + eap->arg = p; + eap->args[0] = p; + } + + *cmdlinep = cmdline; + return OK; +err: + xfree(cmdline); + return FAIL; + +#undef CMDLINE_APPEND +} -- cgit From cf68f0a51202342163825015602bdf78b783c777 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 11 May 2022 22:51:53 +0600 Subject: fix(api): make `nvim_cmd` work correctly with empty arguments list (#18527) Closes #18526. --- src/nvim/api/private/helpers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ccf1896f08..542e5c4953 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1843,7 +1843,8 @@ int build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char CMDLINE_APPEND(" %s", args[i]); } eap->argc = argc; - eap->arg = argc > 0 ? eap->args[0] : NULL; + // If there isn't an argument, make eap->arg point to end of cmd + eap->arg = argc > 0 ? eap->args[0] : cmdline + pos; // Replace, :make and :grep with 'makeprg' and 'grepprg'. char *p = replace_makeprg(eap, eap->arg, cmdlinep); -- cgit From 566f8f80d6cb7ef2df8366e5f092b0841ee757ce Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 12 May 2022 10:57:43 +0200 Subject: refactor(api/nvim_cmd): use `kvec_t` for constructing cmdline string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Linse --- src/nvim/api/private/helpers.c | 159 ++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 99 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 542e5c4953..dcede27bcb 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1707,144 +1707,113 @@ bool string_iswhite(String str) return true; } -// Add modifier string for command into the command line. Includes trailing whitespace if non-empty. -// @return OK or FAIL. -static int add_cmd_modifier_str(char *cmdline, size_t *pos, const size_t bufsize, - CmdParseInfo *cmdinfo) +/// Build cmdline string for command, used by `nvim_cmd()`. +/// +/// @return OK or FAIL. +void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, + size_t argc) { -#define APPEND_MODIFIER(...) \ - do { \ - if (*pos < bufsize) { \ - *pos += (size_t)snprintf(cmdline + *pos, bufsize - *pos, __VA_ARGS__); \ - } \ - if (*pos < bufsize) { \ - cmdline[*pos] = ' '; \ - *pos += 1; \ - } else { \ - goto err; \ - } \ - } while (0) - -#define APPEND_MODIFIER_IF(cond, mod) \ - do { \ - if (cond) { \ - APPEND_MODIFIER(mod); \ - } \ - } while (0) + StringBuilder cmdline = KV_INITIAL_VALUE; + // Add command modifiers if (cmdinfo->cmdmod.tab != 0) { - APPEND_MODIFIER("%dtab", cmdinfo->cmdmod.tab - 1); + kv_printf(cmdline, "%dtab ", cmdinfo->cmdmod.tab - 1); } if (cmdinfo->verbose != -1) { - APPEND_MODIFIER("%ldverbose", cmdinfo->verbose); + kv_printf(cmdline, "%ldverbose ", cmdinfo->verbose); + } + + if (cmdinfo->emsg_silent) { + kv_concat(cmdline, "silent! "); + } else if (cmdinfo->silent) { + kv_concat(cmdline, "silent "); } switch (cmdinfo->cmdmod.split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) { case WSP_ABOVE: - APPEND_MODIFIER("aboveleft"); + kv_concat(cmdline, "aboveleft "); break; case WSP_BELOW: - APPEND_MODIFIER("belowright"); + kv_concat(cmdline, "belowright "); break; case WSP_TOP: - APPEND_MODIFIER("topleft"); + kv_concat(cmdline, "topleft "); break; case WSP_BOT: - APPEND_MODIFIER("botright"); + kv_concat(cmdline, "botright "); break; default: break; } - APPEND_MODIFIER_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical"); - - if (cmdinfo->emsg_silent) { - APPEND_MODIFIER("silent!"); - } else if (cmdinfo->silent) { - APPEND_MODIFIER("silent"); - } - - APPEND_MODIFIER_IF(cmdinfo->sandbox, "sandbox"); - APPEND_MODIFIER_IF(cmdinfo->noautocmd, "noautocmd"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.browse, "browse"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.confirm, "confirm"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.hide, "hide"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepalt, "keepalt"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepjumps, "keepjumps"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.keepmarks, "keepmarks"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.lockmarks, "lockmarks"); - APPEND_MODIFIER_IF(cmdinfo->cmdmod.noswapfile, "noswapfile"); - - return OK; -err: - return FAIL; - -#undef APPEND_MODIFIER -#undef APPEND_MODIFIER_IF -} - -/// Build cmdline string for command, used by `nvim_cmd()`. -/// -/// @return OK or FAIL. -int build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, - size_t argc) -{ - const size_t bufsize = IOSIZE; - size_t pos = 0; - char *cmdline = xcalloc(bufsize, sizeof(char)); - -#define CMDLINE_APPEND(...) \ +#define CMDLINE_APPEND_IF(cond, str) \ do { \ - if (pos < bufsize) { \ - pos += (size_t)snprintf(cmdline + pos, bufsize - pos, __VA_ARGS__); \ - } else { \ - goto err; \ + if (cond) { \ + kv_concat(cmdline, str); \ } \ } while (0) - // Command modifiers. - if (add_cmd_modifier_str(cmdline, &pos, bufsize, cmdinfo) == FAIL) { - goto err; - } + CMDLINE_APPEND_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical "); + CMDLINE_APPEND_IF(cmdinfo->sandbox, "sandbox "); + CMDLINE_APPEND_IF(cmdinfo->noautocmd, "noautocmd "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.browse, "browse "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.confirm, "confirm "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.hide, "hide "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepalt, "keepalt "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepjumps, "keepjumps "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepmarks, "keepmarks "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.lockmarks, "lockmarks "); + CMDLINE_APPEND_IF(cmdinfo->cmdmod.noswapfile, "noswapfile "); +#undef CMDLINE_APPEND_IF // Command range / count. if (eap->argt & EX_RANGE) { if (eap->addr_count == 1) { - CMDLINE_APPEND("%ld", eap->line2); + kv_printf(cmdline, "%ld", eap->line2); } else if (eap->addr_count > 1) { - CMDLINE_APPEND("%ld,%ld", eap->line1, eap->line2); + kv_printf(cmdline, "%ld,%ld", eap->line1, eap->line2); eap->addr_count = 2; // Make sure address count is not greater than 2 } } // Keep the index of the position where command name starts, so eap->cmd can point to it. - size_t cmdname_idx = pos; - CMDLINE_APPEND("%s", eap->cmd); - eap->cmd = cmdline + cmdname_idx; + size_t cmdname_idx = cmdline.size; + kv_printf(cmdline, "%s", eap->cmd); // Command bang. if (eap->argt & EX_BANG && eap->forceit) { - CMDLINE_APPEND("!"); + kv_printf(cmdline, "!"); } // Command register. if (eap->argt & EX_REGSTR && eap->regname) { - CMDLINE_APPEND(" %c", eap->regname); + kv_printf(cmdline, " %c", eap->regname); } - // Iterate through each argument and store the starting position and length of each argument in - // the cmdline string in `eap->args` and `eap->arglens`, respectively. - eap->args = xcalloc(argc, sizeof(char *)); + // Iterate through each argument and store the starting index and length of each argument + size_t *argidx = xcalloc(argc, sizeof(size_t)); + eap->argc = argc; eap->arglens = xcalloc(argc, sizeof(size_t)); for (size_t i = 0; i < argc; i++) { - eap->args[i] = cmdline + pos + 1; // add 1 to skip the leading space. + argidx[i] = cmdline.size + 1; // add 1 to account for the space. eap->arglens[i] = STRLEN(args[i]); - CMDLINE_APPEND(" %s", args[i]); + kv_printf(cmdline, " %s", args[i]); } - eap->argc = argc; - // If there isn't an argument, make eap->arg point to end of cmd - eap->arg = argc > 0 ? eap->args[0] : cmdline + pos; + + // Now that all the arguments are appended, use the command index and argument indices to set the + // values of eap->cmd, eap->arg and eap->args. + eap->cmd = cmdline.items + cmdname_idx; + eap->args = xcalloc(argc, sizeof(char *)); + for (size_t i = 0; i < argc; i++) { + eap->args[i] = cmdline.items + argidx[i]; + } + // If there isn't an argument, make eap->arg point to end of cmdline. + eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size; + + // Finally, make cmdlinep point to the cmdline string. + *cmdlinep = cmdline.items; + xfree(argidx); // Replace, :make and :grep with 'makeprg' and 'grepprg'. char *p = replace_makeprg(eap, eap->arg, cmdlinep); @@ -1854,12 +1823,4 @@ int build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char eap->arg = p; eap->args[0] = p; } - - *cmdlinep = cmdline; - return OK; -err: - xfree(cmdline); - return FAIL; - -#undef CMDLINE_APPEND } -- cgit From 6219331c4d333e5a76129746021f972c21a040db Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 11 May 2022 13:49:43 +0100 Subject: feat(api): add win and buf to nvim_set_option_value Co-authored-by: Gregory Anders <8965202+gpanders@users.noreply.github.com> --- src/nvim/api/private/helpers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index dcede27bcb..7be55595cf 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1040,8 +1040,8 @@ Object copy_object(Object obj) } } -static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags, - int opt_type, void *from, Error *err) +void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, + int opt_type, void *from, Error *err) { switchwin_T switchwin; aco_save_T aco; @@ -1081,7 +1081,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt } -static void set_option_value_err(char *key, int numval, char *stringval, int opt_flags, Error *err) +static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err) { char *errmsg; -- cgit From e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Fri, 13 May 2022 20:47:11 +0600 Subject: feat(ui): add `'winbar'` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for a bar at the top of each window, enabled through the `'winbar'` option. Co-authored-by: Björn Linse --- src/nvim/api/private/helpers.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index dcede27bcb..7bd68f277b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1418,14 +1418,14 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) } /// Get default statusline highlight for window -const char *get_default_stl_hl(win_T *wp) +const char *get_default_stl_hl(win_T *wp, bool use_winbar) { if (wp == NULL) { return "TabLineFill"; - } else if (wp == curwin) { - return "StatusLine"; + } else if (use_winbar) { + return (wp == curwin) ? "WinBar" : "WinBarNC"; } else { - return "StatusLineNC"; + return (wp == curwin) ? "StatusLine" : "StatusLineNC"; } } -- cgit From f15122e8a2938b0a440aa3d834f6648537f1951f Mon Sep 17 00:00:00 2001 From: James McCoy Date: Thu, 19 May 2022 22:12:48 -0400 Subject: fix(cid/351940): free compl_arg in create_user_commands()'s error path exit --- src/nvim/api/private/helpers.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7bd68f277b..5d4b84482b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1624,6 +1624,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, err: NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); + xfree(compl_arg); } int find_sid(uint64_t channel_id) -- cgit From 47d9a393de766d8e444043f5bc40dc774d45fece Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 22 May 2022 13:29:15 -0400 Subject: fix(cid/352839): USE_AFTER_FREE in create_user_command #18669 --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 5d4b84482b..adabb1471e 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1616,7 +1616,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref, addr_type_arg, luaref, force) != OK) { api_set_error(err, kErrorTypeException, "Failed to create user command"); - goto err; + // Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg } return; -- cgit From 9fec6dc9a25b5cf9c9a444ac2bd0728e8af3229e Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 25 May 2022 20:31:14 +0200 Subject: refactor(uncrustify): set maximum number of consecutive newlines to 2 (#18695) --- src/nvim/api/private/helpers.c | 7 ++----- src/nvim/api/private/helpers.h | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index c251a4a25c..3cccbc3cdf 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -407,7 +407,6 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } - buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -1040,8 +1039,8 @@ Object copy_object(Object obj) } } -void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, - int opt_type, void *from, Error *err) +void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type, + void *from, Error *err) { switchwin_T switchwin; aco_save_T aco; @@ -1080,7 +1079,6 @@ void set_option_value_for(char *key, long numval, char *stringval, int opt_flags try_end(err); } - static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err) { char *errmsg; @@ -1562,7 +1560,6 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, goto err; } - if (api_object_to_bool(opts->register_, "register", false, err)) { argt |= EX_REGSTR; } else if (ERROR_SET(err)) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index bbbc3de7d5..8423db4970 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -146,7 +146,6 @@ typedef struct { code; \ } - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" # include "keysets.h.generated.h" @@ -163,5 +162,4 @@ typedef struct { current_sctx = save_current_sctx; \ } while (0); - #endif // NVIM_API_PRIVATE_HELPERS_H -- cgit From 1f63052b682a6019d7f092553747272195fbfffd Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 30 May 2022 00:59:06 +0200 Subject: refactor(api): use hashy hash for looking up api method and event names This avoids generating khash tables at runtime, and is consistent with how evalfuncs lookup work. --- src/nvim/api/private/dispatch.c | 33 +++++++++------------------------ src/nvim/api/private/dispatch.h | 1 + 2 files changed, 10 insertions(+), 24 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index ba2e560d63..3da2c2cde4 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -32,37 +32,22 @@ #include "nvim/api/window.h" #include "nvim/ui_client.h" -static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; - -static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) -{ - map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); -} - -void msgpack_rpc_add_redraw(void) -{ - msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"), - (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw, - .fast = true }); -} +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/private/dispatch_wrappers.generated.h" +#endif /// @param name API method name /// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, Error *error) { - String m = { .data = (char *)name, .size = name_len }; - MsgpackRpcRequestHandler rv = - map_get(String, MsgpackRpcRequestHandler)(&methods, m); + int hash = msgpack_rpc_get_handler_for_hash(name, name_len); - if (!rv.fn) { + if (hash < 0) { api_set_error(error, kErrorTypeException, "Invalid method: %.*s", - m.size > 0 ? (int)m.size : (int)sizeof(""), - m.size > 0 ? m.data : ""); + name_len > 0 ? (int)name_len : (int)sizeof(""), + name_len > 0 ? name : ""); + return (MsgpackRpcRequestHandler){ 0 }; } - return rv; + return method_handlers[hash]; } - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/private/dispatch_wrappers.generated.h" -#endif diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index bad5a13934..4b7c394944 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -10,6 +10,7 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. typedef struct { + const char *name; ApiDispatchWrapper fn; bool fast; // Function is safe to be executed immediately while running the // uv loop (the loop is run very frequently due to breakcheck). -- cgit From 46536f53e82967dcac8d030ee3394cdb156f9603 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 20 Apr 2022 17:02:18 +0600 Subject: feat: add preview functionality to user commands Adds a Lua-only `preview` flag to user commands which allows the command to be incrementally previewed like `:substitute` when 'inccommand' is set. --- src/nvim/api/private/helpers.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3cccbc3cdf..6981ecc455 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1438,6 +1438,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, char *rep = NULL; LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + LuaRef preview_luaref = LUA_NOREF; if (!uc_validate_name(name.data)) { api_set_error(err, kErrorTypeValidation, "Invalid command name"); @@ -1592,6 +1593,14 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, goto err; } + if (opts->preview.type == kObjectTypeLuaRef) { + argt |= EX_PREVIEW; + preview_luaref = api_new_luaref(opts->preview.data.luaref); + } else if (HAS_KEY(opts->preview)) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'"); + goto err; + } + switch (command.type) { case kObjectTypeLuaRef: luaref = api_new_luaref(command.data.luaref); @@ -1611,7 +1620,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts, } if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref, - addr_type_arg, luaref, force) != OK) { + preview_luaref, addr_type_arg, luaref, force) != OK) { api_set_error(err, kErrorTypeException, "Failed to create user command"); // Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg } -- cgit From 96c494dec345f9a3dd2676048292267552e3f5f8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 1 Jun 2022 21:37:01 +0800 Subject: refactor: correct comments and assertions about mapping rhs (#18821) Also avoid referring to mappings as "keymaps" in commands and docs. *map_empty_rhs* *map-empty-rhs* You can create an empty {rhs} by typing nothing after a single CTRL-V (you have to type CTRL-V two times). Unfortunately, you cannot do this in a vimrc file. --- src/nvim/api/private/helpers.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6981ecc455..af4aaf01aa 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -678,11 +678,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod if (rhs.size == 0) { // assume that the user wants RHS to be a parsed_args.rhs_is_noop = true; } else { - // the given RHS was nonempty and not a , but was parsed as if it - // were empty? - assert(false && "Failed to parse nonempty RHS!"); - api_set_error(err, kErrorTypeValidation, "Parsing of nonempty RHS failed: %s", rhs.data); - goto fail_and_free; + abort(); // should never happen } } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) { if (parsed_args.rhs_len) { -- cgit From a732c253b71f89702285d5ec6fd7803045f6add9 Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Sat, 7 May 2022 12:53:37 +0200 Subject: refactor: change type of linenr_T from long to int32_t The size of long varies depending on architecture, in contrast to the MAXLNUM constant which sets the maximum allowable number of lines to 2^32-1. This discrepancy may lead to hard to detect bugs, for example https://github.com/neovim/neovim/issues/18454. Setting linenr_T to a fix maximum size of 2^32-1 will prevent this type of errors in the future. Also change the variables `amount` and `amount_after` to be linenr_T since they're referring to "the line number difference" between two texts. --- src/nvim/api/private/helpers.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index af4aaf01aa..1ddaf63743 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1397,7 +1397,8 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) return res; } } - pos_T pos = { line, (int)col, (int)col }; + assert(INT32_MIN <= line && line <= INT32_MAX); + pos_T pos = { (linenr_T)line, (int)col, (int)col }; res = setmark_pos(*name.data, &pos, buf->handle); if (!res) { if (deleting) { @@ -1773,9 +1774,9 @@ void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, cha // Command range / count. if (eap->argt & EX_RANGE) { if (eap->addr_count == 1) { - kv_printf(cmdline, "%ld", eap->line2); + kv_printf(cmdline, "%" PRIdLINENR, eap->line2); } else if (eap->addr_count > 1) { - kv_printf(cmdline, "%ld,%ld", eap->line1, eap->line2); + kv_printf(cmdline, "%" PRIdLINENR ",%" PRIdLINENR, eap->line1, eap->line2); eap->addr_count = 2; // Make sure address count is not greater than 2 } } -- cgit From f4121c52b95d4b79c0de95412c8447614a2f8960 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 10 Jun 2022 11:23:17 +0200 Subject: fix(messages): add color when showing nvim_echo in :messages history --- src/nvim/api/private/helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1ddaf63743..bdbbe9aa88 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1344,8 +1344,8 @@ HlMessage parse_hl_msg(Array chunks, Error *err) return hl_msg; free_exit: - clear_hl_msg(&hl_msg); - return hl_msg; + hl_msg_free(hl_msg); + return (HlMessage)KV_INITIAL_VALUE; } bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err) -- cgit From 4a275e3291d3eade38490801d9842640df3de202 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 12 Jun 2022 00:37:39 +0200 Subject: refactor(api): move option code to own file --- src/nvim/api/private/dispatch.c | 1 + src/nvim/api/private/helpers.c | 200 ---------------------------------------- 2 files changed, 1 insertion(+), 200 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 3da2c2cde4..d2ffd8879a 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -24,6 +24,7 @@ #include "nvim/api/autocmd.h" #include "nvim/api/buffer.h" #include "nvim/api/extmark.h" +#include "nvim/api/options.h" #include "nvim/api/tabpage.h" #include "nvim/api/ui.h" #include "nvim/api/vim.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index bdbbe9aa88..13e3f70ce1 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -32,8 +32,6 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" -#include "nvim/option.h" -#include "nvim/option_defs.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim.h" @@ -262,151 +260,6 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva return rv; } -/// Gets the value of a global or local(buffer, window) option. -/// -/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return the option value -Object get_option_from(void *from, int type, String name, Error *err) -{ - Object rv = OBJECT_INIT; - - if (name.size == 0) { - api_set_error(err, kErrorTypeValidation, "Empty option name"); - return rv; - } - - // Return values - int64_t numval; - char *stringval = NULL; - int flags = get_option_value_strict(name.data, &numval, &stringval, - type, from); - - if (!flags) { - api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'", - name.data); - return rv; - } - - if (flags & SOPT_BOOL) { - rv.type = kObjectTypeBoolean; - rv.data.boolean = numval ? true : false; - } else if (flags & SOPT_NUM) { - rv.type = kObjectTypeInteger; - rv.data.integer = numval; - } else if (flags & SOPT_STRING) { - if (stringval) { - rv.type = kObjectTypeString; - rv.data.string.data = stringval; - rv.data.string.size = strlen(stringval); - } else { - api_set_error(err, kErrorTypeException, - "Failed to get value for option '%s'", - name.data); - } - } else { - api_set_error(err, - kErrorTypeException, - "Unknown type for option '%s'", - name.data); - } - - return rv; -} - -/// Sets the value of a global or local(buffer, window) option. -/// -/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err) -{ - if (name.size == 0) { - api_set_error(err, kErrorTypeValidation, "Empty option name"); - return; - } - - int flags = get_option_value_strict(name.data, NULL, NULL, type, to); - - if (flags == 0) { - api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'", - name.data); - return; - } - - if (value.type == kObjectTypeNil) { - if (type == SREQ_GLOBAL) { - api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", - name.data); - return; - } else if (!(flags & SOPT_GLOBAL)) { - api_set_error(err, - kErrorTypeException, - "Cannot unset option '%s' " - "because it doesn't have a global value", - name.data); - return; - } else { - unset_global_local_option(name.data, to); - return; - } - } - - int numval = 0; - char *stringval = NULL; - - if (flags & SOPT_BOOL) { - if (value.type != kObjectTypeBoolean) { - api_set_error(err, - kErrorTypeValidation, - "Option '%s' requires a Boolean value", - name.data); - return; - } - - numval = value.data.boolean; - } else if (flags & SOPT_NUM) { - if (value.type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "Option '%s' requires an integer value", - name.data); - return; - } - - if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) { - api_set_error(err, kErrorTypeValidation, - "Value for option '%s' is out of range", - name.data); - return; - } - - numval = (int)value.data.integer; - } else { - if (value.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "Option '%s' requires a string value", - name.data); - return; - } - - stringval = value.data.string.data; - } - - WITH_SCRIPT_CONTEXT(channel_id, { - const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) - ? 0 : (type == SREQ_GLOBAL) - ? OPT_GLOBAL : OPT_LOCAL; - - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); - }); -} - buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -1035,59 +888,6 @@ Object copy_object(Object obj) } } -void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type, - void *from, Error *err) -{ - switchwin_T switchwin; - aco_save_T aco; - - try_start(); - switch (opt_type) { - case SREQ_WIN: - if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) - == FAIL) { - restore_win_noblock(&switchwin, true); - if (try_end(err)) { - return; - } - api_set_error(err, - kErrorTypeException, - "Problem while switching windows"); - return; - } - set_option_value_err(key, numval, stringval, opt_flags, err); - restore_win_noblock(&switchwin, true); - break; - case SREQ_BUF: - aucmd_prepbuf(&aco, (buf_T *)from); - set_option_value_err(key, numval, stringval, opt_flags, err); - aucmd_restbuf(&aco); - break; - case SREQ_GLOBAL: - set_option_value_err(key, numval, stringval, opt_flags, err); - break; - } - - if (ERROR_SET(err)) { - return; - } - - try_end(err); -} - -static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err) -{ - char *errmsg; - - if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { - if (try_end(err)) { - return; - } - - api_set_error(err, kErrorTypeException, "%s", errmsg); - } -} - void api_set_error(Error *err, ErrorType errType, const char *format, ...) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PRINTF(3, 4) { -- cgit From a907d6f51761a18b22495cba933de478101644f5 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 12 Jun 2022 16:21:30 +0200 Subject: refactor(api): move extmark specific functions to api/extmark.c --- src/nvim/api/private/helpers.c | 152 ----------------------------------------- 1 file changed, 152 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 13e3f70ce1..a1f5d70a66 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -16,7 +16,6 @@ #include "nvim/assert.h" #include "nvim/buffer.h" #include "nvim/charset.h" -#include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" @@ -960,121 +959,6 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) return mappings; } -/// Gets the line and column of an extmark. -/// -/// Extmarks may be queried by position, name or even special names -/// in the future such as "cursor". -/// -/// @param[out] lnum extmark line -/// @param[out] colnr extmark column -/// -/// @return true if the extmark was found, else false -bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int - *row, colnr_T *col, Error *err) -{ - // Check if it is mark id - if (obj.type == kObjectTypeInteger) { - Integer id = obj.data.integer; - if (id == 0) { - *row = 0; - *col = 0; - return true; - } else if (id == -1) { - *row = MAXLNUM; - *col = MAXCOL; - return true; - } else if (id < 0) { - api_set_error(err, kErrorTypeValidation, "Mark id must be positive"); - return false; - } - - ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); - if (extmark.row >= 0) { - *row = extmark.row; - *col = extmark.col; - return true; - } else { - api_set_error(err, kErrorTypeValidation, "No mark with requested id"); - return false; - } - - // Check if it is a position - } else if (obj.type == kObjectTypeArray) { - Array pos = obj.data.array; - if (pos.size != 2 - || pos.items[0].type != kObjectTypeInteger - || pos.items[1].type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "Position must have 2 integer elements"); - return false; - } - Integer pos_row = pos.items[0].data.integer; - Integer pos_col = pos.items[1].data.integer; - *row = (int)(pos_row >= 0 ? pos_row : MAXLNUM); - *col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL); - return true; - } else { - api_set_error(err, kErrorTypeValidation, - "Position must be a mark id Integer or position Array"); - return false; - } -} - -VirtText parse_virt_text(Array chunks, Error *err, int *width) -{ - VirtText virt_text = KV_INITIAL_VALUE; - int w = 0; - for (size_t i = 0; i < chunks.size; i++) { - if (chunks.items[i].type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); - goto free_exit; - } - Array chunk = chunks.items[i].data.array; - if (chunk.size == 0 || chunk.size > 2 - || chunk.items[0].type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "Chunk is not an array with one or two strings"); - goto free_exit; - } - - String str = chunk.items[0].data.string; - - int hl_id = 0; - if (chunk.size == 2) { - Object hl = chunk.items[1]; - if (hl.type == kObjectTypeArray) { - Array arr = hl.data.array; - for (size_t j = 0; j < arr.size; j++) { - hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err); - if (ERROR_SET(err)) { - goto free_exit; - } - if (j < arr.size - 1) { - kv_push(virt_text, ((VirtTextChunk){ .text = NULL, - .hl_id = hl_id })); - } - } - } else { - hl_id = object_to_hl_id(hl, "virt_text highlight", err); - if (ERROR_SET(err)) { - goto free_exit; - } - } - } - - char *text = transstr(str.size > 0 ? str.data : "", false); // allocates - w += (int)mb_string2cells(text); - - kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); - } - - *width = w; - return virt_text; - -free_exit: - clear_virttext(&virt_text); - return virt_text; -} /// Force obj to bool. /// If it fails, returns false and sets err @@ -1460,42 +1344,6 @@ sctx_T api_set_sctx(uint64_t channel_id) return old_current_sctx; } -// adapted from sign.c:sign_define_init_text. -// TODO(lewis6991): Consider merging -int init_sign_text(char **sign_text, char *text) -{ - char *s; - - char *endp = text + (int)STRLEN(text); - - // Count cells and check for non-printable chars - int cells = 0; - for (s = text; s < endp; s += utfc_ptr2len(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { - break; - } - cells += utf_ptr2cells(s); - } - // Currently must be empty, one or two display cells - if (s != endp || cells > 2) { - return FAIL; - } - if (cells < 1) { - return OK; - } - - // Allocate one byte more if we need to pad up - // with a space. - size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); - *sign_text = xstrnsave(text, len); - - if (cells == 1) { - STRCPY(*sign_text + len - 1, " "); - } - - return OK; -} - /// Check if a string contains only whitespace characters. bool string_iswhite(String str) { -- cgit From 0d63fafcda3847a6ec8a9da42db7bf10ac917d14 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 12 Jun 2022 16:38:31 +0200 Subject: refactor(api): move command related API to separate file --- src/nvim/api/private/dispatch.c | 1 + src/nvim/api/private/helpers.c | 340 ---------------------------------------- 2 files changed, 1 insertion(+), 340 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index d2ffd8879a..d6a6fc1219 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -23,6 +23,7 @@ // =========================================================================== #include "nvim/api/autocmd.h" #include "nvim/api/buffer.h" +#include "nvim/api/command.h" #include "nvim/api/extmark.h" #include "nvim/api/options.h" #include "nvim/api/tabpage.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a1f5d70a66..ff6a4c37e8 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -959,7 +959,6 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) return mappings; } - /// Force obj to bool. /// If it fails, returns false and sets err /// @param obj The object to coerce to a boolean @@ -1108,212 +1107,6 @@ const char *get_default_stl_hl(win_T *wp, bool use_winbar) } } -void create_user_command(String name, Object command, Dict(user_command) *opts, int flags, - Error *err) -{ - uint32_t argt = 0; - long def = -1; - cmd_addr_T addr_type_arg = ADDR_NONE; - int compl = EXPAND_NOTHING; - char *compl_arg = NULL; - char *rep = NULL; - LuaRef luaref = LUA_NOREF; - LuaRef compl_luaref = LUA_NOREF; - LuaRef preview_luaref = LUA_NOREF; - - if (!uc_validate_name(name.data)) { - api_set_error(err, kErrorTypeValidation, "Invalid command name"); - goto err; - } - - if (mb_islower(name.data[0])) { - api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); - goto err; - } - - if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) { - api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive"); - goto err; - } - - if (opts->nargs.type == kObjectTypeInteger) { - switch (opts->nargs.data.integer) { - case 0: - // Default value, nothing to do - break; - case 1: - argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG; - break; - default: - api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'"); - goto err; - } - } else if (opts->nargs.type == kObjectTypeString) { - if (opts->nargs.data.string.size > 1) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'"); - goto err; - } - - switch (opts->nargs.data.string.data[0]) { - case '*': - argt |= EX_EXTRA; - break; - case '?': - argt |= EX_EXTRA | EX_NOSPC; - break; - case '+': - argt |= EX_EXTRA | EX_NEEDARG; - break; - default: - api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'"); - goto err; - } - } else if (HAS_KEY(opts->nargs)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'"); - goto err; - } - - if (HAS_KEY(opts->complete) && !argt) { - api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'"); - goto err; - } - - if (opts->range.type == kObjectTypeBoolean) { - if (opts->range.data.boolean) { - argt |= EX_RANGE; - addr_type_arg = ADDR_LINES; - } - } else if (opts->range.type == kObjectTypeString) { - if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) { - argt |= EX_RANGE | EX_DFLALL; - addr_type_arg = ADDR_LINES; - } else { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'"); - goto err; - } - } else if (opts->range.type == kObjectTypeInteger) { - argt |= EX_RANGE | EX_ZEROR; - def = opts->range.data.integer; - addr_type_arg = ADDR_LINES; - } else if (HAS_KEY(opts->range)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'"); - goto err; - } - - if (opts->count.type == kObjectTypeBoolean) { - if (opts->count.data.boolean) { - argt |= EX_COUNT | EX_ZEROR | EX_RANGE; - addr_type_arg = ADDR_OTHER; - def = 0; - } - } else if (opts->count.type == kObjectTypeInteger) { - argt |= EX_COUNT | EX_ZEROR | EX_RANGE; - addr_type_arg = ADDR_OTHER; - def = opts->count.data.integer; - } else if (HAS_KEY(opts->count)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'"); - goto err; - } - - if (opts->addr.type == kObjectTypeString) { - if (parse_addr_type_arg(opts->addr.data.string.data, (int)opts->addr.data.string.size, - &addr_type_arg) != OK) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'"); - goto err; - } - - if (addr_type_arg != ADDR_LINES) { - argt |= EX_ZEROR; - } - } else if (HAS_KEY(opts->addr)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'"); - goto err; - } - - if (api_object_to_bool(opts->bang, "bang", false, err)) { - argt |= EX_BANG; - } else if (ERROR_SET(err)) { - goto err; - } - - if (api_object_to_bool(opts->bar, "bar", false, err)) { - argt |= EX_TRLBAR; - } else if (ERROR_SET(err)) { - goto err; - } - - if (api_object_to_bool(opts->register_, "register", false, err)) { - argt |= EX_REGSTR; - } else if (ERROR_SET(err)) { - goto err; - } - - if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) { - argt |= EX_KEEPSCRIPT; - } else if (ERROR_SET(err)) { - goto err; - } - - bool force = api_object_to_bool(opts->force, "force", true, err); - if (ERROR_SET(err)) { - goto err; - } - - if (opts->complete.type == kObjectTypeLuaRef) { - compl = EXPAND_USER_LUA; - compl_luaref = api_new_luaref(opts->complete.data.luaref); - } else if (opts->complete.type == kObjectTypeString) { - if (parse_compl_arg(opts->complete.data.string.data, - (int)opts->complete.data.string.size, &compl, &argt, - &compl_arg) != OK) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'"); - goto err; - } - } else if (HAS_KEY(opts->complete)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'"); - goto err; - } - - if (opts->preview.type == kObjectTypeLuaRef) { - argt |= EX_PREVIEW; - preview_luaref = api_new_luaref(opts->preview.data.luaref); - } else if (HAS_KEY(opts->preview)) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'"); - goto err; - } - - switch (command.type) { - case kObjectTypeLuaRef: - luaref = api_new_luaref(command.data.luaref); - if (opts->desc.type == kObjectTypeString) { - rep = opts->desc.data.string.data; - } else { - snprintf((char *)IObuff, IOSIZE, "", luaref); - rep = (char *)IObuff; - } - break; - case kObjectTypeString: - rep = command.data.string.data; - break; - default: - api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function"); - goto err; - } - - if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref, - preview_luaref, addr_type_arg, luaref, force) != OK) { - api_set_error(err, kErrorTypeException, "Failed to create user command"); - // Do not goto err, since uc_add_command now owns luaref, compl_luaref, and compl_arg - } - - return; - -err: - NLUA_CLEAR_REF(luaref); - NLUA_CLEAR_REF(compl_luaref); - xfree(compl_arg); -} - int find_sid(uint64_t channel_id) { switch (channel_id) { @@ -1343,136 +1136,3 @@ sctx_T api_set_sctx(uint64_t channel_id) } return old_current_sctx; } - -/// Check if a string contains only whitespace characters. -bool string_iswhite(String str) -{ - for (size_t i = 0; i < str.size; i++) { - if (!ascii_iswhite(str.data[i])) { - // Found a non-whitespace character - return false; - } else if (str.data[i] == NUL) { - // Terminate at first occurence of a NUL character - break; - } - } - return true; -} - -/// Build cmdline string for command, used by `nvim_cmd()`. -/// -/// @return OK or FAIL. -void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdinfo, char **args, - size_t argc) -{ - StringBuilder cmdline = KV_INITIAL_VALUE; - - // Add command modifiers - if (cmdinfo->cmdmod.tab != 0) { - kv_printf(cmdline, "%dtab ", cmdinfo->cmdmod.tab - 1); - } - if (cmdinfo->verbose != -1) { - kv_printf(cmdline, "%ldverbose ", cmdinfo->verbose); - } - - if (cmdinfo->emsg_silent) { - kv_concat(cmdline, "silent! "); - } else if (cmdinfo->silent) { - kv_concat(cmdline, "silent "); - } - - switch (cmdinfo->cmdmod.split & (WSP_ABOVE | WSP_BELOW | WSP_TOP | WSP_BOT)) { - case WSP_ABOVE: - kv_concat(cmdline, "aboveleft "); - break; - case WSP_BELOW: - kv_concat(cmdline, "belowright "); - break; - case WSP_TOP: - kv_concat(cmdline, "topleft "); - break; - case WSP_BOT: - kv_concat(cmdline, "botright "); - break; - default: - break; - } - -#define CMDLINE_APPEND_IF(cond, str) \ - do { \ - if (cond) { \ - kv_concat(cmdline, str); \ - } \ - } while (0) - - CMDLINE_APPEND_IF(cmdinfo->cmdmod.split & WSP_VERT, "vertical "); - CMDLINE_APPEND_IF(cmdinfo->sandbox, "sandbox "); - CMDLINE_APPEND_IF(cmdinfo->noautocmd, "noautocmd "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.browse, "browse "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.confirm, "confirm "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.hide, "hide "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepalt, "keepalt "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepjumps, "keepjumps "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.keepmarks, "keepmarks "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.keeppatterns, "keeppatterns "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.lockmarks, "lockmarks "); - CMDLINE_APPEND_IF(cmdinfo->cmdmod.noswapfile, "noswapfile "); -#undef CMDLINE_APPEND_IF - - // Command range / count. - if (eap->argt & EX_RANGE) { - if (eap->addr_count == 1) { - kv_printf(cmdline, "%" PRIdLINENR, eap->line2); - } else if (eap->addr_count > 1) { - kv_printf(cmdline, "%" PRIdLINENR ",%" PRIdLINENR, eap->line1, eap->line2); - eap->addr_count = 2; // Make sure address count is not greater than 2 - } - } - - // Keep the index of the position where command name starts, so eap->cmd can point to it. - size_t cmdname_idx = cmdline.size; - kv_printf(cmdline, "%s", eap->cmd); - - // Command bang. - if (eap->argt & EX_BANG && eap->forceit) { - kv_printf(cmdline, "!"); - } - - // Command register. - if (eap->argt & EX_REGSTR && eap->regname) { - kv_printf(cmdline, " %c", eap->regname); - } - - // Iterate through each argument and store the starting index and length of each argument - size_t *argidx = xcalloc(argc, sizeof(size_t)); - eap->argc = argc; - eap->arglens = xcalloc(argc, sizeof(size_t)); - for (size_t i = 0; i < argc; i++) { - argidx[i] = cmdline.size + 1; // add 1 to account for the space. - eap->arglens[i] = STRLEN(args[i]); - kv_printf(cmdline, " %s", args[i]); - } - - // Now that all the arguments are appended, use the command index and argument indices to set the - // values of eap->cmd, eap->arg and eap->args. - eap->cmd = cmdline.items + cmdname_idx; - eap->args = xcalloc(argc, sizeof(char *)); - for (size_t i = 0; i < argc; i++) { - eap->args[i] = cmdline.items + argidx[i]; - } - // If there isn't an argument, make eap->arg point to end of cmdline. - eap->arg = argc > 0 ? eap->args[0] : cmdline.items + cmdline.size; - - // Finally, make cmdlinep point to the cmdline string. - *cmdlinep = cmdline.items; - xfree(argidx); - - // Replace, :make and :grep with 'makeprg' and 'grepprg'. - char *p = replace_makeprg(eap, eap->arg, cmdlinep); - if (p != eap->arg) { - // If replace_makeprg modified the cmdline string, correct the argument pointers. - assert(argc == 1); - eap->arg = p; - eap->args[0] = p; - } -} -- cgit From 5d6987210578f5f1c3151988b99a9411f9603374 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 8 Jun 2022 22:02:02 +0200 Subject: perf(ui): reduce allocation overhead when encoding "redraw" events Note for external UIs: Nvim can now emit multiple "redraw" event batches before a final "flush" event is received. To retain existing behavior, clients should make sure to update visible state at an explicit "flush" event, not just the end of a "redraw" batch of event. * Get rid of copy_object() blizzard in the auto-generated ui_event layer * Special case "grid_line" by encoding screen state directly to msgpack events with no intermediate API events. * Get rid of the arcane notion of referring to the screen as the "shell" * Array and Dictionary are kvec_t:s, so define them as such. * Allow kvec_t:s, such as Arrays and Dictionaries, to be allocated with a predetermined size within an arena. * Eliminate redundant capacity checking when filling such kvec_t:s with values. --- src/nvim/api/private/defs.h | 15 ++++----------- src/nvim/api/private/helpers.c | 26 ++++++++++++++++++++++++++ src/nvim/api/private/helpers.h | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 396fab721d..b1e0dd364c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -6,9 +6,10 @@ #include #include "nvim/func_attr.h" +#include "nvim/lib/kvec.h" #include "nvim/types.h" -#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL } +#define ARRAY_DICT_INIT KV_INITIAL_VALUE #define STRING_INIT { .data = NULL, .size = 0 } #define OBJECT_INIT { .type = kObjectTypeNil } #define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL } @@ -84,18 +85,10 @@ REMOTE_TYPE(Window); REMOTE_TYPE(Tabpage); typedef struct object Object; - -typedef struct { - Object *items; - size_t size, capacity; -} Array; +typedef kvec_t(Object) Array; typedef struct key_value_pair KeyValuePair; - -typedef struct { - KeyValuePair *items; - size_t size, capacity; -} Dictionary; +typedef kvec_t(KeyValuePair) Dictionary; typedef enum { kObjectTypeNil = 0, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff6a4c37e8..693d946088 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -669,6 +669,32 @@ void api_free_string(String value) xfree(value.data); } +Array arena_array(Arena *arena, size_t max_size) +{ + Array arr = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, arr, max_size); + return arr; +} + +Dictionary arena_dict(Arena *arena, size_t max_size) +{ + Dictionary dict = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, dict, max_size); + return dict; +} + +String arena_string(Arena *arena, String str) +{ + if (str.size) { + char *mem = arena_alloc(arena, str.size + 1, false); + memcpy(mem, str.data, str.size); + mem[str.size] = NUL; + return cbuf_as_string(mem, str.size); + } else { + return (String)STRING_INIT; + } +} + void api_free_object(Object value) { switch (value.type) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8423db4970..a4348d8b44 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -63,17 +63,31 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_C(dict, k, v) \ + kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) + #define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); #define ADD(array, item) \ kv_push(array, item) +#define ADD_C(array, item) \ + kv_push_c(array, item) + #define FIXED_TEMP_ARRAY(name, fixsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[fixsize]; \ name.size = fixsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_ARRAY(name, maxsize) \ + Array name = ARRAY_DICT_INIT; \ + Object name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + +#define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) + #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) /// Create a new String instance, putting data in allocated memory -- cgit From a9442c532e9af45fc5c79d0207ab837cb715941f Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 21 Jun 2022 13:03:35 +0200 Subject: perf(highlight): allocate permanent names in an arena for fun and cache locality --- src/nvim/api/private/helpers.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 693d946088..9cadef0385 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -686,10 +686,7 @@ Dictionary arena_dict(Arena *arena, size_t max_size) String arena_string(Arena *arena, String str) { if (str.size) { - char *mem = arena_alloc(arena, str.size + 1, false); - memcpy(mem, str.data, str.size); - mem[str.size] = NUL; - return cbuf_as_string(mem, str.size); + return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size); } else { return (String)STRING_INIT; } -- cgit From 7718b758461265d8966468c104ce5454538471e2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Jun 2022 21:17:11 +0800 Subject: refactor: move some mapping-related code to a separate file (#19061) This marks the following Vim patches as ported: vim-patch:8.1.1785: map functionality mixed with character input Problem: Map functionality mixed with character input. Solution: Move the map functionality to a separate file. (Yegappan Lakshmanan, closes vim/vim#4740) Graduate the +localmap feature. https://github.com/vim/vim/commit/b66bab381c8ba71fd6e92327d1d34c6f8a65f2a7 vim-patch:8.2.3643: header for source file is outdated Problem: Header for source file is outdated. Solution: Make the header more accurate. (closes vim/vim#9186) https://github.com/vim/vim/commit/a3f83feb63eae5464a620ae793c002eb45f7a838 Also cherry-pick a change for mappings from patch 8.2.0807. Rename map_clear_mode() to do_mapclear(). --- src/nvim/api/private/helpers.c | 189 ----------------------------------------- 1 file changed, 189 deletions(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9cadef0385..436bcd5212 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -21,7 +21,6 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" #include "nvim/fileio.h" -#include "nvim/getchar.h" #include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/lua/executor.h" @@ -441,142 +440,6 @@ Array string_to_array(const String input, bool crlf) return ret; } -/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for -/// functions like @ref nvim_buf_set_keymap. -/// -/// Arguments are handled like @ref nvim_set_keymap unless noted. -/// @param buffer Buffer handle for a specific buffer, or 0 for the current -/// buffer, or -1 to signify global behavior ("all buffers") -/// @param is_unmap When true, removes the mapping that matches {lhs}. -void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs, - String rhs, Dict(keymap) *opts, Error *err) -{ - LuaRef lua_funcref = LUA_NOREF; - bool global = (buffer == -1); - if (global) { - buffer = 0; - } - buf_T *target_buf = find_buffer_by_handle(buffer, err); - - if (!target_buf) { - return; - } - - const sctx_T save_current_sctx = api_set_sctx(channel_id); - - if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) { - lua_funcref = opts->callback.data.luaref; - opts->callback.data.luaref = LUA_NOREF; - } - MapArguments parsed_args = MAP_ARGUMENTS_INIT; - if (opts) { -#define KEY_TO_BOOL(name) \ - parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \ - if (ERROR_SET(err)) { \ - goto fail_and_free; \ - } - - KEY_TO_BOOL(nowait); - KEY_TO_BOOL(noremap); - KEY_TO_BOOL(silent); - KEY_TO_BOOL(script); - KEY_TO_BOOL(expr); - KEY_TO_BOOL(unique); -#undef KEY_TO_BOOL - } - parsed_args.buffer = !global; - - set_maparg_lhs_rhs(lhs.data, lhs.size, - rhs.data, rhs.size, lua_funcref, - CPO_TO_CPO_FLAGS, &parsed_args); - if (opts != NULL && opts->desc.type == kObjectTypeString) { - parsed_args.desc = string_to_cstr(opts->desc.data.string); - } else { - parsed_args.desc = NULL; - } - if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) { - api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data); - goto fail_and_free; - } - - if (mode.size > 1) { - api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data); - goto fail_and_free; - } - int mode_val; // integer value of the mapping mode, to be passed to do_map() - char *p = (mode.size) ? mode.data : "m"; - if (STRNCMP(p, "!", 2) == 0) { - mode_val = get_map_mode(&p, true); // mapmode-ic - } else { - mode_val = get_map_mode(&p, false); - if (mode_val == (MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING) && mode.size > 0) { - // get_map_mode() treats unrecognized mode shortnames as ":map". - // This is an error unless the given shortname was empty string "". - api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p); - goto fail_and_free; - } - } - - if (parsed_args.lhs_len == 0) { - api_set_error(err, kErrorTypeValidation, "Invalid (empty) LHS"); - goto fail_and_free; - } - - bool is_noremap = parsed_args.noremap; - assert(!(is_unmap && is_noremap)); - - if (!is_unmap && lua_funcref == LUA_NOREF - && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) { - if (rhs.size == 0) { // assume that the user wants RHS to be a - parsed_args.rhs_is_noop = true; - } else { - abort(); // should never happen - } - } else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) { - if (parsed_args.rhs_len) { - api_set_error(err, kErrorTypeValidation, - "Gave nonempty RHS in unmap command: %s", parsed_args.rhs); - } else { - api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap"); - } - goto fail_and_free; - } - - // buf_do_map() reads noremap/unmap as its own argument. - int maptype_val = 0; - if (is_unmap) { - maptype_val = 1; - } else if (is_noremap) { - maptype_val = 2; - } - - switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) { - case 0: - break; - case 1: - api_set_error(err, kErrorTypeException, (char *)e_invarg, 0); - goto fail_and_free; - case 2: - api_set_error(err, kErrorTypeException, (char *)e_nomap, 0); - goto fail_and_free; - case 5: - api_set_error(err, kErrorTypeException, - "E227: mapping already exists for %s", parsed_args.lhs); - goto fail_and_free; - default: - assert(false && "Unrecognized return code!"); - goto fail_and_free; - } // switch - - parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success -fail_and_free: - current_sctx = save_current_sctx; - NLUA_CLEAR_REF(parsed_args.rhs_lua); - xfree(parsed_args.rhs); - xfree(parsed_args.orig_rhs); - XFREE_CLEAR(parsed_args.desc); -} - /// Collects `n` buffer lines into array `l`, optionally replacing newlines /// with NUL. /// @@ -930,58 +793,6 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...) err->type = errType; } -/// Get an array containing dictionaries describing mappings -/// based on mode and buffer id -/// -/// @param mode The abbreviation for the mode -/// @param buf The buffer to get the mapping array. NULL for global -/// @param from_lua Whether it is called from internal lua api. -/// @returns Array of maparg()-like dictionaries describing mappings -ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua) -{ - Array mappings = ARRAY_DICT_INIT; - dict_T *const dict = tv_dict_alloc(); - - // Convert the string mode to the integer mode - // that is stored within each mapblock - char *p = mode.data; - int int_mode = get_map_mode(&p, 0); - - // Determine the desired buffer value - long buffer_value = (buf == NULL) ? 0 : buf->handle; - - for (int i = 0; i < MAX_MAPHASH; i++) { - for (const mapblock_T *current_maphash = get_maphash(i, buf); - current_maphash; - current_maphash = current_maphash->m_next) { - if (current_maphash->m_simplified) { - continue; - } - // Check for correct mode - if (int_mode & current_maphash->m_mode) { - mapblock_fill_dict(dict, current_maphash, buffer_value, false); - Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT, - .vval.v_dict = dict } }); - if (from_lua) { - Dictionary d = api_dict.data.dictionary; - for (size_t j = 0; j < d.size; j++) { - if (strequal("callback", d.items[j].key.data)) { - d.items[j].value.type = kObjectTypeLuaRef; - d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer); - break; - } - } - } - ADD(mappings, api_dict); - tv_dict_clear(dict); - } - } - } - tv_dict_free(dict); - - return mappings; -} - /// Force obj to bool. /// If it fails, returns false and sets err /// @param obj The object to coerce to a boolean -- cgit From 565f72b9689e0c440ff72c712a090224aaf7631b Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Thu, 30 Jun 2022 07:59:52 -0500 Subject: feat(marks): restore viewport on jump #15831 ** Refactor Previously most functions used to "get" a mark returned a position, changed the line number and sometimes changed even the current buffer. Now functions return a {x}fmark_T making calling context aware whether the mark is in another buffer without arcane casting. A new function is provided for switching to the mark buffer and returning a flag style Enum to convey what happen in the movement. If the cursor changed, line, columns, if it changed buffer, etc. The function to get named mark was split into multiple functions. - mark_get() -> fmark_T - mark_get_global() -> xfmark_T - mark_get_local() -> fmark_T - mark_get_motion() -> fmark_T - mark_get_visual() -> fmark_T Functions that manage the changelist and jumplist were also modified to return mark types. - get_jumplist -> fmark_T - get_changelist -> fmark_T The refactor is also seen mainly on normal.c, where all the mark movement has been siphoned through one function nv_gomark, while the other functions handle getting the mark and setting their movement flags. To handle whether context marks should be left, etc. ** Mark View While doing the refactor the concept of a mark view was also implemented: The view of a mark currently implemented as the number of lines between the mark position on creation and the window topline. This allows for moving not only back to the position of a mark but having the window look similar to when the mark was defined. This is done by carrying and extra element in the fmark_T struct, which can be extended later to also restore horizontal shift. *** User space features 1. There's a new option, jumpoptions+=view enables the mark view restoring automatically when using the jumplist, changelist, alternate-file and mark motions. g; g, '[mark] `[mark] ** Limitations - The view information is not saved in shada. - Calls to get_mark should copy the value in the pointer since we are using pos_to_mark() to wrap and provide a homogeneous interfaces. This was also a limitation in the previous state of things. --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/api/private') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 436bcd5212..fad75d55be 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -916,7 +916,7 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) } assert(INT32_MIN <= line && line <= INT32_MAX); pos_T pos = { (linenr_T)line, (int)col, (int)col }; - res = setmark_pos(*name.data, &pos, buf->handle); + res = setmark_pos(*name.data, &pos, buf->handle, NULL); if (!res) { if (deleting) { api_set_error(err, kErrorTypeException, -- cgit