diff options
Diffstat (limited to 'src/nvim/eval')
-rw-r--r-- | src/nvim/eval/buffer.c | 8 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 18 | ||||
-rw-r--r-- | src/nvim/eval/encode.c | 17 | ||||
-rw-r--r-- | src/nvim/eval/encode.h | 18 | ||||
-rw-r--r-- | src/nvim/eval/executor.c | 5 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 714 | ||||
-rw-r--r-- | src/nvim/eval/funcs.h | 1 | ||||
-rw-r--r-- | src/nvim/eval/gc.h | 2 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 62 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 23 | ||||
-rw-r--r-- | src/nvim/eval/typval_defs.h | 20 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 3 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 45 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 4 | ||||
-rw-r--r-- | src/nvim/eval/vars.c | 131 | ||||
-rw-r--r-- | src/nvim/eval/window.c | 94 | ||||
-rw-r--r-- | src/nvim/eval/window.h | 70 |
17 files changed, 840 insertions, 395 deletions
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c index c60a104381..7b8f71ef3f 100644 --- a/src/nvim/eval/buffer.c +++ b/src/nvim/eval/buffer.c @@ -5,6 +5,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/change.h" @@ -14,7 +15,6 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" #include "nvim/memline.h" @@ -494,6 +494,7 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); tv_dict_add_nr(dict, S_LEN("changedtick"), buf_get_changedtick(buf)); tv_dict_add_nr(dict, S_LEN("hidden"), buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); + tv_dict_add_nr(dict, S_LEN("command"), buf == cmdwin_buf); // Get a reference to buffer variables tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); @@ -507,7 +508,7 @@ static dict_T *get_buffer_info(buf_T *buf) } tv_dict_add_list(dict, S_LEN("windows"), windows); - if (buf->b_signs) { + if (buf_has_signs(buf)) { // List of signs placed in this buffer tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); } @@ -584,7 +585,8 @@ void f_getbufinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @return range (from start to end) of lines in rettv from the specified /// buffer. -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) +static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, bool retlist, + typval_T *rettv) { rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); rettv->vval.v_string = NULL; diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 03f79fca84..d7df7bb150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -14,9 +14,9 @@ #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" +#include "nvim/eval_defs.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -142,9 +142,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack ValuesStackItem key = kv_pop(*stack); if (last_container.special_val == NULL) { // These cases should have already been handled. - assert(!(key.is_special_string - || key.val.vval.v_string == NULL - || *key.val.vval.v_string == NUL)); + assert(!(key.is_special_string || key.val.vval.v_string == NULL)); dictitem_T *const obj_di = tv_dict_item_alloc(key.val.vval.v_string); tv_clear(&key.val); if (tv_dict_add(last_container.container.vval.v_dict, obj_di) @@ -171,11 +169,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack tv_clear(&obj.val); return FAIL; } - // Handle empty key and key represented as special dictionary + // Handle special dictionaries if (last_container.special_val == NULL && (obj.is_special_string || obj.val.vval.v_string == NULL - || *obj.val.vval.v_string == NUL || tv_dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { tv_clear(&obj.val); @@ -405,13 +402,6 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, semsg(_("E474: Expected string end: %.*s"), (int)buf_len, buf); goto parse_json_string_fail; } - if (len == 0) { - POP(((typval_T) { - .v_type = VAR_STRING, - .vval = { .v_string = NULL }, - }), false); - goto parse_json_string_ret; - } char *str = xmalloc(len + 1); int fst_in_pair = 0; char *str_end = str; diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 8505c30fad..d35ac4eb7b 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -20,7 +20,8 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" +#include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/macros_defs.h" #include "nvim/math.h" @@ -1055,3 +1056,17 @@ char *encode_tv2json(typval_T *tv, size_t *len) #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS + +/// Initialize ListReaderState structure +ListReaderState encode_init_lrstate(const list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + return (ListReaderState) { + .list = list, + .li = tv_list_first(list), + .offset = 0, + .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL + ? 0 + : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), + }; +} diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 26a3286f2b..6d1c0b61c5 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -1,12 +1,8 @@ #pragma once -#include <msgpack.h> #include <msgpack/pack.h> -#include <stddef.h> #include <string.h> -#include "nvim/eval.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/garray_defs.h" @@ -36,20 +32,6 @@ typedef struct { size_t li_length; ///< Length of the string inside the read item. } ListReaderState; -/// Initialize ListReaderState structure -static inline ListReaderState encode_init_lrstate(const list_T *const list) - FUNC_ATTR_NONNULL_ALL -{ - return (ListReaderState) { - .list = list, - .li = tv_list_first(list), - .offset = 0, - .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL - ? 0 - : strlen(TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string)), - }; -} - /// Array mapping values from SpecialVarValue enum to names extern const char *const encode_bool_var_names[]; extern const char *const encode_special_var_names[]; diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index dc23fcdc72..1b8c057d7c 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -5,9 +5,8 @@ #include "nvim/eval/executor.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/message.h" #include "nvim/strings.h" @@ -15,7 +14,7 @@ #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/executor.c.generated.h" // IWYU pragma: export +# include "eval/executor.c.generated.h" #endif char *e_list_index_out_of_range_nr diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 13425b21d1..d7237d6443 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> #include <time.h> #include <uv.h> @@ -27,9 +26,11 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" @@ -48,22 +49,26 @@ #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" @@ -75,24 +80,34 @@ #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/math.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/menu.h" +#include "nvim/menu_defs.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/dl.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" @@ -103,15 +118,19 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/spell.h" #include "nvim/spellsuggest.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -305,7 +324,7 @@ int call_internal_method(const char *const fname, const int argcount, typval_T * } /// @return true for a non-zero Number and a non-empty String. -static int non_zero_arg(typval_T *argvars) +static bool non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) @@ -341,33 +360,28 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) MsgpackRpcRequestHandler handler = *fptr.api_handler; - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, false)); } Error err = ERROR_INIT; - Arena res_arena = ARENA_EMPTY; - Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err); + Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err); if (ERROR_SET(&err)) { semsg_multiline(e_api_error, err.msg); goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim_take_luaref(&result, rettv, true, &err); end: - api_free_array(args); - if (handler.arena_return) { - arena_mem_free(arena_finish(&res_arena)); - } else { + if (handler.ret_alloc) { api_free_object(result); } + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -428,8 +442,7 @@ static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "api_info()" function static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - Dictionary metadata = api_metadata(); - (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL); + object_to_vim(api_metadata(), rettv, NULL); } /// "atan2()" function @@ -1022,10 +1035,11 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Dictionary ctx_dict = ctx_to_dict(ctx); + Arena arena = ARENA_EMPTY; + Dictionary ctx_dict = ctx_to_dict(ctx, &arena); Error err = ERROR_INIT; - (void)object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); - api_free_dictionary(ctx_dict); + object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1093,7 +1107,8 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const int save_did_emsg = did_emsg; did_emsg = false; - Dictionary dict = vim_to_object(&argvars[0]).data.dictionary; + Arena arena = ARENA_EMPTY; + Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary; Context tmp = CONTEXT_INIT; Error err = ERROR_INIT; ctx_from_dict(dict, &tmp, &err); @@ -1106,7 +1121,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) *ctx = tmp; } - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); did_emsg = save_did_emsg; } @@ -1675,7 +1690,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *path = NULL; - (void)os_can_exe(tv_get_string(&argvars[0]), &path, true); + os_can_exe(tv_get_string(&argvars[0]), &path, true); #ifdef BACKSLASH_IN_FILENAME if (path != NULL) { @@ -1711,7 +1726,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = false; // Trailing garbage. } } else if (*p == '*') { // Internal or user defined function. - n = function_exists(p + 1, false); + n = strnequal(p, "*v:lua.", 7) ? nlua_func_exists(p + 7) : function_exists(p + 1, false); } else if (*p == ':') { n = cmd_exists(p + 1); } else if (*p == '#') { @@ -1922,44 +1937,47 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, list_T *l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - l1 = tv_list_copy(NULL, l1, false, get_copyID()); - if (l1 == NULL) { - return; - } - } - listitem_T *item; - if (argvars[2].v_type != VAR_UNKNOWN) { - int before = (int)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; // Type error; errmsg already given. - } + if (!is_new && value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + return; + } - if (before == tv_list_len(l1)) { - item = NULL; - } else { - item = tv_list_find(l1, before); - if (item == NULL) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)before); - return; - } - } - } else { - item = NULL; + if (is_new) { + l1 = tv_list_copy(NULL, l1, false, get_copyID()); + if (l1 == NULL) { + return; } - tv_list_extend(l1, l2, item); + } - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval.v_list = l1, - }; + listitem_T *item; + if (argvars[2].v_type != VAR_UNKNOWN) { + int before = (int)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } + + if (before == tv_list_len(l1)) { + item = NULL; } else { - tv_copy(&argvars[0], rettv); + item = tv_list_find(l1, before); + if (item == NULL) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)before); + return; + } } + } else { + item = NULL; + } + tv_list_extend(l1, l2, item); + + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = l1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -1970,54 +1988,61 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv) { dict_T *d1 = argvars[0].vval.v_dict; - dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); - } else if (d2 == NULL) { + return; + } + dict_T *const d2 = argvars[1].vval.v_dict; + if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - d1 = tv_dict_copy(NULL, d1, false, get_copyID()); - if (d1 == NULL) { - return; - } + return; + } + + if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + if (is_new) { + d1 = tv_dict_copy(NULL, d1, false, get_copyID()); + if (d1 == NULL) { + return; } + } - const char *action = "force"; - // Check the third argument. - if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const av[] = { "keep", "force", "error" }; + const char *action = "force"; + // Check the third argument. + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const av[] = { "keep", "force", "error" }; - action = tv_get_string_chk(&argvars[2]); - if (action == NULL) { - return; // Type error; error message already given. - } - size_t i; - for (i = 0; i < ARRAY_SIZE(av); i++) { - if (strcmp(action, av[i]) == 0) { - break; - } - } - if (i == 3) { - semsg(_(e_invarg2), action); - return; + action = tv_get_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { + break; } } + if (i == 3) { + semsg(_(e_invarg2), action); + return; + } + } - tv_dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_DICT, - .v_lock = VAR_UNLOCKED, - .vval.v_dict = d1, - }; - } else { - tv_copy(&argvars[0], rettv); - } + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = d1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -2068,8 +2093,8 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) flags = tv_get_string_buf(&argvars[1], nbuf); } - nvim_feedkeys(cstr_as_string((char *)keys), - cstr_as_string((char *)flags), true); + nvim_feedkeys(cstr_as_string(keys), + cstr_as_string(flags), true); } /// "filereadable()" function @@ -2175,7 +2200,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) { + if (f <= (float_T)(-VARNUMBER_MAX) + DBL_EPSILON) { rettv->vval.v_number = -VARNUMBER_MAX; } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { rettv->vval.v_number = VARNUMBER_MAX; @@ -2219,8 +2244,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) len = strlen(fname); if (*mods != NUL) { size_t usedlen = 0; - (void)modify_fname((char *)mods, false, &usedlen, - (char **)&fname, &fbuf, &len); + modify_fname((char *)mods, false, &usedlen, + (char **)&fname, &fbuf, &len); } } @@ -2444,7 +2469,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool : (varnumber_T)0)); tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { - const int save_set_curswant = curwin->w_set_curswant; + const bool save_set_curswant = curwin->w_set_curswant; const colnr_T save_curswant = curwin->w_curswant; const colnr_T save_virtcol = curwin->w_virtcol; @@ -2776,6 +2801,167 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) getpos_both(argvars, rettv, false, false); } +/// Convert from block_def to string +static char *block_def2str(struct block_def *bd) +{ + size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen; + char *ret = xmalloc(size + 1); + char *p = ret; + memset(p, ' ', (size_t)bd->startspaces); + p += bd->startspaces; + memmove(p, bd->textstart, (size_t)bd->textlen); + p += bd->textlen; + memset(p, ' ', (size_t)bd->endspaces); + *(p + bd->endspaces) = NUL; + return ret; +} + +/// "getregion()" function +static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_list_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + int fnum1 = -1; + int fnum2 = -1; + pos_T p1, p2; + if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK + || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK + || fnum1 != fnum2) { + return; + } + + bool is_select_exclusive; + char *type; + char default_type[] = "v"; + if (argvars[2].v_type == VAR_DICT) { + is_select_exclusive = tv_dict_get_bool(argvars[2].vval.v_dict, "exclusive", + *p_sel == 'e'); + type = tv_dict_get_string(argvars[2].vval.v_dict, "type", false); + if (type == NULL) { + type = default_type; + } + } else { + is_select_exclusive = *p_sel == 'e'; + type = default_type; + } + + MotionType region_type = kMTUnknown; + if (type[0] == 'v' && type[1] == NUL) { + region_type = kMTCharWise; + } else if (type[0] == 'V' && type[1] == NUL) { + region_type = kMTLineWise; + } else if (type[0] == Ctrl_V && type[1] == NUL) { + region_type = kMTBlockWise; + } else { + return; + } + + buf_T *const save_curbuf = curbuf; + + if (fnum1 != 0) { + buf_T *findbuf = buflist_findnr(fnum1); + // buffer not loaded + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + return; + } + curbuf = findbuf; + } + + const TriState save_virtual = virtual_op; + virtual_op = virtual_active(); + + // NOTE: Adjust is needed. + p1.col--; + p2.col--; + + if (!lt(p1, p2)) { + // swap position + pos_T p = p1; + p1 = p2; + p2 = p; + } + + oparg_T oa; + bool inclusive = true; + + if (region_type == kMTCharWise) { + // handle 'selection' == "exclusive" + if (is_select_exclusive && !equalpos(p1, p2)) { + if (p2.coladd > 0) { + p2.coladd--; + } else if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } else if (p2.lnum > 1) { + p2.lnum--; + p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } + } + } + // if fp2 is on NUL (empty line) inclusive becomes false + if (*ml_get_pos(&p2) == NUL && !virtual_op) { + inclusive = false; + } + } else if (region_type == kMTBlockWise) { + colnr_T sc1, ec1, sc2, ec2; + getvvcol(curwin, &p1, &sc1, NULL, &ec1); + getvvcol(curwin, &p2, &sc2, NULL, &ec2); + oa.motion_type = kMTBlockWise; + oa.inclusive = true; + oa.op_type = OP_NOP; + oa.start = p1; + oa.end = p2; + oa.start_vcol = MIN(sc1, sc2); + if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { + oa.end_vcol = sc2 - 1; + } else { + oa.end_vcol = MAX(ec1, ec2); + } + } + + // Include the trailing byte of a multi-byte char. + int l = utfc_ptr2len(ml_get_pos(&p2)); + if (l > 1) { + p2.col += l - 1; + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + char *akt = NULL; + + if (region_type == kMTLineWise) { + akt = xstrdup(ml_get(lnum)); + } else if (region_type == kMTBlockWise) { + struct block_def bd; + block_prep(&oa, &bd, lnum, false); + akt = block_def2str(&bd); + } else if (p1.lnum < lnum && lnum < p2.lnum) { + akt = xstrdup(ml_get(lnum)); + } else { + struct block_def bd; + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + akt = block_def2str(&bd); + } + + assert(akt != NULL); + tv_list_append_allocated_string(rettv->vval.v_list, akt); + } + + if (curbuf != save_curbuf) { + curbuf = save_curbuf; + } + + virtual_op = save_virtual; +} + /// Common between getreg(), getreginfo() and getregtype(): get the register /// name from the first argument. /// Returns zero on error. @@ -3140,7 +3326,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "path_extra", "persistent_undo", "profile", - "pythonx", "reltime", "quickfix", "rightleft", @@ -3182,6 +3367,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "xattr", #endif "nvim", + "rneovim", }; // XXX: eval_has_provider() may shell out :( @@ -3231,6 +3417,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = syntax_present(curwin); } else if (STRICMP(name, "clipboard_working") == 0) { n = eval_has_provider("clipboard"); + } else if (STRICMP(name, "pythonx") == 0) { + n = eval_has_provider("python3"); } else if (STRICMP(name, "wsl") == 0) { n = has_wsl(); #ifdef UNIX @@ -3253,13 +3441,11 @@ static bool has_wsl(void) static TriState has_wsl = kNone; if (has_wsl == kNone) { Error err = ERROR_INIT; - Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()" - ":match('microsoft') and true or false"), - (Array)ARRAY_DICT_INIT, &err); + Object o = NLUA_EXEC_STATIC("return vim.uv.os_uname()['release']:lower()" + ":match('microsoft')", + (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err); assert(!ERROR_SET(&err)); - assert(o.type == kObjectTypeBoolean); - has_wsl = o.data.boolean ? kTrue : kFalse; - api_free_object(o); + has_wsl = LUARET_TRUTHY(o) ? kTrue : kFalse; } return has_wsl == kTrue; } @@ -3639,7 +3825,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) }); // Ask for choice. - int mouse_used; + bool mouse_used; int selected = prompt_for_number(&mouse_used); if (mouse_used) { selected -= lines_left; @@ -3899,13 +4085,16 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = 1; } -static const char *ignored_env_vars[] = { +static const char *pty_ignored_env_vars[] = { #ifndef MSWIN "COLUMNS", "LINES", "TERMCAP", "COLORFGBG", + "COLORTERM", #endif + "VIM", + "VIMRUNTIME", NULL }; @@ -3944,9 +4133,9 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en // child process. We're removing them here so the user can still decide // they want to explicitly set them. for (size_t i = 0; - i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i]; + i < ARRAY_SIZE(pty_ignored_env_vars) && pty_ignored_env_vars[i]; i++) { - dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1); + dictitem_T *dv = tv_dict_find(env, pty_ignored_env_vars[i], -1); if (dv) { tv_dict_item_remove(env, dv); } @@ -3954,10 +4143,6 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en #ifndef MSWIN // Set COLORTERM to "truecolor" if termguicolors is set if (p_tgc) { - dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); - if (dv) { - tv_dict_item_remove(env, dv); - } tv_dict_add_str(env, S_LEN("COLORTERM"), "truecolor"); } #endif @@ -4056,8 +4241,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool clear_env = false; bool overlapped = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; dictitem_T *job_env = NULL; @@ -4120,7 +4305,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - uint16_t width = 0, height = 0; + uint16_t width = 0; + uint16_t height = 0; char *term_name = NULL; if (pty) { @@ -4168,7 +4354,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *error = NULL; if (data->is_rpc) { // Ignore return code, but show error later. - (void)channel_close(data->id, kChannelPartRpc, &error); + channel_close(data->id, kChannelPartRpc, &error); } process_stop(&data->stream.proc); rettv->vval.v_number = 1; @@ -4503,7 +4689,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv); + nlua_typval_eval(cstr_as_string(str), &argvars[1], rettv); } static void find_some_match(typval_T *const argvars, typval_T *const rettv, @@ -4714,6 +4900,151 @@ theend: p_cpo = save_cpo; } +/// Return all the matches in string "str" for pattern "rmp". +/// The matches are returned in the List "mlist". +/// If "submatches" is true, then submatch information is also returned. +/// "matchbuf" is true when called for matchbufline(). +static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, int idx, + bool submatches, bool matchbuf) +{ + size_t len = strlen(str); + int match = 0; + colnr_T startidx = 0; + + while (true) { + match = vim_regexec_nl(rmp, str, startidx); + if (!match) { + break; + } + + dict_T *d = tv_dict_alloc(); + tv_list_append_dict(mlist, d); + + if (matchbuf) { + tv_dict_add_nr(d, S_LEN("lnum"), idx); + } else { + tv_dict_add_nr(d, S_LEN("idx"), idx); + } + + tv_dict_add_nr(d, S_LEN("byteidx"), + (colnr_T)(rmp->startp[0] - str)); + + tv_dict_add_str_len(d, S_LEN("text"), rmp->startp[0], + (int)(rmp->endp[0] - rmp->startp[0])); + + if (submatches) { + list_T *sml = tv_list_alloc(NSUBEXP - 1); + + tv_dict_add_list(d, S_LEN("submatches"), sml); + + // return a list with the submatches + for (int i = 1; i < NSUBEXP; i++) { + if (rmp->endp[i] == NULL) { + tv_list_append_string(sml, "", 0); + } else { + tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]); + } + } + } + startidx = (colnr_T)(rmp->endp[0] - str); + if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) { + break; + } + } +} + +/// "matchbufline()" function +static void f_matchbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_buffer_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_lnum_arg(argvars, 2) == FAIL + || tv_check_for_lnum_arg(argvars, 3) == FAIL + || tv_check_for_opt_dict_arg(argvars, 4) == FAIL) { + return; + } + + const int prev_did_emsg = did_emsg; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + if (did_emsg == prev_did_emsg) { + semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0])); + } + return; + } + if (buf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf(&argvars[1], patbuf); + + const int did_emsg_before = did_emsg; + linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (slnum < 1) { + semsg(_(e_invargval), "lnum"); + return; + } + + linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (elnum < 1 || elnum < slnum) { + semsg(_(e_invargval), "end_lnum"); + return; + } + + if (elnum > buf->b_ml.ml_line_count) { + elnum = buf->b_ml.ml_line_count; + } + + bool submatches = false; + if (argvars[4].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[4].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + return; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + while (slnum <= elnum) { + const char *str = ml_get_buf(buf, slnum); + get_matches_in_str(str, ®match, retlist, slnum, submatches, true); + slnum++; + } + + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "match()" function static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -4738,6 +5069,73 @@ static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) find_some_match(argvars, rettv, kSomeMatchStr); } +/// "matchstrlist()" function +static void f_matchstrlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + list_T *l = NULL; + if ((l = argvars[0].vval.v_list) == NULL) { + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { + return; + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + bool submatches = false; + if (argvars[2].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[2].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + goto cleanup; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + const typval_T *const li_tv = TV_LIST_ITEM_TV(li); + if (li_tv->v_type == VAR_STRING && li_tv->vval.v_string != NULL) { + const char *str = li_tv->vval.v_string; + get_matches_in_str(str, ®match, retlist, idx, submatches, false); + } + idx++; + }); + +cleanup: + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -5216,7 +5614,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (!did_emsg) { char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); + vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -5620,6 +6018,12 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } } + if (blob) { + tv_blob_alloc_ret(rettv); + } else { + tv_list_alloc_ret(rettv, kListLenUnknown); + } + // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -5634,7 +6038,6 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } if (blob) { - tv_blob_alloc_ret(rettv); if (read_blob(fd, rettv, offset, size) == FAIL) { semsg(_(e_notread), fname); } @@ -5642,7 +6045,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl return; } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *const l = rettv->vval.v_list; while (maxline < 0 || tv_list_len(l) < maxline) { int readlen = (int)fread(buf, 1, (size_t)io_size, fd); @@ -5807,7 +6210,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (list == NULL) { return; } - (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); char buf[NUMBUFLEN + 2]; buf[0] = NUL; @@ -5826,15 +6229,15 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) case kMTUnknown: abort(); } - (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); buf[0] = (char)get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') { - (void)tv_dict_add_str(dict, S_LEN("points_to"), buf); + tv_dict_add_str(dict, S_LEN("points_to"), buf); } else { - (void)tv_dict_add_bool(dict, S_LEN("isunnamed"), - regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_bool(dict, S_LEN("isunnamed"), + regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); } } @@ -6648,16 +7051,17 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, tv_get_string(&argvars[1]), args); - api_free_array(args); + arena_mem_free(arena_finish(&arena)); if (!ok) { semsg(_(e_invarg2), "Channel doesn't exist"); @@ -6687,10 +7091,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } sctx_T save_current_sctx; @@ -6726,6 +7131,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); + arena_mem_free(arena_finish(&arena)); if (l_provider_call_nesting) { current_sctx = save_current_sctx; @@ -6755,10 +7161,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim(result, rettv, &err); end: arena_mem_free(res_mem); @@ -6889,15 +7292,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ScreenGrid *grid; screenchar_adjust(&grid, &row, &col); - int c; - if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { - c = -1; - } else { - char buf[MAX_SCHAR_SIZE + 1]; - schar_get(buf, grid_getchar(grid, row, col, NULL)); - c = utf_ptr2char(buf); - } - rettv->vval.v_number = c; + rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) + ? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL)); } /// "screenchars()" function @@ -7247,7 +7643,7 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8090,7 +8486,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (*p == '+' || *p == '-') { p = skipwhite(p + 1); } - (void)string2float(p, &rettv->vval.v_float); + string2float(p, &rettv->vval.v_float); if (isneg) { rettv->vval.v_float *= -1; } @@ -8391,7 +8787,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int syntax_flags = 0; - int cchar; int matchid = 0; char str[NUMBUFLEN]; @@ -8405,19 +8800,18 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, false, NULL, false); + syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { - cchar = syn_get_sub_char(); + schar_T cchar = schar_from_char(syn_get_sub_char()); if (cchar == NUL && curwin->w_p_cole == 1) { cchar = (curwin->w_p_lcs_chars.conceal == NUL) - ? ' ' - : curwin->w_p_lcs_chars.conceal; + ? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal; } if (cchar != NUL) { - utf_char2bytes(cchar, str); + schar_get(str, cchar); } } } @@ -8443,7 +8837,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) && col >= 0 && (size_t)col <= strlen(ml_get(lnum))) { tv_list_alloc_ret(rettv, kListLenMayKnow); - (void)syn_get_id(curwin, lnum, col, false, NULL, true); + syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -8517,8 +8911,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), - (char *)tag_pattern, (char *)fname); + get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char *)tag_pattern, (char *)fname); } /// "tempname()" function @@ -8558,8 +8952,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; @@ -8637,17 +9031,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curbuf->b_p_swf = false; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, NameBuff, NULL, true); + setfname(curbuf, NameBuff, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ((Integer)chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), false, false, &err); + INTEGER_OBJ(pid), false, false, NULL, &err); api_clear_error(&err); channel_incref(chan); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 0c345dacb4..e3a574b233 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include <stdint.h> -#include "nvim/buffer_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" #include "nvim/pos_defs.h" // IWYU pragma: keep diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h index 36149ec060..ea91952fff 100644 --- a/src/nvim/eval/gc.h +++ b/src/nvim/eval/gc.h @@ -6,5 +6,5 @@ extern dict_T *gc_first_dict; extern list_T *gc_first_list; #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval/gc.h.generated.h" // IWYU pragma: export +# include "eval/gc.h.generated.h" #endif diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 069cdced34..9328f53dbd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -5,7 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" @@ -20,10 +20,11 @@ #include "nvim/eval/vars.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" -#include "nvim/lib/queue.h" +#include "nvim/hashtab_defs.h" +#include "nvim/lib/queue_defs.h" #include "nvim/lua/executor.h" #include "nvim/macros_defs.h" #include "nvim/mbyte.h" @@ -622,13 +623,14 @@ tv_list_copy_error: listitem_T *tv_list_check_range_index_one(list_T *const l, int *const n1, const bool quiet) { listitem_T *li = tv_list_find_index(l, n1); - if (li == NULL) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1)); - } - return NULL; + if (li != NULL) { + return li; } - return li; + + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n1)); + } + return NULL; } /// Check that "n2" can be used as the second index in a range of list "l". @@ -1633,11 +1635,13 @@ static listitem_T *tv_list_find_index(list_T *const l, int *const idx) FUNC_ATTR_WARN_UNUSED_RESULT { listitem_T *li = tv_list_find(l, *idx); - if (li == NULL) { - if (*idx < 0) { - *idx = 0; - li = tv_list_find(l, *idx); - } + if (li != NULL) { + return li; + } + + if (*idx < 0) { + *idx = 0; + li = tv_list_find(l, *idx); } return li; } @@ -1801,10 +1805,10 @@ void callback_copy(Callback *dest, Callback *src) } /// Generate a string description of a callback -char *callback_to_string(Callback *cb) +char *callback_to_string(Callback *cb, Arena *arena) { if (cb->type == kCallbackLua) { - return nlua_funcref_str(cb->data.luaref); + return nlua_funcref_str(cb->data.luaref, arena); } const size_t msglen = 100; @@ -2129,10 +2133,12 @@ void tv_dict_free_dict(dict_T *const d) void tv_dict_free(dict_T *const d) FUNC_ATTR_NONNULL_ALL { - if (!tv_in_free_unref_items) { - tv_dict_free_contents(d); - tv_dict_free_dict(d); + if (tv_in_free_unref_items) { + return; } + + tv_dict_free_contents(d); + tv_dict_free_dict(d); } /// Unreference a dictionary @@ -2910,7 +2916,7 @@ static int tv_blob_index(const blob_T *blob, int len, varnumber_T idx, typval_T return OK; } -int tv_blob_slice_or_index(const blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, +int tv_blob_slice_or_index(const blob_T *blob, bool is_range, varnumber_T n1, varnumber_T n2, bool exclusive, typval_T *rettv) { int len = tv_blob_len(rettv->vval.v_blob); @@ -4345,6 +4351,22 @@ int tv_check_for_string_or_number_arg(const typval_T *const args, const int idx) return OK; } +/// Give an error and return FAIL unless "args[idx]" is a buffer number. +/// Buffer number can be a number or a string. +int tv_check_for_buffer_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return tv_check_for_string_or_number_arg(args, idx); +} + +/// Give an error and return FAIL unless "args[idx]" is a line number. +/// Line number can be a number or a string. +int tv_check_for_lnum_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return tv_check_for_string_or_number_arg(args, idx); +} + /// Give an error and return FAIL unless "args[idx]" is a string or a list. int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 58f74a9796..f9ebd2f778 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -6,14 +6,13 @@ #include <stdint.h> #include <string.h> -#include "nvim/eval/typval_defs.h" // IWYU pragma: export +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" -#include "nvim/garray_defs.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/hashtab.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/macros_defs.h" -#include "nvim/mbyte_defs.h" +#include "nvim/mbyte_defs.h" // IWYU pragma: keep #include "nvim/message.h" #include "nvim/types_defs.h" @@ -85,6 +84,9 @@ static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock) l->lv_lock = lock; } +static inline void tv_list_set_copyid(list_T *l, int copyid) + REAL_FATTR_NONNULL_ALL; + /// Set list copyID /// /// Does not expect NULL list, be careful. @@ -92,7 +94,6 @@ static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock) /// @param[out] l List to modify. /// @param[in] copyid New copyID. static inline void tv_list_set_copyid(list_T *const l, const int copyid) - FUNC_ATTR_NONNULL_ALL { l->lv_copyID = copyid; } @@ -359,7 +360,7 @@ extern bool tv_in_free_unref_items; /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define TV_LIST_ITER(l, li, code) \ - TV_LIST_ITER_MOD( , l, li, code) // NOLINT(whitespace/parens) + TV_LIST_ITER_MOD( , l, li, code) /// Iterate over a list /// @@ -442,22 +443,20 @@ static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret } static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE - REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE - FUNC_ATTR_NO_SANITIZE_ADDRESS; + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET + REAL_FATTR_NO_SANITIZE_ADDRESS REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Compute the `DictWatcher` address from a QUEUE node. /// /// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer /// arithmetic). static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - FUNC_ATTR_NO_SANITIZE_ADDRESS { return QUEUE_DATA(q, DictWatcher, node); } static inline bool tv_is_func(typval_T tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; /// Check whether given typval_T contains a function /// diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index c6bd11ccdb..0d6ee28adc 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -2,10 +2,11 @@ #include <inttypes.h> #include <limits.h> +#include <stdbool.h> #include "nvim/garray_defs.h" #include "nvim/hashtab_defs.h" -#include "nvim/lib/queue.h" +#include "nvim/lib/queue_defs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" @@ -13,8 +14,10 @@ typedef int64_t varnumber_T; typedef uint64_t uvarnumber_T; -/// Refcount for dict or list that should not be freed -enum { DO_NOT_FREE_CNT = (INT_MAX / 2), }; +enum { + /// Refcount for dict or list that should not be freed + DO_NOT_FREE_CNT = (INT_MAX / 2), +}; /// Additional values for tv_list_alloc() len argument enum ListLenSpecials { @@ -72,7 +75,7 @@ typedef struct { #define CALLBACK_NONE ((Callback)CALLBACK_INIT) /// Structure holding dictionary watcher -typedef struct dict_watcher { +typedef struct { Callback callback; char *key_pattern; size_t key_pattern_len; @@ -291,12 +294,9 @@ typedef struct { uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT. } LastSet; -/// Maximum number of function arguments -enum { MAX_FUNC_ARGS = 20, }; -/// Short variable name length -enum { VAR_SHORT_LEN = 20, }; -/// Number of fixed variables used for arguments -enum { FIXVAR_CNT = 12, }; +enum { MAX_FUNC_ARGS = 20, }; ///< Maximum number of function arguments +enum { VAR_SHORT_LEN = 20, }; ///< Short variable name length +enum { FIXVAR_CNT = 12, }; ///< Number of fixed variables used for arguments /// Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 2e0b68d486..c0cd0ce557 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -246,11 +246,12 @@ #include <inttypes.h> #include <stddef.h> +#include "klib/kvec.h" +#include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/func_attr.h" -#include "klib/kvec.h" /// Dummy variable used because some macros need lvalue /// diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 33fab82d21..d16814ed1e 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -10,6 +10,8 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" @@ -22,11 +24,13 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/ex_eval_defs.h" #include "nvim/ex_getln.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/insexpand.h" @@ -41,11 +45,13 @@ #include "nvim/path.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_defs.h" #include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -105,8 +111,6 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * bool mustend = false; char *arg = *argp; char *p = arg; - uint8_t c; - int i; if (newargs != NULL) { ga_init(newargs, (int)sizeof(char *), 3); @@ -143,12 +147,12 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * } if (newargs != NULL) { ga_grow(newargs, 1); - c = (uint8_t)(*p); + uint8_t c = (uint8_t)(*p); *p = NUL; arg = xstrdup(arg); // Check for duplicate argument name. - for (i = 0; i < newargs->ga_len; i++) { + for (int i = 0; i < newargs->ga_len; i++) { if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) { semsg(_("E853: Duplicate argument name: %s"), arg); xfree(arg); @@ -174,7 +178,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * while (p > expr && ascii_iswhite(p[-1])) { p--; } - c = (uint8_t)(*p); + uint8_t c = (uint8_t)(*p); *p = NUL; expr = xstrdup(expr); ((char **)(default_args->ga_data))[default_args->ga_len] = expr; @@ -326,7 +330,6 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) if (evaluate) { int flags = 0; - char *p; garray_T newlines; char *name = get_lambda_name(); @@ -339,7 +342,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) // Add "return " before the expression. size_t len = (size_t)(7 + end - start + 1); - p = xmalloc(len); + char *p = xmalloc(len); ((char **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); xstrlcpy(p + 7, start, (size_t)(end - start) + 1); @@ -919,11 +922,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett FUNC_ATTR_NONNULL_ARG(1, 3, 4) { bool using_sandbox = false; - int save_did_emsg; static int depth = 0; dictitem_T *v; int fixvar_idx = 0; // index in fc_fixvar[] - int ai; bool islambda = false; char numbuf[NUMBUFLEN]; char *name; @@ -931,7 +932,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett int tv_to_free_len = 0; proftime_T wait_start; proftime_T call_start; - int started_profiling = false; + bool started_profiling = false; bool did_save_redo = false; save_redo_T save_redo; char* saved_repeat_cmdline = NULL; @@ -1030,7 +1031,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool isdefault = false; typval_T def_rettv; - ai = i - fp->uf_args.ga_len; + int ai = i - fp->uf_args.ga_len; if (ai < 0) { // named argument a:name name = FUNCARG(fp, i); @@ -1173,7 +1174,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett const sctx_T save_current_sctx = current_sctx; current_sctx = fp->uf_script_ctx; - save_did_emsg = did_emsg; + int save_did_emsg = did_emsg; did_emsg = false; if (default_arg_err && (fp->uf_flags & FC_ABORT)) { @@ -1184,7 +1185,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ex_nesting_level++; - (void)eval1(&p, rettv, &EVALARG_EVALUATE); + eval1(&p, rettv, &EVALARG_EVALUATE); ex_nesting_level--; } else { // call do_cmdline() to execute the lines @@ -2080,7 +2081,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi) if (strncmp(p, "<lambda>", 8) == 0) { p += 8; - (void)getdigits(&p, false, 0); + getdigits(&p, false, 0); saved = xmemdupz(*name, (size_t)(p - *name)); if (fudi != NULL) { CLEAR_POINTER(fudi); @@ -3044,7 +3045,7 @@ static inline bool fc_referenced(const funccall_T *const fc) /// @return true if items in "fc" do not have "copyID". That means they are not /// referenced from anywhere that is in use. -static int can_free_funccal(funccall_T *fc, int copyID) +static bool can_free_funccal(funccall_T *fc, int copyID) { return fc->fc_l_varlist.lv_copyID != copyID && fc->fc_l_vars.dv_copyID != copyID @@ -3057,7 +3058,7 @@ void ex_return(exarg_T *eap) { char *arg = eap->arg; typval_T rettv; - int returning = false; + bool returning = false; if (current_funccal == NULL) { emsg(_("E133: :return not inside a function")); @@ -3404,7 +3405,7 @@ end: /// /// @return true when the return can be carried out, /// false when the return gets pending. -int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) +bool do_return(exarg_T *eap, bool reanimate, bool is_cmd, void *rettv) { cstack_T *const cstack = eap->cstack; @@ -3669,7 +3670,7 @@ bool free_unref_funccal(int copyID, int testing) if (did_free_funccal) { // When a funccal was freed some more items might be garbage // collected, so run again. - (void)garbage_collect(testing); + garbage_collect(testing); } return did_free; } @@ -3825,7 +3826,7 @@ bool set_ref_in_previous_funccal(int copyID) fc->fc_copyID = copyID + 1; if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID + 1, NULL) || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_list(&fc->fc_l_varlist, copyID + 1, NULL)) { + || set_ref_in_list_items(&fc->fc_l_varlist, copyID + 1, NULL)) { return true; } } @@ -3838,7 +3839,7 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) fc->fc_copyID = copyID; if (set_ref_in_ht(&fc->fc_l_vars.dv_hashtab, copyID, NULL) || set_ref_in_ht(&fc->fc_l_avars.dv_hashtab, copyID, NULL) - || set_ref_in_list(&fc->fc_l_varlist, copyID, NULL) + || set_ref_in_list_items(&fc->fc_l_varlist, copyID, NULL) || set_ref_in_func(NULL, fc->fc_func, copyID)) { return true; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 8050caab2b..b3488b15a7 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -4,15 +4,13 @@ #include <stddef.h> #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep -#include "nvim/eval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/eval_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/hashtab_defs.h" // IWYU pragma: keep #include "nvim/pos_defs.h" #include "nvim/types_defs.h" // IWYU pragma: keep -struct funccal_entry; - // From user function to hashitem and back. #define UF2HIKEY(fp) ((fp)->uf_name) #define HIKEY2UF(p) ((ufunc_T *)((p) - offsetof(ufunc_T, uf_name))) diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 2968f75f4d..91ac60d8ea 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -7,10 +7,11 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> +#include <uv.h> #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" @@ -18,16 +19,17 @@ #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" +#include "nvim/eval_defs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/macros_defs.h" @@ -44,6 +46,8 @@ #include "nvim/vim_defs.h" #include "nvim/window.h" +typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/vars.c.generated.h" #endif @@ -377,7 +381,7 @@ void ex_let(exarg_T *eap) if (!eap->skip) { op[0] = '='; op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); + ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); } tv_clear(&rettv); } @@ -414,7 +418,7 @@ void ex_let(exarg_T *eap) clear_evalarg(&evalarg, eap); if (!eap->skip && eval_res != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); + ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); } if (eval_res != FAIL) { tv_clear(&rettv); @@ -553,7 +557,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) static const char *skip_var_one(const char *arg) { if (*arg == '@' && arg[1] != NUL) { - return arg + 2; + return arg + 1 + utfc_ptr2len(arg + 1); } return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); @@ -617,7 +621,7 @@ static void list_tab_vars(int *first) /// List variables in "arg". static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) { - int error = false; + bool error = false; int len; const char *name; const char *name_start; @@ -762,11 +766,12 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, // Find the end of the name. char *arg_end = NULL; + OptIndex opt_idx; int scope; - char *const p = (char *)find_option_end((const char **)&arg, &scope); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { + + char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &scope); + + if (p == NULL || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); return NULL; } @@ -774,11 +779,12 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, const char c1 = *p; *p = NUL; - uint32_t opt_p_flags; - bool hidden; - OptVal curval = get_option_value(arg, &opt_p_flags, scope, &hidden); + bool is_tty_opt = is_tty_option(arg); + bool hidden = is_option_hidden(opt_idx); + OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, scope); OptVal newval = NIL_OPTVAL; - if (curval.type == kOptValTypeNil && arg[0] != 't' && arg[1] != '_') { + + if (curval.type == kOptValTypeNil) { semsg(_(e_unknown_option2), arg); goto theend; } @@ -790,7 +796,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, } bool error; - newval = tv_to_optval(tv, arg, opt_p_flags, &error); + newval = tv_to_optval(tv, opt_idx, arg, &error); if (error) { goto theend; } @@ -832,7 +838,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, } } - const char *err = set_option_value(arg, newval, scope); + const char *err = set_option_value_handle_tty(arg, opt_idx, newval, scope); arg_end = p; if (err != NULL) { emsg(_(err)); @@ -857,16 +863,20 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, char *arg_end = NULL; arg++; + + int regname = utf_ptr2char(arg); + int mblen = utf_ptr2len(arg); + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { semsg(_(e_letwrong), op); } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + mblen))) == NULL) { emsg(_(e_letunexp)); } else { char *ptofree = NULL; const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + char *s = get_reg_contents(*arg == '@' ? '"' : regname, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); p = ptofree; @@ -874,8 +884,9 @@ static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); - arg_end = arg + 1; + write_reg_contents(*arg == '@' ? '"' : regname, + p, (ssize_t)strlen(p), false); + arg_end = arg + mblen; } xfree(ptofree); } @@ -1300,7 +1311,7 @@ void vars_clear(hashtab_T *ht) } /// Like vars_clear(), but only free the value if "free_val" is true. -void vars_clear_ext(hashtab_T *ht, int free_val) +void vars_clear_ext(hashtab_T *ht, bool free_val) { int todo; hashitem_T *hi; @@ -1847,22 +1858,27 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// /// @return Typval converted to OptVal. Must be freed by caller. /// Returns NIL_OPTVAL for invalid option name. -static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error) +/// +/// TODO(famiu): Refactor this to support multitype options. +static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error) { OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; bool err = false; + const bool is_tty_opt = is_tty_option(option); + const bool option_has_bool = !is_tty_opt && option_has_type(opt_idx, kOptValTypeBoolean); + const bool option_has_num = !is_tty_opt && option_has_type(opt_idx, kOptValTypeNumber); + const bool option_has_str = is_tty_opt || option_has_type(opt_idx, kOptValTypeString); - if ((flags & P_FUNC) && tv_is_func(*tv)) { + if (!is_tty_opt && (get_option(opt_idx)->flags & P_FUNC) && tv_is_func(*tv)) { // If the option can be set to a function reference or a lambda // and the passed value is a function reference, then convert it to // the name (string) of the function reference. char *strval = encode_tv2string(tv, NULL); err = strval == NULL; value = CSTR_AS_OPTVAL(strval); - } else if (flags & (P_NUM | P_BOOL)) { - varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err) - : tv_get_bool_chk(tv, &err); + } else if (option_has_bool || option_has_num) { + varnumber_T n = option_has_num ? tv_get_number_chk(tv, &err) : tv_get_bool_chk(tv, &err); // This could be either "0" or a string that's not a number. // So we need to check if it's actually a number. if (!err && tv->v_type == VAR_STRING && n == 0) { @@ -1875,14 +1891,14 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } - value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); - } else if ((flags & P_STRING) || is_tty_option(option)) { + value = option_has_num ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); + } else if (option_has_str) { // Avoid setting string option to a boolean or a special value. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { const char *strval = tv_get_string_buf_chk(tv, nbuf); err = strval == NULL; value = CSTR_TO_OPTVAL(strval); - } else if (flags & P_STRING) { + } else if (!is_tty_opt) { err = true; emsg(_(e_stringreq)); } @@ -1898,10 +1914,12 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo /// Convert an option value to typval. /// -/// @param[in] value Option value to convert. +/// @param[in] value Option value to convert. +/// @param numbool Whether to convert boolean values to number. +/// Used for backwards compatibility. /// /// @return OptVal converted to typval. -typval_T optval_as_tv(OptVal value) +typval_T optval_as_tv(OptVal value, bool numbool) { typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } }; @@ -1909,19 +1927,16 @@ typval_T optval_as_tv(OptVal value) case kOptValTypeNil: break; case kOptValTypeBoolean: - switch (value.data.boolean) { - case kTrue: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarTrue; - break; - case kFalse: - rettv.v_type = VAR_BOOL; - rettv.vval.v_bool = kBoolVarFalse; - break; - case kNone: - break; // return v:null for None boolean value + if (value.data.boolean != kNone) { + if (numbool) { + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = value.data.boolean == kTrue; + } else { + rettv.v_type = VAR_BOOL; + rettv.vval.v_bool = value.data.boolean == kTrue; + } } - break; + break; // return v:null for None boolean value. case kOptValTypeNumber: rettv.v_type = VAR_NUMBER; rettv.vval.v_number = value.data.number; @@ -1938,25 +1953,27 @@ typval_T optval_as_tv(OptVal value) /// Set option "varname" to the value of "varp" for the current buffer/window. static void set_option_from_tv(const char *varname, typval_T *varp) { - int opt_idx = findoption(varname); - if (opt_idx < 0) { + OptIndex opt_idx = find_option(varname); + if (opt_idx == kOptInvalid) { semsg(_(e_unknown_option2), varname); return; } - uint32_t opt_p_flags = get_option(opt_idx)->flags; bool error = false; - OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error); + OptVal value = tv_to_optval(varp, opt_idx, varname, &error); if (!error) { - set_option_value_give_err(varname, value, OPT_LOCAL); - } + const char *errmsg = set_option_value_handle_tty(varname, opt_idx, value, OPT_LOCAL); + if (errmsg) { + emsg(errmsg); + } + } optval_free(value); } /// "setwinvar()" and "settabwinvar()" functions -static void setwinvar(typval_T *argvars, typval_T *rettv, int off) +static void setwinvar(typval_T *argvars, int off) { if (check_secure()) { return; @@ -2065,8 +2082,6 @@ void f_getbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "settabvar()" function void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - rettv->vval.v_number = 0; - if (check_secure()) { return; } @@ -2080,6 +2095,7 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } tabpage_T *const save_curtab = curtab; + tabpage_T *const save_lu_tp = lastused_tabpage; goto_tabpage_tp(tp, false, false); const size_t varname_len = strlen(varname); @@ -2089,22 +2105,25 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); - // Restore current tabpage. + // Restore current tabpage and last accessed tabpage. if (valid_tabpage(save_curtab)) { goto_tabpage_tp(save_curtab, false, false); + if (valid_tabpage(save_lu_tp)) { + lastused_tabpage = save_lu_tp; + } } } /// "settabwinvar()" function void f_settabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - setwinvar(argvars, rettv, 1); + setwinvar(argvars, 1); } /// "setwinvar()" function void f_setwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - setwinvar(argvars, rettv, 0); + setwinvar(argvars, 0); } /// "setbufvar()" function diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index e0abbad477..b8aa0c9641 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -9,19 +9,22 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" -#include "nvim/buffer_defs.h" #include "nvim/cursor.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" #include "nvim/garray.h" -#include "nvim/gettext.h" +#include "nvim/garray_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/macros_defs.h" +#include "nvim/mark_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" +#include "nvim/option_vars.h" +#include "nvim/os/fs.h" #include "nvim/pos_defs.h" #include "nvim/types_defs.h" #include "nvim/vim_defs.h" @@ -480,6 +483,68 @@ void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = nr; } +/// Switch to a window for executing user code. +/// Caller must call win_execute_after() later regardless of return value. +/// +/// @return whether switching the window succeeded. +bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp) +{ + args->wp = wp; + args->curpos = wp->w_cursor; + args->cwd_status = FAIL; + args->apply_acd = false; + + // Getting and setting directory can be slow on some systems, only do + // this when the current or target window/tab have a local directory or + // 'acd' is set. + if (curwin != wp + && (curwin->w_localdir != NULL || wp->w_localdir != NULL + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) + || p_acd)) { + args->cwd_status = os_dirname(args->cwd, MAXPATHL); + } + + // If 'acd' is set, check we are using that directory. If yes, then + // apply 'acd' afterwards, otherwise restore the current directory. + if (args->cwd_status == OK && p_acd) { + do_autochdir(); + char autocwd[MAXPATHL]; + if (os_dirname(autocwd, MAXPATHL) == OK) { + args->apply_acd = strcmp(args->cwd, autocwd) == 0; + } + } + + if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) { + check_cursor(); + return true; + } + return false; +} + +/// Restore the previous window after executing user code. +void win_execute_after(win_execute_T *args) +{ + restore_win_noblock(&args->switchwin, true); + + if (args->apply_acd) { + do_autochdir(); + } else if (args->cwd_status == OK) { + os_chdir(args->cwd); + } + + // Update the status line if the cursor moved. + if (win_valid(args->wp) && !equalpos(args->curpos, args->wp->w_cursor)) { + args->wp->w_redr_status = true; + } + + // In case the command moved the cursor or changed the Visual area, + // check it is valid. + check_cursor(); + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } +} + /// "win_execute(win_id, command)" function void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -494,7 +559,11 @@ void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1)); + win_execute_T win_execute_args; + if (win_execute_before(&win_execute_args, wp, tp)) { + execute_common(argvars, rettv, 1); + } + win_execute_after(&win_execute_args); } /// "win_findbuf()" function @@ -607,13 +676,13 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags // Remove the old window and frame from the tree of frames int dir; - (void)winframe_remove(wp, &dir, NULL); + winframe_remove(wp, &dir, NULL); win_remove(wp, NULL); last_status(false); // may need to remove last status line - (void)win_comp_pos(); // recompute window positions + win_comp_pos(); // recompute window positions // Split a window on the desired side and put the old window there - (void)win_split_ins(size, flags, wp, dir); + win_split_ins(size, flags, wp, dir); // If splitting horizontally, try to preserve height if (size == 0 && !(flags & WSP_VERT)) { @@ -642,7 +711,8 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - int flags = 0, size = 0; + int flags = 0; + int size = 0; if (argvars[2].v_type != VAR_UNKNOWN) { dict_T *d; @@ -685,7 +755,7 @@ void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_string = xstrdup("preview"); } else if (wp->w_floating) { rettv->vval.v_string = xstrdup("popup"); - } else if (wp == curwin && cmdwin_type != 0) { + } else if (wp == cmdwin_win) { rettv->vval.v_string = xstrdup("command"); } else if (bt_quickfix(wp->w_buffer)) { rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix")); @@ -914,16 +984,16 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n return OK; } -// Restore current tabpage and window saved by switch_win(), if still valid. -// When "no_display" is true the display won't be affected, no redraw is -// triggered. +/// Restore current tabpage and window saved by switch_win(), if still valid. +/// When "no_display" is true the display won't be affected, no redraw is +/// triggered. void restore_win(switchwin_T *switchwin, bool no_display) { restore_win_noblock(switchwin, no_display); unblock_autocmds(); } -// As restore_win() but without unblocking autocommands. +/// As restore_win() but without unblocking autocommands. void restore_win_noblock(switchwin_T *switchwin, bool no_display) { if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { diff --git a/src/nvim/eval/window.h b/src/nvim/eval/window.h index ed879c895a..37cb138404 100644 --- a/src/nvim/eval/window.h +++ b/src/nvim/eval/window.h @@ -1,20 +1,12 @@ #pragma once #include <stdbool.h> -#include <string.h> -#include "nvim/buffer.h" #include "nvim/buffer_defs.h" -#include "nvim/cursor.h" -#include "nvim/eval/typval_defs.h" -#include "nvim/globals.h" -#include "nvim/mark.h" -#include "nvim/option_defs.h" -#include "nvim/option_vars.h" -#include "nvim/os/fs.h" +#include "nvim/eval/typval_defs.h" // IWYU pragma: keep +#include "nvim/os/os_defs.h" #include "nvim/pos_defs.h" -#include "nvim/vim_defs.h" -#include "nvim/window.h" +#include "nvim/types_defs.h" /// Structure used by switch_win() to pass values to restore_win() typedef struct { @@ -24,53 +16,15 @@ typedef struct { bool sw_visual_active; } switchwin_T; -/// Execute a block of code in the context of window `wp` in tabpage `tp`. -/// Ensures the status line is redrawn and cursor position is valid if it is moved. -#define WIN_EXECUTE(wp, tp, block) \ - do { \ - win_T *const wp_ = (wp); \ - const pos_T curpos_ = wp_->w_cursor; \ - char cwd_[MAXPATHL]; \ - char autocwd_[MAXPATHL]; \ - bool apply_acd_ = false; \ - int cwd_status_ = FAIL; \ - /* Getting and setting directory can be slow on some systems, only do */ \ - /* this when the current or target window/tab have a local directory or */ \ - /* 'acd' is set. */ \ - if (curwin != wp \ - && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ - || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ - || p_acd)) { \ - cwd_status_ = os_dirname(cwd_, MAXPATHL); \ - } \ - /* If 'acd' is set, check we are using that directory. If yes, then */ \ - /* apply 'acd' afterwards, otherwise restore the current directory. */ \ - if (cwd_status_ == OK && p_acd) { \ - do_autochdir(); \ - apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && strcmp(cwd_, autocwd_) == 0; \ - } \ - switchwin_T switchwin_; \ - if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ - check_cursor(); \ - block; \ - } \ - restore_win_noblock(&switchwin_, true); \ - if (apply_acd_) { \ - do_autochdir(); \ - } else if (cwd_status_ == OK) { \ - os_chdir(cwd_); \ - } \ - /* Update the status line if the cursor moved. */ \ - if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ - wp_->w_redr_status = true; \ - } \ - /* In case the command moved the cursor or changed the Visual area, */ \ - /* check it is valid. */ \ - check_cursor(); \ - if (VIsual_active) { \ - check_pos(curbuf, &VIsual); \ - } \ - } while (false) +/// Structure used by win_execute_before() to pass values to win_execute_after() +typedef struct { + win_T *wp; + pos_T curpos; + char cwd[MAXPATHL]; + int cwd_status; + bool apply_acd; + switchwin_T switchwin; +} win_execute_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/window.h.generated.h" |