From f357c9bca59a58c8586a348d0d1dcd81116079a3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 18 Jul 2022 08:54:53 +0800 Subject: vim-patch:8.1.1076: file for Insert mode is much too big Problem: File for Insert mode is much too big. Solution: Split off the code for Insert completion. (Yegappan Lakshmanan, closes vim/vim#4044) https://github.com/vim/vim/commit/7591bb39d58ece38a5fef984a08ea9012616c1f9 Cherry-pick ins_compl_len() -> get_compl_len() from patch 8.2.4001. Revert a71c5e9eb98fbb2ca88510269935cdcda37369fc: ctrl_x_mode is no longer a global variable, so l_ctrl_x_mode is no longer needed. --- src/nvim/eval/funcs.c | 3 ++- src/nvim/eval/userfunc.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 022e2497b7..8817cd3e2d 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -36,6 +36,7 @@ #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/input.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mapping.h" @@ -1091,7 +1092,7 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) RedrawingDisabled = 0; ins_compl_check_keys(0, true); - rettv->vval.v_number = compl_interrupted; + rettv->vval.v_number = ins_compl_interrupted(); RedrawingDisabled = saved; } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index c2579944e4..d9102c07a5 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -16,6 +16,7 @@ #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/globals.h" +#include "nvim/insexpand.h" #include "nvim/lua/executor.h" #include "nvim/os/input.h" #include "nvim/regexp.h" -- cgit From 420bb2eb8a583391c38a1e6d60132a70de21d99e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 18 Jul 2022 10:54:49 +0800 Subject: vim-patch:8.1.1849 https://github.com/vim/vim/commit/9bca58f36d1f6a2ac0e4022caa5f355d39357a05 --- src/nvim/eval/funcs.c | 58 --------------------------------------------------- 1 file changed, 58 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8817cd3e2d..c58dbcd620 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1055,64 +1055,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_col(argvars, rettv, false); } -/// "complete()" function -static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if ((State & MODE_INSERT) == 0) { - emsg(_("E785: complete() can only be used in Insert mode")); - return; - } - - // Check for undo allowed here, because if something was already inserted - // the line was already saved for undo and this check isn't done. - if (!undo_allowed(curbuf)) { - return; - } - - if (argvars[1].v_type != VAR_LIST) { - emsg(_(e_invarg)); - } else { - const colnr_T startcol = tv_get_number_chk(&argvars[0], NULL); - if (startcol > 0) { - set_completion(startcol - 1, argvars[1].vval.v_list); - } - } -} - -/// "complete_add()" function -static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); -} - -/// "complete_check()" function -static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int saved = RedrawingDisabled; - - RedrawingDisabled = 0; - ins_compl_check_keys(0, true); - rettv->vval.v_number = ins_compl_interrupted(); - RedrawingDisabled = saved; -} - -/// "complete_info()" function -static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - tv_dict_alloc_ret(rettv); - - list_T *what_list = NULL; - - if (argvars[0].v_type != VAR_UNKNOWN) { - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - what_list = argvars[0].vval.v_list; - } - get_complete_info(what_list, rettv->vval.v_dict); -} - /// "confirm(message, buttons[, default [, type]])" function static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { -- cgit From 9d4a4f49ef74f3c14df63e3d32a20830bfa8c7a9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Jul 2022 21:14:17 +0800 Subject: vim-patch:8.1.1933: the eval.c file is too big (#19462) Problem: The eval.c file is too big. Solution: Move code related to variables to evalvars.c. (Yegappan Lakshmanan, closes vim/vim#4868) https://github.com/vim/vim/commit/0522ba0359c96a8c2a4fc8fca0d3b58e49dda759 Name the new file eval/vars.c instead. --- src/nvim/eval/executor.c | 2 - src/nvim/eval/funcs.c | 94 +-- src/nvim/eval/typval.c | 1 + src/nvim/eval/userfunc.c | 1 + src/nvim/eval/vars.c | 1715 ++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval/vars.h | 10 + 6 files changed, 1728 insertions(+), 95 deletions(-) create mode 100644 src/nvim/eval/vars.c create mode 100644 src/nvim/eval/vars.h (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 3e66150180..b461456a3a 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -12,8 +12,6 @@ # include "eval/executor.c.generated.h" #endif -static char *e_letwrong = N_("E734: Wrong variable type for %s="); - char *e_listidx = N_("E684: list index out of range: %" PRId64); /// Handle tv1 += tv2, -=, *=, /=, %=, .= diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c58dbcd620..6dcdccf773 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -25,6 +25,7 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" @@ -3707,50 +3708,6 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "gettabvar()" function -static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - const char *const varname = tv_get_string_chk(&argvars[1]); - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) { - // Set tp to be our tabpage, temporarily. Also set the window to the - // first window in the tabpage, otherwise the window is not valid. - win_T *const window = tp == curtab || tp->tp_firstwin == NULL - ? firstwin - : tp->tp_firstwin; - switchwin_T switchwin; - if (switch_win(&switchwin, window, tp, true) == OK) { - // look up the variable - // Let gettabvar({nr}, "") return the "t:" dictionary. - const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', - varname, strlen(varname), - false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - - // restore previous notion of curwin - restore_win(&switchwin, true); - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - tv_copy(&argvars[2], rettv); - } -} - -/// "gettabwinvar()" function -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 1); -} - /// "gettagstack()" function static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3974,12 +3931,6 @@ static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; } -/// "getwinvar()" function -static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - getwinvar(argvars, rettv, 0); -} - /// "glob()" function static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -8856,43 +8807,6 @@ free_lstval: } } -/// "settabvar()" function -static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = 0; - - if (check_secure()) { - return; - } - - tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = tv_get_string_chk(&argvars[1]); - typval_T *const varp = &argvars[2]; - - if (varname != NULL && tp != NULL) { - tabpage_T *const save_curtab = curtab; - goto_tabpage_tp(tp, false, false); - - const size_t varname_len = strlen(varname); - char *const tabvarname = xmalloc(varname_len + 3); - memcpy(tabvarname, "t:", 2); - memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varname_len + 2, varp, true); - xfree(tabvarname); - - // Restore current tabpage. - if (valid_tabpage(save_curtab)) { - goto_tabpage_tp(save_curtab, false, false); - } - } -} - -/// "settabwinvar()" function -static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 1); -} - /// "settagstack()" function static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -8946,12 +8860,6 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setwinvar()" function -static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - setwinvar(argvars, rettv, 0); -} - /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e19cf411c0..63458e7e69 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_encode.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d9102c07a5..b01415f052 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -10,6 +10,7 @@ #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c new file mode 100644 index 0000000000..021d2764a6 --- /dev/null +++ b/src/nvim/eval/vars.c @@ -0,0 +1,1715 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// eval/vars.c: functions for dealing with variables + +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/charset.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/eval/vars.h" +#include "nvim/ex_cmds.h" +#include "nvim/ex_docmd.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/search.h" +#include "nvim/window.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.c.generated.h" +#endif + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 // maximum nesting of lists and dicts + +static char *e_letunexp = N_("E18: Unexpected characters in :let"); +static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); + +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. +static list_T *heredoc_get(exarg_T *eap, char *cmd) +{ + char *marker; + char *p; + int marker_indent_len = 0; + int text_indent_len = 0; + char *text_indent = NULL; + + if (eap->getline == NULL) { + emsg(_("E991: cannot use =<< here")); + return NULL; + } + + // Check for the optional 'trim' word before the marker + cmd = skipwhite(cmd); + if (STRNCMP(cmd, "trim", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + + // Trim the indentation from all the lines in the here document. + // The amount of indentation trimmed is the same as the indentation of + // the first line after the :let command line. To find the end marker + // the indent of the :let command line is trimmed. + p = *eap->cmdlinep; + while (ascii_iswhite(*p)) { + p++; + marker_indent_len++; + } + text_indent_len = -1; + } + + // The marker is the next word. + if (*cmd != NUL && *cmd != '"') { + marker = skipwhite(cmd); + p = (char *)skiptowhite((char_u *)marker); + if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + emsg(_(e_trailing)); + return NULL; + } + *p = NUL; + if (islower(*marker)) { + emsg(_("E221: Marker cannot start with lower case letter")); + return NULL; + } + } else { + emsg(_("E172: Missing marker")); + return NULL; + } + + list_T *l = tv_list_alloc(0); + for (;;) { + int mi = 0; + int ti = 0; + + char *theline = eap->getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + semsg(_("E990: Missing end marker '%s'"), marker); + break; + } + + // with "trim": skip the indent matching the :let line to find the + // marker + if (marker_indent_len > 0 + && STRNCMP(theline, *eap->cmdlinep, marker_indent_len) == 0) { + mi = marker_indent_len; + } + if (STRCMP(marker, theline + mi) == 0) { + xfree(theline); + break; + } + if (text_indent_len == -1 && *theline != NUL) { + // set the text indent from the first line. + p = theline; + text_indent_len = 0; + while (ascii_iswhite(*p)) { + p++; + text_indent_len++; + } + text_indent = xstrnsave(theline, (size_t)text_indent_len); + } + // with "trim": skip the indent matching the first line + if (text_indent != NULL) { + for (ti = 0; ti < text_indent_len; ti++) { + if (theline[ti] != text_indent[ti]) { + break; + } + } + } + + tv_list_append_string(l, theline + ti, -1); + xfree(theline); + } + xfree(text_indent); + + return l; +} + +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. +void ex_let(exarg_T *eap) +{ + ex_let_const(eap, false); +} + +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +void ex_const(exarg_T *eap) +{ + ex_let_const(eap, true); +} + +static void ex_let_const(exarg_T *eap, const bool is_const) +{ + char *arg = eap->arg; + char *expr = NULL; + typval_T rettv; + int i; + int var_count = 0; + int semicolon = 0; + char op[2]; + char *argend; + int first = true; + + argend = (char *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { + return; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } + expr = skipwhite(argend); + if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { + // ":let" without "=": list variables + if (*arg == '[') { + emsg(_(e_invarg)); + } else if (!ends_excmd(*arg)) { + // ":let var1 var2" + arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + } else if (!eap->skip) { + // ":let" + list_glob_vars(&first); + list_buf_vars(&first); + list_win_vars(&first); + list_tab_vars(&first); + list_script_vars(&first); + list_func_vars(&first); + list_vim_vars(&first); + } + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); + } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + // HERE document + list_T *l = heredoc_get(eap, expr + 3); + if (l != NULL) { + tv_list_set_ret(&rettv, l); + if (!eap->skip) { + op[0] = '='; + op[1] = NUL; + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + } + tv_clear(&rettv); + } + } else { + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } + } + expr = skipwhite(expr + 2); + } else { + expr = skipwhite(expr + 1); + } + + if (eap->skip) { + emsg_skip++; + } + i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + if (eap->skip) { + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; + } else if (i != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, + is_const, (char *)op); + tv_clear(&rettv); + } + } +} + +/// Assign the typevalue "tv" to the variable or variables at "arg_start". +/// Handles both "var" with any type and "[var, var; var]" with a list type. +/// When "op" is not NULL it points to a string with characters that +/// must appear after the variable(s). Use "+", "-" or "." for add, subtract +/// or concatenate. +/// +/// @param copy copy values from "tv", don't move +/// @param semicolon from skip_var_list() +/// @param var_count from skip_var_list() +/// @param is_const lock variables for :const +/// +/// @return OK or FAIL; +int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_count, int is_const, + char *op) +{ + char *arg = arg_start; + typval_T ltv; + + if (*arg != '[') { + // ":let var = expr" or ":for var in list" + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { + return FAIL; + } + return OK; + } + + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { + emsg(_(e_listreq)); + return FAIL; + } + list_T *const l = tv->vval.v_list; + + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { + emsg(_("E687: Less targets than List items")); + return FAIL; + } + if (var_count - semicolon > len) { + emsg(_("E688: More targets than List items")); + return FAIL; + } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); + + listitem_T *item = tv_list_first(l); + size_t rest_len = (size_t)tv_list_len(l); + while (*arg != ']') { + arg = skipwhite(arg + 1); + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, ",;]", op); + if (arg == NULL) { + return FAIL; + } + rest_len--; + + item = TV_LIST_ITEM_NEXT(l, item); + arg = skipwhite(arg); + if (*arg == ';') { + // Put the rest of the list (may be empty) in the var after ';'. + // Create a new list for this. + list_T *const rest_list = tv_list_alloc((ptrdiff_t)rest_len); + while (item != NULL) { + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); + } + + ltv.v_type = VAR_LIST; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); + + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, "]", op); + tv_clear(<v); + if (arg == NULL) { + return FAIL; + } + break; + } else if (*arg != ',' && *arg != ']') { + internal_error("ex_let_vars()"); + return FAIL; + } + } + + return OK; +} + +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. +const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +{ + const char *p; + const char *s; + + if (*arg == '[') { + // "[var, var]": find the matching ']'. + p = arg; + for (;;) { + p = skipwhite(p + 1); // skip whites after '[', ';' or ',' + s = skip_var_one((char *)p); + if (s == p) { + semsg(_(e_invarg2), p); + return NULL; + } + (*var_count)++; + + p = skipwhite(s); + if (*p == ']') { + break; + } else if (*p == ';') { + if (*semicolon == 1) { + emsg(_("E452: Double ; in list of variables")); + return NULL; + } + *semicolon = 1; + } else if (*p != ',') { + semsg(_(e_invarg2), p); + return NULL; + } + } + return p + 1; + } else { + return skip_var_one((char *)arg); + } +} + +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. +static const char *skip_var_one(const char *arg) +{ + if (*arg == '@' && arg[1] != NUL) { + return arg + 2; + } + return (char *)find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); +} + +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if true also list NULL strings as empty strings. +void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) +{ + hashitem_T *hi; + dictitem_T *di; + int todo; + + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0 && !got_int; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + di = TV_DICT_HI2DI(hi); + char buf[IOSIZE]; + + // apply :filter /pat/ to variable name + xstrlcpy(buf, prefix, IOSIZE); + xstrlcat(buf, (char *)di->di_key, IOSIZE); + if (message_filtered((char_u *)buf)) { + continue; + } + + if (empty || di->di_tv.v_type != VAR_STRING + || di->di_tv.vval.v_string != NULL) { + list_one_var(di, prefix, first); + } + } + } +} + +/// List global variables. +static void list_glob_vars(int *first) +{ + list_hashtable_vars(&globvarht, "", true, first); +} + +/// List buffer variables. +static void list_buf_vars(int *first) +{ + list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); +} + +/// List window variables. +static void list_win_vars(int *first) +{ + list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); +} + +/// List tab page variables. +static void list_tab_vars(int *first) +{ + list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); +} + +/// List variables in "arg". +static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) +{ + int error = false; + int len; + const char *name; + const char *name_start; + typval_T tv; + + while (!ends_excmd(*arg) && !got_int) { + if (error || eap->skip) { + arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); + if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) { + emsg_severe = true; + emsg(_(e_trailing)); + break; + } + } else { + // get_name_len() takes care of expanding curly braces + name_start = name = arg; + char *tofree; + len = get_name_len(&arg, &tofree, true, true); + if (len <= 0) { + // This is mainly to keep test 49 working: when expanding + // curly braces fails overrule the exception error message. + if (len < 0 && !aborting()) { + emsg_severe = true; + semsg(_(e_invarg2), arg); + break; + } + error = true; + } else { + if (tofree != NULL) { + name = tofree; + } + if (get_var_tv(name, len, &tv, NULL, true, false) + == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) + const char *const arg_subsc = arg; + if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + error = true; + } else { + if (arg == arg_subsc && len == 2 && name[1] == ':') { + switch (*name) { + case 'g': + list_glob_vars(first); break; + case 'b': + list_buf_vars(first); break; + case 'w': + list_win_vars(first); break; + case 't': + list_tab_vars(first); break; + case 'v': + list_vim_vars(first); break; + case 's': + list_script_vars(first); break; + case 'l': + list_func_vars(first); break; + default: + semsg(_("E738: Can't list variables for %s"), name); + } + } else { + char *const s = encode_tv2echo(&tv, NULL); + const char *const used_name = (arg == arg_subsc + ? name + : name_start); + const ptrdiff_t name_size = (used_name == tofree + ? (ptrdiff_t)strlen(used_name) + : (arg - used_name)); + list_one_var_a("", used_name, name_size, + tv.v_type, s == NULL ? "" : s, first); + xfree(s); + } + tv_clear(&tv); + } + } + } + + xfree(tofree); + } + + arg = (const char *)skipwhite(arg); + } + + return arg; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *arg_end = NULL; + int len; + int opt_flags; + char *tofree = NULL; + + // ":let $VAR = expr": Set environment variable. + if (*arg == '$') { + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + // Find the end of the name. + arg++; + char *name = arg; + len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg)) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + const char c1 = name[len]; + name[len] = NUL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = vim_getenv(name); + + if (s != NULL) { + tofree = (char *)concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; + xfree(s); + } + } + if (p != NULL) { + os_setenv(name, p, 1); + if (STRICMP(name, "HOME") == 0) { + init_homedir(); + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + // ":let &option = expr": Set option value. + // ":let &l:option = expr": Set local option value. + // ":let &g:option = expr": Set global option value. + } else if (*arg == '&') { + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + // Find the end of the name. + char *const p = (char *)find_option_end((const char **)&arg, &opt_flags); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, *skipwhite(p)) == NULL)) { + emsg(_(e_letunexp)); + } else { + int opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + + const char c1 = *p; + *p = NUL; + + varnumber_T n = tv_get_number(tv); + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + s = tv_get_string_chk(tv); // != NULL if number or string. + } + if (s != NULL && op != NULL && *op != '=') { + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if ((opt_type == 1 && *op == '.') + || (opt_type == 0 && *op != '.')) { + semsg(_(e_letwrong), op); + s = NULL; // don't set the value + } else { + if (opt_type == 1) { // number + switch (*op) { + case '+': + n = numval + n; break; + case '-': + n = numval - n; break; + case '*': + n = numval * n; break; + case '/': + n = num_divide(numval, n); break; + case '%': + n = num_modulus(numval, n); break; + } + } else if (opt_type == 0 && stringval != NULL) { // string + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; + } + } + } + if (s != NULL || tv->v_type == VAR_BOOL + || tv->v_type == VAR_SPECIAL) { + set_option_value((const char *)arg, n, s, opt_flags); + arg_end = p; + } + *p = c1; + xfree(stringval); + } + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + arg++; + if (op != NULL && vim_strchr("+-*/%", *op) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + emsg(_(e_letunexp)); + } else { + char *s; + + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + if (s != NULL) { + ptofree = (char *)concat_str((char_u *)s, (const char_u *)p); + p = (const char *)ptofree; + xfree(s); + } + } + if (p != NULL) { + write_reg_contents(*arg == '@' ? '"' : *arg, + (const char_u *)p, (ssize_t)STRLEN(p), false); + arg_end = arg + 1; + } + xfree(ptofree); + } + // ":let var = expr": Set internal variable. + // ":let {expr} = expr": Idem, name made with curly braces + } else if (eval_isnamec1(*arg) || *arg == '{') { + lval_T lv; + + char *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); + if (p != NULL && lv.ll_name != NULL) { + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { + emsg(_(e_letunexp)); + } else { + set_var_lval(&lv, p, tv, copy, is_const, op); + arg_end = p; + } + } + clear_lval(&lv); + } else { + semsg(_(e_invarg2), arg); + } + + return arg_end; +} + +/// ":unlet[!] var1 ... " command. +void ex_unlet(exarg_T *eap) +{ + ex_unletlock(eap, eap->arg, 0, do_unlet_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// ":lockvar" and ":unlockvar" commands +void ex_lockvar(exarg_T *eap) +{ + char *arg = eap->arg; + int deep = 2; + + if (eap->forceit) { + deep = -1; + } else if (ascii_isdigit(*arg)) { + deep = getdigits_int(&arg, false, -1); + arg = skipwhite(arg); + } + + ex_unletlock(eap, arg, deep, do_lock_var); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Common parsing logic for :unlet, :lockvar and :unlockvar. +/// +/// Invokes `callback` afterwards if successful and `eap->skip == false`. +/// +/// @param[in] eap Ex command arguments for the command. +/// @param[in] argstart Start of the string argument for the command. +/// @param[in] deep Levels to (un)lock for :(un)lockvar, -1 to (un)lock +/// everything. +/// @param[in] callback Appropriate handler for the command. +static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_callback callback) + FUNC_ATTR_NONNULL_ALL +{ + char *arg = argstart; + char *name_end; + bool error = false; + lval_T lv; + + do { + if (*arg == '$') { + lv.ll_name = (const char *)arg; + lv.ll_tv = NULL; + arg++; + if (get_env_len((const char **)&arg) == 0) { + semsg(_(e_invarg2), arg - 1); + return; + } + if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) { + error = true; + } + name_end = arg; + } else { + // Parse the name and find the end. + name_end = get_lval(arg, NULL, &lv, true, eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } + if (name_end == NULL + || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { + if (name_end != NULL) { + emsg_severe = true; + emsg(_(e_trailing)); + } + if (!(eap->skip || error)) { + clear_lval(&lv); + } + break; + } + + if (!error && !eap->skip && callback(&lv, name_end, eap, deep) == FAIL) { + error = true; + } + + if (!eap->skip) { + clear_lval(&lv); + } + } + arg = skipwhite(name_end); + } while (!ends_excmd(*arg)); + + eap->nextcmd = (char *)check_nextcmd((char_u *)arg); +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Unlet a variable indicated by `lp`. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end End of the string argument for the command. +/// @param[in] eap Ex command arguments for :unlet. +/// @param[in] deep Unused. +/// +/// @return OK on success, or FAIL on failure. +static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ATTR_UNUSED) + FUNC_ATTR_NONNULL_ALL +{ + int forceit = eap->forceit; + int ret = OK; + int cc; + + if (lp->ll_tv == NULL) { + cc = (char_u)(*name_end); + *name_end = NUL; + + // Environment variable, normal name or expanded name. + if (*lp->ll_name == '$') { + os_unsetenv(lp->ll_name + 1); + } else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { + ret = FAIL; + } + *name_end = (char)cc; + } else if ((lp->ll_list != NULL + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && var_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) + || (lp->ll_dict != NULL + && var_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { + return FAIL; + } else if (lp->ll_range) { + assert(lp->ll_list != NULL); + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { + return false; + } + lp->ll_li = li; + lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } + } + tv_list_remove_items(lp->ll_list, first_li, last_li); + } else { + if (lp->ll_list != NULL) { + // unlet a List item. + tv_list_item_remove(lp->ll_list, lp->ll_li); + } else { + // unlet a Dictionary item. + dict_T *d = lp->ll_dict; + assert(d != NULL); + dictitem_T *di = lp->ll_di; + bool watched = tv_dict_is_watched(d); + char *key = NULL; + typval_T oldtv; + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + // need to save key because dictitem_remove will free it + key = xstrdup((char *)di->di_key); + } + + tv_dict_item_remove(d, di); + + if (watched) { + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); + xfree(key); + } + } + } + + return ret; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] forceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const bool forceit) + FUNC_ATTR_NONNULL_ALL +{ + const char *varname; + dict_T *dict; + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); + + if (ht != NULL && *varname != NUL) { + dict_T *d = get_current_funccal_dict(ht); + if (d == NULL) { + if (ht == &globvarht) { + d = &globvardict; + } else if (is_compatht(ht)) { + d = &vimvardict; + } else { + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); + d = di->di_tv.vval.v_dict; + } + if (d == NULL) { + internal_error("do_unlet()"); + return FAIL; + } + } + + hashitem_T *hi = hash_find(ht, varname); + if (HASHITEM_EMPTY(hi)) { + hi = find_hi_in_scoped_ht(name, &ht); + } + if (hi != NULL && !HASHITEM_EMPTY(hi)) { + dictitem_T *const di = TV_DICT_HI2DI(hi); + if (var_check_fixed(di->di_flags, name, TV_CSTRING) + || var_check_ro(di->di_flags, name, TV_CSTRING) + || var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { + return FAIL; + } + + typval_T oldtv; + bool watched = tv_dict_is_watched(dict); + + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } + + delete_var(ht, hi); + + if (watched) { + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); + tv_clear(&oldtv); + } + return OK; + } + } + if (forceit) { + return OK; + } + semsg(_("E108: No such variable: \"%s\""), name); + return FAIL; +} + +// TODO(ZyX-I): move to eval/ex_cmds + +/// Lock or unlock variable indicated by `lp`. +/// +/// Locks if `eap->cmdidx == CMD_lockvar`, unlocks otherwise. +/// +/// @param[in] lp The lvalue. +/// @param[in] name_end Unused. +/// @param[in] eap Ex command arguments for :(un)lockvar. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// +/// @return OK on success, or FAIL on failure. +static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap, int deep) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + bool lock = eap->cmdidx == CMD_lockvar; + int ret = OK; + + if (deep == 0) { // Nothing to do. + return OK; + } + + if (lp->ll_tv == NULL) { + if (*lp->ll_name == '$') { + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + // Normal name or expanded name. + dictitem_T *const di = find_var(lp->ll_name, lp->ll_name_len, NULL, + true); + if (di == NULL) { + ret = FAIL; + } else if ((di->di_flags & DI_FLAGS_FIX) + && di->di_tv.v_type != VAR_DICT + && di->di_tv.v_type != VAR_LIST) { + // For historical reasons this error is not given for Lists and + // Dictionaries. E.g. b: dictionary may be locked/unlocked. + semsg(_(e_lock_unlock), lp->ll_name); + ret = FAIL; + } else { + if (lock) { + di->di_flags |= DI_FLAGS_LOCK; + } else { + di->di_flags &= (uint8_t)(~DI_FLAGS_LOCK); + } + tv_item_lock(&di->di_tv, deep, lock, false); + } + } + } else if (lp->ll_range) { + listitem_T *li = lp->ll_li; + + // (un)lock a range of List items. + while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock, false); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; + } + } else if (lp->ll_list != NULL) { + // (un)lock a List item. + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock, false); + } else { + // (un)lock a Dictionary item. + tv_item_lock(&lp->ll_di->di_tv, deep, lock, false); + } + + return ret; +} + +/// Get the value of internal variable "name". +/// Return OK or FAIL. If OK is returned "rettv" must be cleared. +/// +/// @param len length of "name" +/// @param rettv NULL when only checking existence +/// @param dip non-NULL when typval's dict item is needed +/// @param verbose may give error message +/// @param no_autoload do not use script autoloading +int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose, + bool no_autoload) +{ + int ret = OK; + typval_T *tv = NULL; + dictitem_T *v; + + v = find_var(name, (size_t)len, NULL, no_autoload); + if (v != NULL) { + tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } + + if (tv == NULL) { + if (rettv != NULL && verbose) { + semsg(_("E121: Undefined variable: %.*s"), len, name); + } + ret = FAIL; + } else if (rettv != NULL) { + tv_copy(tv, rettv); + } + + return ret; +} + +/// @return the string value of a (global/local) variable or +/// NULL when it doesn't exist. +/// +/// @see tv_get_string() for how long the pointer remains valid. +char_u *get_var_value(const char *const name) +{ + dictitem_T *v; + + v = find_var(name, strlen(name), NULL, false); + if (v == NULL) { + return NULL; + } + return (char_u *)tv_get_string(&v->di_tv); +} + +/// Clean up a list of internal variables. +/// Frees all allocated variables and the value they contain. +/// Clears hashtab "ht", does not free it. +void vars_clear(hashtab_T *ht) +{ + vars_clear_ext(ht, true); +} + +/// Like vars_clear(), but only free the value if "free_val" is TRUE. +void vars_clear_ext(hashtab_T *ht, int free_val) +{ + int todo; + hashitem_T *hi; + dictitem_T *v; + + hash_lock(ht); + todo = (int)ht->ht_used; + for (hi = ht->ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + + // Free the variable. Don't remove it from the hashtab, + // ht_array might change then. hash_clear() takes care of it + // later. + v = TV_DICT_HI2DI(hi); + if (free_val) { + tv_clear(&v->di_tv); + } + if (v->di_flags & DI_FLAGS_ALLOC) { + xfree(v); + } + } + } + hash_clear(ht); + ht->ht_used = 0; +} + +/// Delete a variable from hashtab "ht" at item "hi". +/// Clear the variable value and free the dictitem. +void delete_var(hashtab_T *ht, hashitem_T *hi) +{ + dictitem_T *di = TV_DICT_HI2DI(hi); + + hash_remove(ht, hi); + tv_clear(&di->di_tv); + xfree(di); +} + +/// List the value of one internal variable. +static void list_one_var(dictitem_T *v, const char *prefix, int *first) +{ + char *const s = encode_tv2echo(&v->di_tv, NULL); + list_one_var_a(prefix, (const char *)v->di_key, (ptrdiff_t)STRLEN(v->di_key), + v->di_tv.v_type, (s == NULL ? "" : s), first); + xfree(s); +} + +/// @param[in] name_len Length of the name. May be -1, in this case strlen() +/// will be used. +/// @param[in,out] first When true clear rest of screen and set to false. +static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len, + const VarType type, const char *string, int *first) +{ + // don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" + msg_start(); + msg_puts(prefix); + if (name != NULL) { // "a:" vars don't have a name stored + msg_puts_attr_len(name, name_len, 0); + } + msg_putchar(' '); + msg_advance(22); + if (type == VAR_NUMBER) { + msg_putchar('#'); + } else if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_putchar('*'); + } else if (type == VAR_LIST) { + msg_putchar('['); + if (*string == '[') { + string++; + } + } else if (type == VAR_DICT) { + msg_putchar('{'); + if (*string == '{') { + string++; + } + } else { + msg_putchar(' '); + } + + msg_outtrans((char *)string); + + if (type == VAR_FUNC || type == VAR_PARTIAL) { + msg_puts("()"); + } + if (*first) { + msg_clr_eos(); + *first = false; + } +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +void set_var(const char *name, const size_t name_len, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL +{ + set_var_const(name, name_len, tv, copy, false); +} + +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +/// @param[in] is_const True if value in tv is to be locked. +void set_var_const(const char *name, const size_t name_len, typval_T *const tv, const bool copy, + const bool is_const) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *v; + hashtab_T *ht; + dict_T *dict; + + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); + + if (ht == NULL || *varname == NUL) { + semsg(_(e_illvar), name); + return; + } + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); + + // Search in parent scope which is possible to reference from lambda + if (v == NULL) { + v = find_var_in_scoped_ht(name, name_len, true); + } + + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { + return; + } + + typval_T oldtv = TV_INITIAL_VALUE; + if (v != NULL) { + if (is_const) { + emsg(_(e_cannot_mod)); + return; + } + + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, name_len) + || var_check_lock(v->di_tv.v_lock, name, name_len)) { + return; + } + + // Handle setting internal v: variables separately where needed to + // prevent changing the type. + if (is_vimvarht(ht)) { + if (v->di_tv.v_type == VAR_STRING) { + XFREE_CLEAR(v->di_tv.vval.v_string); + if (copy || tv->v_type != VAR_STRING) { + const char *const val = tv_get_string(tv); + + // Careful: when assigning to v:errmsg and tv_get_string() + // causes an error message the variable will already be set. + if (v->di_tv.vval.v_string == NULL) { + v->di_tv.vval.v_string = xstrdup(val); + } + } else { + // Take over the string to avoid an extra alloc/free. + v->di_tv.vval.v_string = tv->vval.v_string; + tv->vval.v_string = NULL; + } + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { + v->di_tv.vval.v_number = tv_get_number(tv); + if (strcmp(varname, "searchforward") == 0) { + set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); + } else if (strcmp(varname, "hlsearch") == 0) { + no_hlsearch = !v->di_tv.vval.v_number; + redraw_all_later(SOME_VALID); + } + return; + } else if (v->di_tv.v_type != tv->v_type) { + semsg(_("E963: setting %s to value with wrong type"), name); + return; + } + } + + if (watched) { + tv_copy(&v->di_tv, &oldtv); + } + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" or "a:" variable. + if (is_vimvarht(ht) || ht == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + return; + } + + // Make sure the variable name is valid. + if (!valid_varname(varname)) { + return; + } + + // Make sure dict is valid + assert(dict != NULL); + + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); + STRCPY(v->di_key, varname); + if (tv_dict_add(dict, v) == FAIL) { + xfree(v); + return; + } + v->di_flags = DI_FLAGS_ALLOC; + if (is_const) { + v->di_flags |= DI_FLAGS_LOCK; + } + } + + if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { + tv_copy(tv, &v->di_tv); + } else { + v->di_tv = *tv; + v->di_tv.v_lock = VAR_UNLOCKED; + tv_init(tv); + } + + if (watched) { + if (oldtv.v_type == VAR_UNKNOWN) { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + } else { + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_clear(&oldtv); + } + } + + if (is_const) { + // Like :lockvar! name: lock the value and what it contains, but only + // if the reference count is up to one. That locks only literal + // values. + tv_item_lock(&v->di_tv, DICT_MAXNEST, true, true); + } +} + +/// Check whether variable is read-only (DI_FLAGS_RO, DI_FLAGS_RO_SBX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is read-only: either always or in sandbox when +/// sandbox is enabled, false otherwise. +bool var_check_ro(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + const char *error_message = NULL; + if (flags & DI_FLAGS_RO) { + error_message = _(e_readonlyvar); + } else if ((flags & DI_FLAGS_RO_SBX) && sandbox) { + error_message = N_("E794: Cannot set variable in the sandbox: \"%.*s\""); + } + + if (error_message == NULL) { + return false; + } + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + + semsg(_(error_message), (int)name_len, name); + + return true; +} + +/// Check whether variable is fixed (DI_FLAGS_FIX) +/// +/// Also gives an error message. +/// +/// @param[in] flags di_flags attribute value. +/// @param[in] name Variable name, for use in error message. +/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate +/// variable name and compute the length. Use #TV_CSTRING +/// to compute the length with strlen() without +/// translating. +/// +/// Both #TV_… values are used for optimization purposes: +/// variable name with its length is needed only in case +/// of error, when no error occurs computing them is +/// a waste of CPU resources. This especially applies to +/// gettext. +/// +/// @return True if variable is fixed, false otherwise. +bool var_check_fixed(const int flags, const char *name, size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (flags & DI_FLAGS_FIX) { + if (name_len == TV_TRANSLATE) { + name = _(name); + name_len = strlen(name); + } else if (name_len == TV_CSTRING) { + name_len = strlen(name); + } + semsg(_("E795: Cannot delete variable %.*s"), (int)name_len, name); + return true; + } + return false; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Allow for w: b: s: and t:. + if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':') + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { + semsg(_("E704: Funcref variable name must start with a capital: %s"), name); + return false; + } + // Don't allow hiding a function. When "v" is not NULL we might be + // assigning another function to the same var, the type is checked + // below. + if (new_var && function_exists(name, false)) { + semsg(_("E705: Variable name conflicts with existing function: %s"), name); + return false; + } + return true; +} + +// TODO(ZyX-I): move to eval/expressions + +/// Check if a variable name is valid +/// +/// @param[in] varname Variable name to check. +/// +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + semsg(_(e_illvar), varname); + return false; + } + } + return true; +} + +/// getwinvar() and gettabwinvar() +/// +/// @param off 1 for gettabwinvar() +void getwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + win_T *win; + dictitem_T *v; + tabpage_T *tp = NULL; + bool done = false; + + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win = find_win_by_nr(&argvars[off], tp); + const char *varname = tv_get_string_chk(&argvars[off + 1]); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + emsg_off++; + if (win != NULL && varname != NULL) { + // Set curwin to be our win, temporarily. Also set the tabpage, + // otherwise the window is not valid. Only do this when needed, + // autocommands get blocked. + bool need_switch_win = tp != curtab || win != curwin; + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&') { + if (varname[1] == NUL) { + // get all window-local options in a dict + dict_T *opts = get_winbuf_options(false); + + if (opts != NULL) { + tv_dict_set_ret(rettv, opts); + done = true; + } + } else if (get_option_tv(&varname, rettv, 1) == OK) { + // window-local-option + done = true; + } + } else { + // Look up the variable. + // Let getwinvar({nr}, "") return the "w:" dictionary. + v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, + strlen(varname), false); + if (v != NULL) { + tv_copy(&v->di_tv, rettv); + done = true; + } + } + } + + if (need_switch_win) { + // restore previous notion of curwin + restore_win(&switchwin, true); + } + } + emsg_off--; + + if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { + // use the default return value + tv_copy(&argvars[off + 2], rettv); + } +} + +/// "setwinvar()" and "settabwinvar()" functions +void setwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + if (check_secure()) { + return; + } + + tabpage_T *tp = NULL; + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = tv_get_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; + + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = tp != curtab || win != curwin; + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { + if (*varname == '&') { + long numval; + bool error = false; + + varname++; + numval = tv_get_number_chk(varp, &error); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } + } else { + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); + set_var(winvarname, varname_len + 2, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(&switchwin, true); + } + } +} + +bool var_exists(const char *var) + FUNC_ATTR_NONNULL_ALL +{ + char *tofree; + bool n = false; + + // get_name_len() takes care of expanding curly braces + const char *name = var; + const int len = get_name_len(&var, &tofree, true, false); + if (len > 0) { + typval_T tv; + + if (tofree != NULL) { + name = tofree; + } + n = get_var_tv(name, len, &tv, NULL, false, true) == OK; + if (n) { + // Handle d.key, l[idx], f(expr). + n = handle_subscript(&var, &tv, true, false, name, &name) == OK; + if (n) { + tv_clear(&tv); + } + } + } + if (*var != NUL) { + n = false; + } + + xfree(tofree); + return n; +} + +/// "gettabvar()" function +void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + bool done = false; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *const varname = tv_get_string_chk(&argvars[1]); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + if (tp != NULL && varname != NULL) { + // Set tp to be our tabpage, temporarily. Also set the window to the + // first window in the tabpage, otherwise the window is not valid. + win_T *const window = tp == curtab || tp->tp_firstwin == NULL + ? firstwin + : tp->tp_firstwin; + switchwin_T switchwin; + if (switch_win(&switchwin, window, tp, true) == OK) { + // look up the variable + // Let gettabvar({nr}, "") return the "t:" dictionary. + const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', + varname, strlen(varname), + false); + if (v != NULL) { + tv_copy(&v->di_tv, rettv); + done = true; + } + } + + // restore previous notion of curwin + restore_win(&switchwin, true); + } + + if (!done && argvars[2].v_type != VAR_UNKNOWN) { + tv_copy(&argvars[2], rettv); + } +} + +/// "gettabwinvar()" function +void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 1); +} + +/// "getwinvar()" function +void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getwinvar(argvars, rettv, 0); +} + +/// "settabvar()" function +void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = 0; + + if (check_secure()) { + return; + } + + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + const char *const varname = tv_get_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; + + if (varname != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varname_len + 2, varp, true); + xfree(tabvarname); + + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } + } +} + +/// "settabwinvar()" function +void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 1); +} + +/// "setwinvar()" function +void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + setwinvar(argvars, rettv, 0); +} diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h new file mode 100644 index 0000000000..4eea37d404 --- /dev/null +++ b/src/nvim/eval/vars.h @@ -0,0 +1,10 @@ +#ifndef NVIM_EVAL_VARS_H +#define NVIM_EVAL_VARS_H + +#include "nvim/eval/funcs.h" // For FunPtr +#include "nvim/ex_cmds_defs.h" // For exarg_T + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/vars.h.generated.h" +#endif +#endif // NVIM_EVAL_VARS_H -- cgit From a7b9920930f032419439f52ca529abcffc0d15b1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Jul 2022 22:05:02 +0800 Subject: refactor: move FunPtr to types.h (#19466) This type itself is not eval-specific. Moving it to types.h can avoid including eval/funcs.h in many headers, and types.h is already included by many headers. --- src/nvim/eval/funcs.h | 2 -- src/nvim/eval/userfunc.c | 1 + src/nvim/eval/vars.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 5f8d81c989..583ee0e75e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -4,8 +4,6 @@ #include "nvim/buffer_defs.h" #include "nvim/eval/typval.h" -typedef void (*FunPtr)(void); - /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index b01415f052..7283fb3cec 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -9,6 +9,7 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds2.h" diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h index 4eea37d404..73efc4938a 100644 --- a/src/nvim/eval/vars.h +++ b/src/nvim/eval/vars.h @@ -1,7 +1,6 @@ #ifndef NVIM_EVAL_VARS_H #define NVIM_EVAL_VARS_H -#include "nvim/eval/funcs.h" // For FunPtr #include "nvim/ex_cmds_defs.h" // For exarg_T #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 271739c8305ac7eb41a3467e508bdeb5c03f0427 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Jul 2022 21:44:28 +0800 Subject: refactor: move f_getbufvar() and f_setbufvar() to eval/vars.c Vim moved them there in patch 8.1.1943. --- src/nvim/eval/funcs.c | 104 ----------------------------------------------- src/nvim/eval/vars.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 106 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6dcdccf773..5ee499e006 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2804,66 +2804,6 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buffer_lines(buf, lnum, end, true, rettv); } -/// "getbufvar()" function -static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (!tv_check_str_or_nr(&argvars[0])) { - goto f_getbufvar_end; - } - - const char *varname = tv_get_string_chk(&argvars[1]); - emsg_off++; - buf_T *const buf = tv_get_buf(&argvars[0], false); - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { // buffer-local-option - buf_T *const save_curbuf = curbuf; - - // set curbuf to be our buf, temporarily - curbuf = buf; - - if (varname[1] == NUL) { - // get all buffer-local options in a dict - dict_T *opts = get_winbuf_options(true); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, true) == OK) { - // buffer-local-option - done = true; - } - - // restore previous notion of curbuf - curbuf = save_curbuf; - } else { - // Look up the variable. - // Let getbufvar({nr}, "") return the "b:" dictionary. - dictitem_T *const v = *varname == NUL - ? (dictitem_T *)&buf->b_bufvar - : find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', - varname, strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - emsg_off--; - -f_getbufvar_end: - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - // use the default value - tv_copy(&argvars[2], rettv); - } -} - /// "getchangelist()" function static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -8341,50 +8281,6 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setbufvar()" function -static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (check_secure() - || !tv_check_str_or_nr(&argvars[0])) { - return; - } - const char *varname = tv_get_string_chk(&argvars[1]); - buf_T *const buf = tv_get_buf(&argvars[0], false); - typval_T *varp = &argvars[2]; - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { - long numval; - bool error = false; - aco_save_T aco; - - // set curbuf to be our buf, temporarily - aucmd_prepbuf(&aco, buf); - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } - - // reset notion of buffer - aucmd_restbuf(&aco); - } else { - const size_t varname_len = STRLEN(varname); - char *const bufvarname = xmalloc(varname_len + 3); - buf_T *const save_curbuf = curbuf; - curbuf = buf; - memcpy(bufvarname, "b:", 2); - memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varname_len + 2, varp, true); - xfree(bufvarname); - curbuf = save_curbuf; - } - } -} - /// Set the cursor or mark position. /// If 'charpos' is TRUE, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 021d2764a6..01fd10dcda 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -4,10 +4,12 @@ // eval/vars.c: functions for dealing with variables #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" +#include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" @@ -1478,7 +1480,7 @@ bool valid_varname(const char *varname) /// getwinvar() and gettabwinvar() /// /// @param off 1 for gettabwinvar() -void getwinvar(typval_T *argvars, typval_T *rettv, int off) +static void getwinvar(typval_T *argvars, typval_T *rettv, int off) { win_T *win; dictitem_T *v; @@ -1543,7 +1545,7 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) } /// "setwinvar()" and "settabwinvar()" functions -void setwinvar(typval_T *argvars, typval_T *rettv, int off) +static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { if (check_secure()) { return; @@ -1671,6 +1673,66 @@ void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) getwinvar(argvars, rettv, 0); } +/// "getbufvar()" function +void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + bool done = false; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + if (!tv_check_str_or_nr(&argvars[0])) { + goto f_getbufvar_end; + } + + const char *varname = tv_get_string_chk(&argvars[1]); + emsg_off++; + buf_T *const buf = tv_get_buf(&argvars[0], false); + + if (buf != NULL && varname != NULL) { + if (*varname == '&') { // buffer-local-option + buf_T *const save_curbuf = curbuf; + + // set curbuf to be our buf, temporarily + curbuf = buf; + + if (varname[1] == NUL) { + // get all buffer-local options in a dict + dict_T *opts = get_winbuf_options(true); + + if (opts != NULL) { + tv_dict_set_ret(rettv, opts); + done = true; + } + } else if (get_option_tv(&varname, rettv, true) == OK) { + // buffer-local-option + done = true; + } + + // restore previous notion of curbuf + curbuf = save_curbuf; + } else { + // Look up the variable. + // Let getbufvar({nr}, "") return the "b:" dictionary. + dictitem_T *const v = *varname == NUL + ? (dictitem_T *)&buf->b_bufvar + : find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', + varname, strlen(varname), false); + if (v != NULL) { + tv_copy(&v->di_tv, rettv); + done = true; + } + } + } + emsg_off--; + +f_getbufvar_end: + if (!done && argvars[2].v_type != VAR_UNKNOWN) { + // use the default value + tv_copy(&argvars[2], rettv); + } +} + /// "settabvar()" function void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -1713,3 +1775,47 @@ void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { setwinvar(argvars, rettv, 0); } + +/// "setbufvar()" function +void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (check_secure() + || !tv_check_str_or_nr(&argvars[0])) { + return; + } + const char *varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf(&argvars[0], false); + typval_T *varp = &argvars[2]; + + if (buf != NULL && varname != NULL) { + if (*varname == '&') { + long numval; + bool error = false; + aco_save_T aco; + + // set curbuf to be our buf, temporarily + aucmd_prepbuf(&aco, buf); + + varname++; + numval = tv_get_number_chk(varp, &error); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } + + // reset notion of buffer + aucmd_restbuf(&aco); + } else { + const size_t varname_len = STRLEN(varname); + char *const bufvarname = xmalloc(varname_len + 3); + buf_T *const save_curbuf = curbuf; + curbuf = buf; + memcpy(bufvarname, "b:", 2); + memcpy(bufvarname + 2, varname, varname_len + 1); + set_var(bufvarname, varname_len + 2, varp, true); + xfree(bufvarname); + curbuf = save_curbuf; + } + } +} -- cgit From 232cdbb8807c79a5d933f28345011d2a32264f02 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 Jul 2022 21:47:03 +0800 Subject: vim-patch:8.2.1489: Vim9: error when setting an option with setbufvar() Problem: Vim9: error when setting an option with setbufvar(). Solution: Do not get a number from a string value. (closes vim/vim#6740) https://github.com/vim/vim/commit/191929b182ba38abe6bc431fb9d8d9507f408903 Vim9 is N/A, so this just refactors the code without changing behavior. --- src/nvim/eval/vars.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 01fd10dcda..e56ccef02b 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1544,6 +1544,18 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) } } +/// 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) +{ + bool error = false; + char nbuf[NUMBUFLEN]; + const long numval = (long)tv_get_number_chk(varp, &error); + const char *const strval = tv_get_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } +} + /// "setwinvar()" and "settabwinvar()" functions static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { @@ -1566,16 +1578,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) switchwin_T switchwin; if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { - long numval; - bool error = false; - - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } + set_option_from_tv(varname + 1, varp); } else { const size_t varname_len = strlen(varname); char *const winvarname = xmalloc(varname_len + 3); @@ -1789,20 +1792,12 @@ void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buf != NULL && varname != NULL) { if (*varname == '&') { - long numval; - bool error = false; aco_save_T aco; // set curbuf to be our buf, temporarily aucmd_prepbuf(&aco, buf); - varname++; - numval = tv_get_number_chk(varp, &error); - char nbuf[NUMBUFLEN]; - const char *const strval = tv_get_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) { - set_option_value(varname, numval, strval, OPT_LOCAL); - } + set_option_from_tv(varname + 1, varp); // reset notion of buffer aucmd_restbuf(&aco); -- cgit From 91c99eed540a329b7738bd2f28259f8ac0670ef2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 Jul 2022 09:15:31 +0800 Subject: vim-patch:8.2.4731: the changelist index is not remembered per buffer Problem: The changelist index is not remembered per buffer. Solution: Keep the changelist index per window and buffer. (closes vim/vim#10135, closes vim/vim#2173) https://github.com/vim/vim/commit/db0ea7f2b00c84d84f188c9e9953c4f1887528e7 Cherry-pick FOR_ALL_BUF_WININFO from patch 8.2.0500. Cherry-pick test_changelist.vim change from patch 8.2.3795. --- src/nvim/eval/funcs.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5ee499e006..dac6bb6e34 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2824,13 +2824,23 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc(buf->b_changelistlen); tv_list_append_list(rettv->vval.v_list, l); - // The current window change list index tracks only the position in the - // current buffer change list. For other buffers, use the change list - // length as the current index. - tv_list_append_number(rettv->vval.v_list, - (buf == curwin->w_buffer) - ? curwin->w_changelistidx - : buf->b_changelistlen); + // The current window change list index tracks only the position for the + // current buffer. For other buffers use the stored index for the current + // window, or, if that's not available, the change list length. + int changelistindex; + if (buf == curwin->w_buffer) { + changelistindex = curwin->w_changelistidx; + } else { + wininfo_T *wip; + + FOR_ALL_BUF_WININFO(buf, wip) { + if (wip->wi_win == curwin) { + break; + } + } + changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen; + } + tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex); for (int i = 0; i < buf->b_changelistlen; i++) { if (buf->b_changelist[i].mark.lnum == 0) { -- cgit From d529523027c3d3c80c469fa689b0f65ec6f8298e Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 24 Jul 2022 15:02:39 +0100 Subject: vim-patch:8.1.1763: evalfunc.c is still too big (#17952) --- src/nvim/eval/funcs.c | 556 +------------------------------------------- src/nvim/eval/typval.c | 620 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 625 insertions(+), 551 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index dac6bb6e34..7d5404d782 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4200,22 +4200,6 @@ static bool has_wsl(void) return has_wsl == kTrue; } -/// "has_key()" function -static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) { - return; - } - - rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - tv_get_string(&argvars[1]), - -1) != NULL; -} - /// `haslocaldir([{win}[, {tab}]])` function /// /// Returns `1` if the scope object has a local directory, `0` otherwise. If a @@ -4770,12 +4754,6 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); } -/// "items(dict)" function -static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 2); -} - /// "jobpid(id)" function static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5208,30 +5186,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = rv; } -/// "join()" function -static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - const char *const sep = (argvars[1].v_type == VAR_UNKNOWN - ? " " - : tv_get_string_chk(&argvars[1])); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) { - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, sep); - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; - } else { - rettv->vval.v_string = NULL; - } -} - /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5273,12 +5227,6 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = encode_tv2json(&argvars[0], NULL); } -/// "keys()" function -static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 0); -} - /// "last_buffer_nr()" function. static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -5435,35 +5383,6 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "list2str()" function -static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - garray_T ga; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_invarg)); - return; - } - - list_T *const l = argvars[0].vval.v_list; - if (l == NULL) { - return; // empty list results in empty string - } - - ga_init(&ga, 1, 80); - char buf[MB_MAXBYTES + 1]; - - TV_LIST_ITER_CONST(l, li, { - buf[utf_char2bytes(tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; - ga_concat(&ga, (char *)buf); - }); - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -} - /// "localtime()" function static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -6849,134 +6768,16 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "remove()" function static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - dict_T *d; - dictitem_T *di; const char *const arg_errmsg = N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) { - semsg(_(e_toomanyarg), "remove()"); - } else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { - const char *key = tv_get_string_chk(&argvars[1]); - if (key != NULL) { - di = tv_dict_find(d, key, -1); - if (di == NULL) { - semsg(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) - && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { - *rettv = di->di_tv; - di->di_tv = TV_INITIAL_VALUE; - tv_dict_item_remove(d, di); - if (tv_dict_is_watched(d)) { - tv_dict_watcher_notify(d, key, NULL, rettv); - } - } - } - } + tv_dict_remove(argvars, rettv, arg_errmsg); } else if (argvars[0].v_type == VAR_BLOB) { - blob_T *const b = argvars[0].vval.v_blob; - - if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { - return; - } - - bool error = false; - idx = (long)tv_get_number_chk(&argvars[1], &error); - - if (!error) { - const int len = tv_blob_len(b); - - if (idx < 0) { - // count from the end - idx = len + idx; - } - if (idx < 0 || idx >= len) { - semsg(_(e_blobidx), (int64_t)idx); - return; - } - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - char_u *const p = (char_u *)b->bv_ga.ga_data; - rettv->vval.v_number = (varnumber_T)(*(p + idx)); - memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); - b->bv_ga.ga_len--; - } else { - // Remove range of items, return blob with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; - } - if (end < 0) { - // count from the end - end = len + end; - } - if (end >= len || idx > end) { - semsg(_(e_blobidx), (int64_t)end); - return; - } - blob_T *const blob = tv_blob_alloc(); - blob->bv_ga.ga_len = end - idx + 1; - ga_grow(&blob->bv_ga, end - idx + 1); - - char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); - tv_blob_set_ret(rettv, blob); - - if (len - end - 1 > 0) { - memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); - } - b->bv_ga.ga_len -= end - idx + 1; - } - } - } else if (argvars[0].v_type != VAR_LIST) { + tv_blob_remove(argvars, rettv, arg_errmsg); + } else if (argvars[0].v_type == VAR_LIST) { + tv_list_remove(argvars, rettv, arg_errmsg); + } else { semsg(_(e_listdictblobarg), "remove()"); - } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { - bool error = false; - - idx = tv_get_number_chk(&argvars[1], &error); - if (error) { - // Type error: do nothing, errmsg already given. - } else if ((item = tv_list_find(l, idx)) == NULL) { - semsg(_(e_listidx), (int64_t)idx); - } else { - if (argvars[2].v_type == VAR_UNKNOWN) { - // Remove one item, return its value. - tv_list_drop_items(l, item, item); - *rettv = *TV_LIST_ITEM_TV(item); - xfree(item); - } else { - // Remove range of items, return list with values. - end = tv_get_number_chk(&argvars[2], &error); - if (error) { - // Type error: do nothing. - } else if ((item2 = tv_list_find(l, end)) == NULL) { - semsg(_(e_listidx), (int64_t)end); - } else { - int cnt = 0; - - for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - cnt++; - if (li == item2) { - break; - } - } - if (li == NULL) { // Didn't find "item2" after "item". - emsg(_(e_invrange)); - } else { - tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), - cnt); - } - } - } - } } } @@ -8867,341 +8668,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct storing information about current sort -typedef struct { - int item_compare_ic; - bool item_compare_lc; - bool item_compare_numeric; - bool item_compare_numbers; - bool item_compare_float; - const char *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - bool item_compare_func_err; -} sortinfo_T; -static sortinfo_T *sortinfo = NULL; - -#define ITEM_COMPARE_FAIL 999 - -/// Compare functions for f_sort() and f_uniq() below. -static int item_compare(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *const si1 = (ListSortItem *)s1; - ListSortItem *const si2 = (ListSortItem *)s2; - - typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); - typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); - - int res; - - if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - if (sortinfo->item_compare_float) { - const float_T v1 = tv_get_float(tv1); - const float_T v2 = tv_get_float(tv2); - - res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - goto item_compare_end; - } - - char *tofree1 = NULL; - char *tofree2 = NULL; - char *p1; - char *p2; - - // encode_tv2string() puts quotes around a string and allocates memory. Don't - // do that for string variables. Use a single quote when comparing with - // a non-string to do what the docs promise. - if (tv1->v_type == VAR_STRING) { - if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = "'"; - } else { - p1 = tv1->vval.v_string; - } - } else { - tofree1 = p1 = encode_tv2string(tv1, NULL); - } - if (tv2->v_type == VAR_STRING) { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = "'"; - } else { - p2 = tv2->vval.v_string; - } - } else { - tofree2 = p2 = encode_tv2string(tv2, NULL); - } - if (p1 == NULL) { - p1 = ""; - } - if (p2 == NULL) { - p2 = ""; - } - if (!sortinfo->item_compare_numeric) { - if (sortinfo->item_compare_lc) { - res = strcoll(p1, p2); - } else { - res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); - } - } else { - double n1, n2; - n1 = strtod(p1, &p1); - n2 = strtod(p2, &p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - xfree(tofree1); - xfree(tofree2); - -item_compare_end: - // When the result would be zero, compare the item indexes. Makes the - // sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - return res; -} - -static int item_compare_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, true); -} - -static int item_compare_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare(s1, s2, false); -} - -static int item_compare2(const void *s1, const void *s2, bool keep_zero) -{ - ListSortItem *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - const char *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - // shortcut after failure in previous call; compare all items equal - if (sortinfo->item_compare_func_err) { - return 0; - } - - si1 = (ListSortItem *)s1; - si2 = (ListSortItem *)s2; - - if (partial == NULL) { - func_name = sortinfo->item_compare_func; - } else { - func_name = (const char *)partial_name(partial); - } - - // Copy the values. This is needed to be able to set v_lock to VAR_FIXED - // in the copy without changing the original list items. - tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); - tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); - - rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = sortinfo->item_compare_selfdict; - res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - tv_clear(&argv[1]); - - if (res == FAIL) { - res = ITEM_COMPARE_FAIL; - } else { - res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); - if (res > 0) { - res = 1; - } else if (res < 0) { - res = -1; - } - } - if (sortinfo->item_compare_func_err) { - res = ITEM_COMPARE_FAIL; // return value has wrong type - } - tv_clear(&rettv); - - // When the result would be zero, compare the pointers themselves. Makes - // the sort stable. - if (res == 0 && !keep_zero) { - // WARNING: When using uniq si1 and si2 are actually listitem_T **, no - // indexes are there. - res = si1->idx > si2->idx ? 1 : -1; - } - - return res; -} - -static int item_compare2_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, true); -} - -static int item_compare2_not_keeping_zero(const void *s1, const void *s2) -{ - return item_compare2(s1, s2, false); -} - -/// "sort({list})" function -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) -{ - ListSortItem *ptrs; - long len; - long i; - - // Pointer to current info struct used in compare function. Save and restore - // the current one for nested calls. - sortinfo_T info; - sortinfo_T *old_sortinfo = sortinfo; - sortinfo = &info; - - const char *const arg_errmsg = (sort - ? N_("sort() argument") - : N_("uniq() argument")); - - if (argvars[0].v_type != VAR_LIST) { - semsg(_(e_listarg), sort ? "sort()" : "uniq()"); - } else { - list_T *const l = argvars[0].vval.v_list; - if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { - goto theend; - } - tv_list_set_ret(rettv, l); - - len = tv_list_len(l); - if (len <= 1) { - goto theend; // short list sorts pretty quickly - } - - info.item_compare_ic = false; - info.item_compare_lc = false; - info.item_compare_numeric = false; - info.item_compare_numbers = false; - info.item_compare_float = false; - info.item_compare_func = NULL; - info.item_compare_partial = NULL; - info.item_compare_selfdict = NULL; - - if (argvars[1].v_type != VAR_UNKNOWN) { - // optional second argument: {func} - if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = (const char *)argvars[1].vval.v_string; - } else if (argvars[1].v_type == VAR_PARTIAL) { - info.item_compare_partial = argvars[1].vval.v_partial; - } else { - bool error = false; - - i = tv_get_number_chk(&argvars[1], &error); - if (error) { - goto theend; // type error; errmsg already given - } - if (i == 1) { - info.item_compare_ic = true; - } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = tv_get_string(&argvars[1]); - } else if (i != 0) { - emsg(_(e_invarg)); - goto theend; - } - if (info.item_compare_func != NULL) { - if (*info.item_compare_func == NUL) { - // empty string means default sort - info.item_compare_func = NULL; - } else if (strcmp(info.item_compare_func, "n") == 0) { - info.item_compare_func = NULL; - info.item_compare_numeric = true; - } else if (strcmp(info.item_compare_func, "N") == 0) { - info.item_compare_func = NULL; - info.item_compare_numbers = true; - } else if (strcmp(info.item_compare_func, "f") == 0) { - info.item_compare_func = NULL; - info.item_compare_float = true; - } else if (strcmp(info.item_compare_func, "i") == 0) { - info.item_compare_func = NULL; - info.item_compare_ic = true; - } else if (strcmp(info.item_compare_func, "l") == 0) { - info.item_compare_func = NULL; - info.item_compare_lc = true; - } - } - } - - if (argvars[2].v_type != VAR_UNKNOWN) { - // optional third argument: {dict} - if (argvars[2].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - // Make an array with each entry pointing to an item in the List. - ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - - if (sort) { - info.item_compare_func_err = false; - tv_list_item_sort(l, ptrs, - ((info.item_compare_func == NULL - && info.item_compare_partial == NULL) - ? item_compare_not_keeping_zero - : item_compare2_not_keeping_zero), - &info.item_compare_func_err); - if (info.item_compare_func_err) { - emsg(_("E702: Sort compare function failed")); - } - } else { - ListSorter item_compare_func_ptr; - - // f_uniq(): ptrs will be a stack of items to remove. - info.item_compare_func_err = false; - if (info.item_compare_func != NULL - || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; - } else { - item_compare_func_ptr = item_compare_keeping_zero; - } - - int idx = 0; - for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) - ; li != NULL;) { - listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); - if (item_compare_func_ptr(&prev_li, &li) == 0) { - if (info.item_compare_func_err) { // -V547 - emsg(_("E882: Uniq compare function failed")); - break; - } - li = tv_list_item_remove(l, li); - } else { - idx++; - li = TV_LIST_ITEM_NEXT(l, li); - } - } - } - - xfree(ptrs); - } - -theend: - sortinfo = old_sortinfo; -} - -/// "sort"({list})" function -static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, true); -} - /// "stdioopen()" function static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9237,12 +8703,6 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// "uniq({list})" function -static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - do_sort_uniq(argvars, rettv, false); -} - /// "reltimefloat()" function static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL @@ -10801,12 +10261,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } -/// "values(dict)" function -static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_list(argvars, rettv, 1); -} - /// "virtcol(string)" function static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 63458e7e69..fd57b45e86 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -830,6 +830,454 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) return retval; } +/// "join()" function +void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) { + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + tv_list_join(&ga, argvars[0].vval.v_list, sep); + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; + } else { + rettv->vval.v_string = NULL; + } +} + +/// "list2str()" function +void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + garray_T ga; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_invarg)); + return; + } + + list_T *const l = argvars[0].vval.v_list; + if (l == NULL) { + return; // empty list results in empty string + } + + ga_init(&ga, 1, 80); + char buf[MB_MAXBYTES + 1]; + + TV_LIST_ITER_CONST(l, li, { + buf[utf_char2bytes((int)tv_get_number(TV_LIST_ITEM_TV(li)), (char *)buf)] = NUL; + ga_concat(&ga, (char *)buf); + }); + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; +} + +/// "remove({list})" function +void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + list_T *l; + bool error = false; + + if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { + return; + } + + long idx = tv_get_number_chk(&argvars[1], &error); + + listitem_T *item; + + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, (int)idx)) == NULL) { + semsg(_(e_listidx), (int64_t)idx); + } else { + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + tv_list_drop_items(l, item, item); + *rettv = *TV_LIST_ITEM_TV(item); + xfree(item); + } else { + listitem_T *item2; + // Remove range of items, return list with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, (int)end)) == NULL) { + semsg(_(e_listidx), (int64_t)end); + } else { + int cnt = 0; + + listitem_T *li; + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { + break; + } + } + if (li == NULL) { // Didn't find "item2" after "item". + emsg(_(e_invrange)); + } else { + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); + } + } + } + } +} + +/// struct storing information about current sort +typedef struct { + int item_compare_ic; + bool item_compare_lc; + bool item_compare_numeric; + bool item_compare_numbers; + bool item_compare_float; + const char *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + bool item_compare_func_err; +} sortinfo_T; +static sortinfo_T *sortinfo = NULL; + +#define ITEM_COMPARE_FAIL 999 + +/// Compare functions for f_sort() and f_uniq() below. +static int item_compare(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; + + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); + + int res; + + if (sortinfo->item_compare_numbers) { + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + if (sortinfo->item_compare_float) { + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); + + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; + } + + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + + // encode_tv2string() puts quotes around a string and allocates memory. Don't + // do that for string variables. Use a single quote when comparing with + // a non-string to do what the docs promise. + if (tv1->v_type == VAR_STRING) { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p1 = "'"; + } else { + p1 = tv1->vval.v_string; + } + } else { + tofree1 = p1 = encode_tv2string(tv1, NULL); + } + if (tv2->v_type == VAR_STRING) { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { + p2 = "'"; + } else { + p2 = tv2->vval.v_string; + } + } else { + tofree2 = p2 = encode_tv2string(tv2, NULL); + } + if (p1 == NULL) { + p1 = ""; + } + if (p2 == NULL) { + p2 = ""; + } + if (!sortinfo->item_compare_numeric) { + if (sortinfo->item_compare_lc) { + res = strcoll(p1, p2); + } else { + res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); + } + } else { + double n1, n2; + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + xfree(tofree1); + xfree(tofree2); + +item_compare_end: + // When the result would be zero, compare the item indexes. Makes the + // sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + return res; +} + +static int item_compare_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, true); +} + +static int item_compare_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare(s1, s2, false); +} + +static int item_compare2(const void *s1, const void *s2, bool keep_zero) +{ + ListSortItem *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + const char *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + // shortcut after failure in previous call; compare all items equal + if (sortinfo->item_compare_func_err) { + return 0; + } + + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; + + if (partial == NULL) { + func_name = sortinfo->item_compare_func; + } else { + func_name = (const char *)partial_name(partial); + } + + // Copy the values. This is needed to be able to set v_lock to VAR_FIXED + // in the copy without changing the original list items. + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); + + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + tv_clear(&argv[1]); + + if (res == FAIL) { + res = ITEM_COMPARE_FAIL; + } else { + res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (res > 0) { + res = 1; + } else if (res < 0) { + res = -1; + } + } + if (sortinfo->item_compare_func_err) { + res = ITEM_COMPARE_FAIL; // return value has wrong type + } + tv_clear(&rettv); + + // When the result would be zero, compare the pointers themselves. Makes + // the sort stable. + if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. + res = si1->idx > si2->idx ? 1 : -1; + } + + return res; +} + +static int item_compare2_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, true); +} + +static int item_compare2_not_keeping_zero(const void *s1, const void *s2) +{ + return item_compare2(s1, s2, false); +} + +/// "sort({list})" function +static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) +{ + ListSortItem *ptrs; + long len; + long i; + + // Pointer to current info struct used in compare function. Save and restore + // the current one for nested calls. + sortinfo_T info; + sortinfo_T *old_sortinfo = sortinfo; + sortinfo = &info; + + const char *const arg_errmsg = (sort + ? N_("sort() argument") + : N_("uniq() argument")); + + if (argvars[0].v_type != VAR_LIST) { + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + } else { + list_T *const l = argvars[0].vval.v_list; + if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + goto theend; + } + tv_list_set_ret(rettv, l); + + len = tv_list_len(l); + if (len <= 1) { + goto theend; // short list sorts pretty quickly + } + + info.item_compare_ic = false; + info.item_compare_lc = false; + info.item_compare_numeric = false; + info.item_compare_numbers = false; + info.item_compare_float = false; + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + + if (argvars[1].v_type != VAR_UNKNOWN) { + // optional second argument: {func} + if (argvars[1].v_type == VAR_FUNC) { + info.item_compare_func = (const char *)argvars[1].vval.v_string; + } else if (argvars[1].v_type == VAR_PARTIAL) { + info.item_compare_partial = argvars[1].vval.v_partial; + } else { + bool error = false; + + i = tv_get_number_chk(&argvars[1], &error); + if (error) { + goto theend; // type error; errmsg already given + } + if (i == 1) { + info.item_compare_ic = true; + } else if (argvars[1].v_type != VAR_NUMBER) { + info.item_compare_func = tv_get_string(&argvars[1]); + } else if (i != 0) { + emsg(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) { + if (*info.item_compare_func == NUL) { + // empty string means default sort + info.item_compare_func = NULL; + } else if (strcmp(info.item_compare_func, "n") == 0) { + info.item_compare_func = NULL; + info.item_compare_numeric = true; + } else if (strcmp(info.item_compare_func, "N") == 0) { + info.item_compare_func = NULL; + info.item_compare_numbers = true; + } else if (strcmp(info.item_compare_func, "f") == 0) { + info.item_compare_func = NULL; + info.item_compare_float = true; + } else if (strcmp(info.item_compare_func, "i") == 0) { + info.item_compare_func = NULL; + info.item_compare_ic = true; + } else if (strcmp(info.item_compare_func, "l") == 0) { + info.item_compare_func = NULL; + info.item_compare_lc = true; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) { + // optional third argument: {dict} + if (argvars[2].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + // Make an array with each entry pointing to an item in the List. + ptrs = xmalloc((size_t)((unsigned)len * sizeof(ListSortItem))); + + if (sort) { + info.item_compare_func_err = false; + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { + emsg(_("E702: Sort compare function failed")); + } + } else { + ListSorter item_compare_func_ptr; + + // f_uniq(): ptrs will be a stack of items to remove. + info.item_compare_func_err = false; + if (info.item_compare_func != NULL + || info.item_compare_partial != NULL) { + item_compare_func_ptr = item_compare2_keeping_zero; + } else { + item_compare_func_ptr = item_compare_keeping_zero; + } + + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL;) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { + if (info.item_compare_func_err) { // -V547 + emsg(_("E882: Uniq compare function failed")); + break; + } + li = tv_list_item_remove(l, li); + } else { + idx++; + li = TV_LIST_ITEM_NEXT(l, li); + } + } + } + + xfree(ptrs); + } + +theend: + sortinfo = old_sortinfo; +} + +/// "sort"({list})" function +void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, true); +} + +/// "uniq({list})" function +void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_sort_uniq(argvars, rettv, false); +} + /// Check whether two lists are equal /// /// @param[in] l1 First list to compare. @@ -2200,6 +2648,66 @@ bool tv_blob_equal(const blob_T *const b1, const blob_T *const b2) return true; } +/// "remove({blob})" function +void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + blob_T *const b = argvars[0].vval.v_blob; + + if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + bool error = false; + long idx = tv_get_number_chk(&argvars[1], &error); + + if (!error) { + const int len = tv_blob_len(b); + + if (idx < 0) { + // count from the end + idx = len + idx; + } + if (idx < 0 || idx >= len) { + semsg(_(e_blobidx), (int64_t)idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) { + // Remove one item, return its value. + char_u *const p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T)(*(p + idx)); + memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1)); + b->bv_ga.ga_len--; + } else { + // Remove range of items, return blob with values. + long end = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (end < 0) { + // count from the end + end = len + end; + } + if (end >= len || idx > end) { + semsg(_(e_blobidx), (int64_t)end); + return; + } + blob_T *const blob = tv_blob_alloc(); + blob->bv_ga.ga_len = (int)(end - idx + 1); + ga_grow(&blob->bv_ga, (int)(end - idx + 1)); + + char_u *const p = (char_u *)b->bv_ga.ga_data; + memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + tv_blob_set_ret(rettv, blob); + + if (len - end - 1 > 0) { + memmove(p + idx, p + end + 1, (size_t)(len - end - 1)); + } + b->bv_ga.ga_len -= (int)(end - idx + 1); + } + } +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -2244,6 +2752,118 @@ void tv_dict_alloc_ret(typval_T *const ret_tv) tv_dict_set_ret(ret_tv, d); } +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (tv->vval.v_dict == NULL) { + return; + } + + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); + + TV_DICT_ITER(tv->vval.v_dict, di, { + typval_T tv_item = { .v_lock = VAR_UNLOCKED }; + + switch (what) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = (char *)vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); + + break; + } + } + + tv_list_append_owned_tv(rettv->vval.v_list, tv_item); + }); +} + +/// "items(dict)" function +void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 2); +} + +/// "keys()" function +void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 0); +} + +/// "values(dict)" function +void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_list(argvars, rettv, 1); +} + +/// "has_key()" function +void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) { + return; + } + + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), + -1) != NULL; +} + +/// "remove({dict})" function +void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) +{ + dict_T *d; + if (argvars[2].v_type != VAR_UNKNOWN) { + semsg(_(e_toomanyarg), "remove()"); + } else if ((d = argvars[0].vval.v_dict) != NULL + && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + const char *key = tv_get_string_chk(&argvars[1]); + if (key != NULL) { + dictitem_T *di = tv_dict_find(d, key, -1); + if (di == NULL) { + semsg(_(e_dictkey), key); + } else if (!var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + && !var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + *rettv = di->di_tv; + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); + } + } + } + } +} + /// Allocate an empty blob for a return value. /// /// Also sets reference count. -- cgit From 3ea45a2caf23ac1c335d04c0a3b2b2aa254d3d96 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 16:56:11 +0800 Subject: vim-patch:8.2.1469: Vim9: cannot assign string to string option Problem: Vim9: cannot assign string to string option. Solution: Change checks for option value. (closes vim/vim#6720) https://github.com/vim/vim/commit/0aae4809fd52b445531766411a9c963dc6274a04 --- src/nvim/eval/vars.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e56ccef02b..edc112109a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -619,24 +619,32 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo && vim_strchr(endchars, *skipwhite(p)) == NULL)) { emsg(_(e_letunexp)); } else { + varnumber_T n = 0; int opt_type; long numval; char *stringval = NULL; const char *s = NULL; + bool failed = false; const char c1 = *p; *p = NUL; - varnumber_T n = tv_get_number(tv); + opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + if (opt_type == 1 || opt_type == -1) { + // number, possibly hidden + n = (long)tv_get_number(tv); + } + + // Avoid setting a string option to the text "v:false" or similar. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - s = tv_get_string_chk(tv); // != NULL if number or string. + s = tv_get_string_chk(tv); } - if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + + if (op != NULL && *op != '=') { if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { semsg(_(e_letwrong), op); - s = NULL; // don't set the value + failed = true; // don't set the value } else { if (opt_type == 1) { // number switch (*op) { @@ -651,7 +659,8 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo case '%': n = num_modulus(numval, n); break; } - } else if (opt_type == 0 && stringval != NULL) { // string + } else if (opt_type == 0 && stringval != NULL && s != NULL) { + // string char *const oldstringval = stringval; stringval = (char *)concat_str((const char_u *)stringval, (const char_u *)s); @@ -660,10 +669,14 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo } } } - if (s != NULL || tv->v_type == VAR_BOOL - || tv->v_type == VAR_SPECIAL) { - set_option_value((const char *)arg, n, s, opt_flags); - arg_end = p; + + if (!failed) { + if (opt_type != 0 || s != NULL) { + set_option_value(arg, n, s, opt_flags); + arg_end = p; + } else { + emsg(_(e_stringreq)); + } } *p = c1; xfree(stringval); -- cgit From 2241fd3211012e5eba3479d64e190f206c12087e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 17:37:06 +0800 Subject: vim-patch:8.2.2254: Vim9: bool option type is number Problem: Vim9: bool option type is number. Solution: Have get_option_value() return a different value for bool and number options. (closes vim/vim#7583) https://github.com/vim/vim/commit/dd1f426bd617ac6a775f2e7795ff0b159e3fa315 --- src/nvim/eval/vars.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index edc112109a..091e3f87c4 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -620,7 +620,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo emsg(_(e_letunexp)); } else { varnumber_T n = 0; - int opt_type; + getoption_T opt_type; long numval; char *stringval = NULL; const char *s = NULL; @@ -630,7 +630,10 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo *p = NUL; opt_type = get_option_value(arg, &numval, &stringval, opt_flags); - if (opt_type == 1 || opt_type == -1) { + if (opt_type == gov_bool + || opt_type == gov_number + || opt_type == gov_hidden_bool + || opt_type == gov_hidden_number) { // number, possibly hidden n = (long)tv_get_number(tv); } @@ -641,12 +644,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo } if (op != NULL && *op != '=') { - if ((opt_type == 1 && *op == '.') - || (opt_type == 0 && *op != '.')) { + if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') + || (opt_type == gov_string && *op != '.')) { semsg(_(e_letwrong), op); failed = true; // don't set the value } else { - if (opt_type == 1) { // number + // number or bool + if (opt_type == gov_number || opt_type == gov_bool) { switch (*op) { case '+': n = numval + n; break; @@ -659,7 +663,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo case '%': n = num_modulus(numval, n); break; } - } else if (opt_type == 0 && stringval != NULL && s != NULL) { + } else if (opt_type == gov_string && stringval != NULL && s != NULL) { // string char *const oldstringval = stringval; stringval = (char *)concat_str((const char_u *)stringval, @@ -671,7 +675,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo } if (!failed) { - if (opt_type != 0 || s != NULL) { + if (opt_type != gov_string || s != NULL) { set_option_value(arg, n, s, opt_flags); arg_end = p; } else { -- cgit From 8921035fc762d39f42f1b644a19a78d36fbda87a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 17:17:20 +0800 Subject: vim-patch:8.2.2284: Vim9: cannot set an option to a boolean value Problem: Vim9: cannot set an option to a boolean value. Solution: Check for VAR_BOOL. (closes vim/vim#7603) https://github.com/vim/vim/commit/31a201a04aa95708af5d62070d2d397a201cc1a5 --- src/nvim/eval/vars.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 091e3f87c4..40a3707060 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1564,10 +1564,17 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// 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) { + long numval = 0; + const char *strval; bool error = false; char nbuf[NUMBUFLEN]; - const long numval = (long)tv_get_number_chk(varp, &error); - const char *const strval = tv_get_string_buf_chk(varp, nbuf); + + if (varp->v_type == VAR_BOOL) { + numval = (long)varp->vval.v_number; + } else { + numval = (long)tv_get_number_chk(varp, &error); + } + strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } -- cgit From 963ea726daf3e19279f8e24b48116f11f1921c7d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 17:20:23 +0800 Subject: vim-patch:8.2.2285: Vim9: cannot set an option to a false Problem: Vim9: cannot set an option to a false. Solution: For VAR_BOOL use string "0". (closes vim/vim#7603) https://github.com/vim/vim/commit/b0d8182fa39f2c9403f5f9a0663589fcab43a6c8 --- src/nvim/eval/vars.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 40a3707060..a0141402bc 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1571,10 +1571,11 @@ static void set_option_from_tv(const char *varname, typval_T *varp) if (varp->v_type == VAR_BOOL) { numval = (long)varp->vval.v_number; + strval = "0"; // avoid using "false" } else { numval = (long)tv_get_number_chk(varp, &error); + strval = tv_get_string_buf_chk(varp, nbuf); } - strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } -- cgit From d8df9afad62a9a4beb4ec607f8e10d674de66dbe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 18:11:37 +0800 Subject: vim-patch:8.2.2969: subtracting from number option fails when result is zero Problem: Subtracting from number option fails when result is zero. (Ingo Karkat) Solution: Reset the string value when using the numeric value. (closes vim/vim#8351) https://github.com/vim/vim/commit/a42e6e0082a6d564dbfa55317d4a698ac12ae898 Cherry-pick Test_compound_assignment_operators() changes from patch 8.2.1593 --- src/nvim/eval/vars.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index a0141402bc..152d1c3e00 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -663,6 +663,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo case '%': n = num_modulus(numval, n); break; } + s = NULL; } else if (opt_type == gov_string && stringval != NULL && s != NULL) { // string char *const oldstringval = stringval; -- cgit From 56ed5a040303ecb30aa50c8bde545e5e47862271 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 Jul 2022 05:51:55 +0800 Subject: vim-patch:8.2.4866: duplicate code in "get" functions Problem: Duplicate code in "get" functions. Solution: Use get_var_from() for getwinvar(), gettabvar(), gettabwinvar() and getbufvar(). (closes vim/vim#10335) https://github.com/vim/vim/commit/47d4e317f85e4aeb3799d962f173bd0f1e7bc71c f_setbufvar() can use tv_get_buf_from_arg() as it sets emsg_off. --- src/nvim/eval/vars.c | 188 ++++++++++++++++++++++----------------------------- 1 file changed, 79 insertions(+), 109 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 152d1c3e00..a0e4bfd6a3 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1495,53 +1495,79 @@ bool valid_varname(const char *varname) return true; } -/// getwinvar() and gettabwinvar() +/// Implements the logic to retrieve local variable and option values. +/// Used by "getwinvar()" "gettabvar()" "gettabwinvar()" "getbufvar()". /// -/// @param off 1 for gettabwinvar() -static void getwinvar(typval_T *argvars, typval_T *rettv, int off) +/// @param deftv default value if not found +/// @param htname 't'ab, 'w'indow or 'b'uffer local +/// @param tp can be NULL +/// @param buf ignored if htname is not 'b' +static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, int htname, + tabpage_T *tp, win_T *win, buf_T *buf) { - win_T *win; - dictitem_T *v; - tabpage_T *tp = NULL; bool done = false; - if (off == 1) { - tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - } else { - tp = curtab; - } - win = find_win_by_nr(&argvars[off], tp); - const char *varname = tv_get_string_chk(&argvars[off + 1]); + emsg_off++; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - emsg_off++; - if (win != NULL && varname != NULL) { + if (varname != NULL && tp != NULL && win != NULL && (htname != 'b' || buf != NULL)) { // Set curwin to be our win, temporarily. Also set the tabpage, // otherwise the window is not valid. Only do this when needed, // autocommands get blocked. - bool need_switch_win = tp != curtab || win != curwin; + // If we have a buffer reference avoid the switching, we're saving and + // restoring curbuf directly. + const bool need_switch_win = !(tp == curtab && win == curwin) || (buf != NULL); switchwin_T switchwin; if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { - if (*varname == '&') { + if (*varname == '&' && htname != 't') { + buf_T *const save_curbuf = curbuf; + + // Change curbuf so the option is read from the correct buffer. + if (buf != NULL && htname == 'b') { + curbuf = buf; + } + if (varname[1] == NUL) { // get all window-local options in a dict - dict_T *opts = get_winbuf_options(false); + dict_T *opts = get_winbuf_options(htname == 'b'); if (opts != NULL) { tv_dict_set_ret(rettv, opts); done = true; } - } else if (get_option_tv(&varname, rettv, 1) == OK) { - // window-local-option + } else if (get_option_tv(&varname, rettv, true) == OK) { + // Local option done = true; } + + curbuf = save_curbuf; + } else if (*varname == NUL) { + const ScopeDictDictItem *v; + // Empty string: return a dict with all the local variables. + if (htname == 'b') { + v = &buf->b_bufvar; + } else if (htname == 'w') { + v = &win->w_winvar; + } else { + v = &tp->tp_winvar; + } + tv_copy(&v->di_tv, rettv); + done = true; } else { + hashtab_T *ht; + + if (htname == 'b') { + ht = &buf->b_vars->dv_hashtab; + } else if (htname == 'w') { + ht = &win->w_vars->dv_hashtab; + } else { + ht = &tp->tp_vars->dv_hashtab; + } + // Look up the variable. - // Let getwinvar({nr}, "") return the "w:" dictionary. - v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, - strlen(varname), false); + const dictitem_T *const v = find_var_in_ht(ht, htname, varname, strlen(varname), false); if (v != NULL) { tv_copy(&v->di_tv, rettv); done = true; @@ -1554,12 +1580,31 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) restore_win(&switchwin, true); } } + + if (!done && deftv->v_type != VAR_UNKNOWN) { + // use the default value + tv_copy(deftv, rettv); + } + emsg_off--; +} + +/// getwinvar() and gettabwinvar() +/// +/// @param off 1 for gettabwinvar() +static void getwinvar(typval_T *argvars, typval_T *rettv, int off) +{ + tabpage_T *tp; - if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { - // use the default return value - tv_copy(&argvars[off + 2], rettv); + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { + tp = curtab; } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *const varname = tv_get_string_chk(&argvars[off + 1]); + + get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL); } /// Set option "varname" to the value of "varp" for the current buffer/window. @@ -1600,7 +1645,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { - bool need_switch_win = tp != curtab || win != curwin; + bool need_switch_win = !(tp == curtab && win == curwin); switchwin_T switchwin; if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { @@ -1655,39 +1700,15 @@ bool var_exists(const char *var) /// "gettabvar()" function void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - const char *const varname = tv_get_string_chk(&argvars[1]); tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) { - // Set tp to be our tabpage, temporarily. Also set the window to the - // first window in the tabpage, otherwise the window is not valid. - win_T *const window = tp == curtab || tp->tp_firstwin == NULL - ? firstwin - : tp->tp_firstwin; - switchwin_T switchwin; - if (switch_win(&switchwin, window, tp, true) == OK) { - // look up the variable - // Let gettabvar({nr}, "") return the "t:" dictionary. - const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', - varname, strlen(varname), - false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } + win_T *win = NULL; - // restore previous notion of curwin - restore_win(&switchwin, true); + if (tp != NULL) { + win = tp == curtab || tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; } - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - tv_copy(&argvars[2], rettv); - } + get_var_from(varname, rettv, &argvars[2], 't', tp, win, NULL); } /// "gettabwinvar()" function @@ -1705,61 +1726,10 @@ void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "getbufvar()" function void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - bool done = false; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (!tv_check_str_or_nr(&argvars[0])) { - goto f_getbufvar_end; - } - - const char *varname = tv_get_string_chk(&argvars[1]); - emsg_off++; - buf_T *const buf = tv_get_buf(&argvars[0], false); - - if (buf != NULL && varname != NULL) { - if (*varname == '&') { // buffer-local-option - buf_T *const save_curbuf = curbuf; - - // set curbuf to be our buf, temporarily - curbuf = buf; - - if (varname[1] == NUL) { - // get all buffer-local options in a dict - dict_T *opts = get_winbuf_options(true); - - if (opts != NULL) { - tv_dict_set_ret(rettv, opts); - done = true; - } - } else if (get_option_tv(&varname, rettv, true) == OK) { - // buffer-local-option - done = true; - } - - // restore previous notion of curbuf - curbuf = save_curbuf; - } else { - // Look up the variable. - // Let getbufvar({nr}, "") return the "b:" dictionary. - dictitem_T *const v = *varname == NUL - ? (dictitem_T *)&buf->b_bufvar - : find_var_in_ht(&buf->b_vars->dv_hashtab, 'b', - varname, strlen(varname), false); - if (v != NULL) { - tv_copy(&v->di_tv, rettv); - done = true; - } - } - } - emsg_off--; + const char *const varname = tv_get_string_chk(&argvars[1]); + buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); -f_getbufvar_end: - if (!done && argvars[2].v_type != VAR_UNKNOWN) { - // use the default value - tv_copy(&argvars[2], rettv); - } + get_var_from(varname, rettv, &argvars[2], 'b', curtab, curwin, buf); } /// "settabvar()" function -- cgit From ad57610ac7ad3a52e1de42c9dc446acf48fadbb6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 25 Jul 2022 19:31:55 +0800 Subject: vim-patch:9.0.0066: switching window uneccarily when getting buffer options Problem: Switching window uneccarily when getting buffer options. Solution: Do not switch window when getting buffer options. (closes vim/vim#10767) https://github.com/vim/vim/commit/cd6ad6439da2ee2d1a8a6934c9d69e9c2664ba55 --- src/nvim/eval/vars.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index a0e4bfd6a3..190cc62d85 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1506,6 +1506,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, tabpage_T *tp, win_T *win, buf_T *buf) { bool done = false; + const bool do_change_curbuf = buf != NULL && htname == 'b'; emsg_off++; @@ -1518,19 +1519,19 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, // autocommands get blocked. // If we have a buffer reference avoid the switching, we're saving and // restoring curbuf directly. - const bool need_switch_win = !(tp == curtab && win == curwin) || (buf != NULL); + const bool need_switch_win = !(tp == curtab && win == curwin) && !do_change_curbuf; switchwin_T switchwin; if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&' && htname != 't') { buf_T *const save_curbuf = curbuf; // Change curbuf so the option is read from the correct buffer. - if (buf != NULL && htname == 'b') { + if (do_change_curbuf) { curbuf = buf; } if (varname[1] == NUL) { - // get all window-local options in a dict + // get all window-local or buffer-local options in a dict dict_T *opts = get_winbuf_options(htname == 'b'); if (opts != NULL) { -- cgit From 0c0a2e4e526109c6fcd44618e199b36927dff0ff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 27 Jul 2022 06:19:59 +0800 Subject: vim-patch:9.0.0083: ModeChanged event not triggered when leaving cmdline window Problem: ModeChanged event not triggered when leaving the cmdline window. Solution: Call may_trigger_modechanged(). (closes vim/vim#10791) https://github.com/vim/vim/commit/c9e8fd6fc7d2027d0645b376d95a6ed51098036c Code is already present in Nvim. Add some other related missing changes. --- src/nvim/eval/funcs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7d5404d782..fb59a3da4c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4025,6 +4025,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "cindent", "cmdline_compl", "cmdline_hist", + "cmdwin", "comments", "conceal", "cscope", -- cgit From f57432af4db184912af7c107f2bba23b5c37473a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 27 Jul 2022 20:59:43 +0800 Subject: vim-patch:9.0.0090: no error when assigning bool to a string option (#19539) Problem: No error when assigning bool to a string option with setwinvar(). Solution: Give an error (closes vim/vim#10766) https://github.com/vim/vim/commit/28f84e17b068daca2635692d279930dcb7a150d0 --- src/nvim/eval/vars.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 190cc62d85..1aecb40e0b 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1617,6 +1617,10 @@ static void set_option_from_tv(const char *varname, typval_T *varp) char nbuf[NUMBUFLEN]; if (varp->v_type == VAR_BOOL) { + if (is_string_option(varname)) { + emsg(_(e_stringreq)); + return; + } numval = (long)varp->vval.v_number; strval = "0"; // avoid using "false" } else { -- cgit From e0c433833f638fc5e21b00d298661a5fa7c4b5be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 28 Jul 2022 06:05:33 +0800 Subject: refactor: fix clang and PVS warnings (#19532) --- src/nvim/eval/vars.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 1aecb40e0b..dec3ef68aa 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -514,6 +514,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) const char *const used_name = (arg == arg_subsc ? name : name_start); + assert(used_name != NULL); const ptrdiff_t name_size = (used_name == tofree ? (ptrdiff_t)strlen(used_name) : (arg - used_name)); -- cgit From dadb0d6f0194ba7dcdcd2628a77bfec843af8e88 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 28 Jul 2022 00:37:39 +0200 Subject: refactor: enable -Wconversion warning for eval/funcs.c (#19541) Work on https://github.com/neovim/neovim/issues/567 --- src/nvim/eval/funcs.c | 323 +++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 164 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index fb59a3da4c..255a58fede 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -215,12 +215,11 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T } typval_T argv[MAX_FUNC_ARGS + 1]; - const ptrdiff_t base_index - = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; - memcpy(argv, argvars, base_index * sizeof(typval_T)); + const ptrdiff_t base_index = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, (size_t)base_index * sizeof(typval_T)); argv[base_index] = *basetv; memcpy(argv + base_index + 1, argvars + base_index, - (argcount - base_index) * sizeof(typval_T)); + (size_t)(argcount - base_index) * sizeof(typval_T)); argv[argcount + 1].v_type = VAR_UNKNOWN; fdef->func(argv, rettv, fdef->data); @@ -328,7 +327,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const varnumber_T n = tv_get_number_chk(&argvars[1], &error); if (!error) { - ga_append(&b->bv_ga, (int)n); + ga_append(&b->bv_ga, (char)n); tv_copy(&argvars[0], rettv); } } @@ -432,7 +431,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int idx = tv_get_number_chk(&argvars[0], NULL); + int idx = (int)tv_get_number_chk(&argvars[0], NULL); if (arglist != NULL && idx >= 0 && idx < argcount) { rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx])); } else if (idx == -1) { @@ -833,7 +832,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error); + rettv->vval.v_number = channel_close((uint64_t)argvars[0].vval.v_number, part, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -861,7 +860,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const blob_T *const b = argvars[1].vval.v_blob; input_len = tv_blob_len(b); if (input_len > 0) { - input = xmemdup(b->bv_ga.ga_data, input_len); + input = xmemdup(b->bv_ga.ga_data, (size_t)input_len); } } else { input = save_tv_as_string(&argvars[1], &input_len, false); @@ -872,9 +871,9 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) // or there is no input to send. return; } - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; const char *error = NULL; - rettv->vval.v_number = channel_send(id, input, input_len, true, &error); + rettv->vval.v_number = (varnumber_T)channel_send(id, input, (size_t)input_len, true, &error); if (error) { emsg(error); } @@ -1078,7 +1077,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) error = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - def = tv_get_number_chk(&argvars[2], &error); + def = (int)tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = tv_get_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) { @@ -1125,7 +1124,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[2], &error); + ic = (int)tv_get_number_chk(&argvars[2], &error); } if (argvars[0].v_type == VAR_STRING) { @@ -1163,7 +1162,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[3].v_type != VAR_UNKNOWN) { idx = tv_get_number_chk(&argvars[3], &error); if (!error) { - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); if (li == NULL) { semsg(_(e_listidx), (int64_t)idx); } @@ -1236,7 +1235,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { size_t index = 0; if (argvars[0].v_type == VAR_NUMBER) { - index = argvars[0].vval.v_number; + index = (size_t)argvars[0].vval.v_number; } else if (argvars[0].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as an argument"); return; @@ -1304,7 +1303,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) size_t index = 0; if (argvars[1].v_type == VAR_NUMBER) { - index = argvars[1].vval.v_number; + index = (size_t)argvars[1].vval.v_number; } else if (argvars[1].v_type != VAR_UNKNOWN) { semsg(_(e_invarg2), "expected nothing or a Number as second argument"); return; @@ -1338,7 +1337,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = ctx_size(); + rettv->vval.v_number = (varnumber_T)ctx_size(); } /// Set the cursor position. @@ -1372,7 +1371,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { - col = buf_charidx_to_byteidx(curbuf, line, col) + 1; + col = buf_charidx_to_byteidx(curbuf, (linenr_T)line, (int)col) + 1; } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); @@ -1385,12 +1384,12 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) return; // type error; errmsg already given } if (line > 0) { - curwin->w_cursor.lnum = line; + curwin->w_cursor.lnum = (linenr_T)line; } if (col > 0) { - curwin->w_cursor.col = col - 1; + curwin->w_cursor.col = (colnr_T)col - 1; } - curwin->w_cursor.coladd = coladd; + curwin->w_cursor.coladd = (colnr_T)coladd; // Make sure the cursor is in a valid position. check_cursor(); @@ -1442,7 +1441,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) int noref = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - noref = tv_get_number_chk(&argvars[1], NULL); + noref = (int)tv_get_number_chk(&argvars[1], NULL); } if (noref < 0 || noref > 1) { emsg(_(e_invarg)); @@ -1619,7 +1618,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { if (wp->w_cursor.lnum > last) { - wp->w_cursor.lnum -= count; + wp->w_cursor.lnum -= (linenr_T)count; } else if (wp->w_cursor.lnum > first) { wp->w_cursor.lnum = first; } @@ -1656,7 +1655,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; - static int changedtick = 0; + static varnumber_T changedtick = 0; static int fnum = 0; static int change_start = 0; static int change_end = 0; @@ -1693,7 +1692,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. if (col >= change_start && col <= change_end) { hlID = HLF_TXD; // Changed text. } else { @@ -1764,7 +1763,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_copy_fullenv(env, env_size); - for (ssize_t i = env_size - 1; i >= 0; i--) { + for (ssize_t i = (ssize_t)env_size - 1; i >= 0; i--) { const char *str = env[i]; const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), '='); @@ -1792,9 +1791,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(key); continue; } - tv_dict_add_str(rettv->vval.v_dict, - key, len, - value); + tv_dict_add_str(rettv->vval.v_dict, key, (size_t)len, value); xfree(key); } os_free_fullenv(env); @@ -1973,7 +1970,7 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - int id = tv_get_number(argvars); + int id = (int)tv_get_number(argvars); tabpage_T *tp; win_T *wp = win_id2wp_tp(id, &tp); if (wp != NULL && tp != NULL) { @@ -2209,7 +2206,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (before == tv_list_len(l1)) { item = NULL; } else { - item = tv_list_find(l1, before); + item = tv_list_find(l1, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); return; @@ -2326,7 +2323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (argvars[2].v_type != VAR_UNKNOWN) { - count = tv_get_number_chk(&argvars[2], &error); + count = (int)tv_get_number_chk(&argvars[2], &error); } } } @@ -2521,7 +2518,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - unsigned long count = (unsigned long)foldend - foldstart + 1; + unsigned long count = (unsigned long)foldend - (unsigned long)foldstart + 1; txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count); r = xmalloc(STRLEN(txt) + STRLEN(dashes) // for %s @@ -2557,7 +2554,7 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) foldinfo_T info = fold_info(curwin, lnum); if (info.fi_lines > 0) { - text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf); + text = get_foldtext(curwin, lnum, lnum + (linenr_T)info.fi_lines - 1, info, buf); if (text == buf) { text = vim_strsave(text); } @@ -2605,7 +2602,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_BLOB) { bool error = false; - int idx = tv_get_number_chk(&argvars[1], &error); + int idx = (int)tv_get_number_chk(&argvars[1], &error); if (!error) { rettv->v_type = VAR_NUMBER; @@ -2623,7 +2620,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); + li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = TV_LIST_ITEM_TV(li); } @@ -2813,7 +2810,7 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_UNKNOWN) { buf = curbuf; } else { - vim_ignored = tv_get_number(&argvars[0]); // issue errmsg if type error + vim_ignored = (int)tv_get_number(&argvars[0]); // issue errmsg if type error emsg_off++; buf = tv_get_buf(&argvars[0], false); emsg_off--; @@ -2916,21 +2913,21 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (mod_mask != 0) { temp[i++] = K_SPECIAL; temp[i++] = KS_MODIFIER; - temp[i++] = mod_mask; + temp[i++] = (char_u)mod_mask; } if (IS_SPECIAL(n)) { temp[i++] = K_SPECIAL; - temp[i++] = K_SECOND(n); + temp[i++] = (char_u)K_SECOND(n); temp[i++] = K_THIRD(n); } else { - i += utf_char2bytes(n, (char *)temp + i); + i += utf_char2bytes((int)n, (char *)temp + i); } assert(i < 10); temp[i++] = NUL; rettv->v_type = VAR_STRING; rettv->vval.v_string = (char *)vim_strsave(temp); - if (is_mouse_key(n)) { + if (is_mouse_key((int)n)) { int row = mouse_row; int col = mouse_col; int grid = mouse_grid; @@ -2975,7 +2972,7 @@ static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 0; if (n != 0) { - i += utf_char2bytes(n, (char *)temp); + i += utf_char2bytes((int)n, (char *)temp); } assert(i < 7); temp[i++] = NUL; @@ -3094,7 +3091,7 @@ static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = get_cmdline_type(); + rettv->vval.v_string[0] = (char)get_cmdline_type(); } /// "getcmdwintype()" function @@ -3103,7 +3100,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; rettv->vval.v_string = xmallocz(1); - rettv->vval.v_string[0] = cmdwin_type; + rettv->vval.v_string[0] = (char)cmdwin_type; } /// "getcompletion()" function @@ -3143,7 +3140,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strcmp(type, "cmdline") == 0) { set_one_cmd_context(&xpc, pattern); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); - xpc.xp_col = STRLEN(pattern); + xpc.xp_col = (int)STRLEN(pattern); goto theend; } @@ -3224,7 +3221,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; // It is an error for the scope number to be less than `-1`. if (scope_number[i] < -1) { emsg(_(e_invarg)); @@ -3324,7 +3321,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { if (file_perm & (1 << (8 - i))) { - perm[i] = flags[i % 3]; + perm[i] = (char)flags[i % 3]; } } } @@ -3683,7 +3680,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { - wparg = win_id2wp(tv_get_number(&argvars[0])); + wparg = win_id2wp((int)tv_get_number(&argvars[0])); if (wparg == NULL) { return; } @@ -3736,10 +3733,10 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int timeout = argvars[0].vval.v_number; + int timeout = (int)argvars[0].vval.v_number; typval_T expr = argvars[1]; int interval = argvars[2].v_type == VAR_NUMBER - ? argvars[2].vval.v_number + ? (int)argvars[2].vval.v_number : 200; // Default. TimeWatcher *tw = xmalloc(sizeof(TimeWatcher)); @@ -3747,7 +3744,7 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) time_watcher_init(&main_loop, tw, NULL); tw->events = main_loop.events; tw->blockable = true; - time_watcher_start(tw, dummy_timer_due_cb, interval, interval); + time_watcher_start(tw, dummy_timer_due_cb, (uint64_t)interval, (uint64_t)interval); typval_T argv = TV_INITIAL_VALUE; typval_T exprval = TV_INITIAL_VALUE; @@ -3855,7 +3852,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) { flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE; } - size = tv_dict_get_number(d, "size"); + size = (int)tv_dict_get_number(d, "size"); } win_move_into_split(wp, targetwin, size, flags); @@ -4124,7 +4121,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) }; // XXX: eval_has_provider() may shell out :( - const int save_shell_error = get_vim_var_nr(VV_SHELL_ERROR); + const int save_shell_error = (int)get_vim_var_nr(VV_SHELL_ERROR); bool n = false; const char *const name = tv_get_string(&argvars[0]); for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { @@ -4239,7 +4236,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg(_(e_invarg)); return; } - scope_number[i] = argvars[i].vval.v_number; + scope_number[i] = (int)argvars[i].vval.v_number; if (scope_number[i] < -1) { emsg(_(e_invarg)); return; @@ -4455,7 +4452,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) int start = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - start = tv_get_number_chk(&argvars[2], &error); + start = (int)tv_get_number_chk(&argvars[2], &error); if (error) { return; } @@ -4473,7 +4470,7 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (idx = start; idx < tv_blob_len(b); idx++) { typval_T tv; tv.v_type = VAR_NUMBER; - tv.vval.v_number = tv_blob_get(b, idx); + tv.vval.v_number = tv_blob_get(b, (int)idx); if (tv_equal(&tv, &argvars[1], ic, false)) { rettv->vval.v_number = idx; return; @@ -4491,11 +4488,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; // Start at specified item. - idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + idx = tv_list_uidx(l, (int)tv_get_number_chk(&argvars[2], &error)); if (error || idx == -1) { item = NULL; } else { - item = tv_list_find(l, idx); + item = tv_list_find(l, (int)idx); assert(item != NULL); } if (argvars[3].v_type != VAR_UNKNOWN) { @@ -4623,7 +4620,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - const int val = tv_get_number_chk(&argvars[1], &error); + const int val = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -4634,8 +4631,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_grow(&b->bv_ga, 1); char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove(p + before + 1, p + before, (size_t)len - before); - *(p + before) = val; + memmove(p + before + 1, p + before, (size_t)(len - before)); + *(p + before) = (char_u)val; b->bv_ga.ga_len++; tv_copy(&argvars[0], rettv); @@ -4654,7 +4651,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *item = NULL; if (before != tv_list_len(l)) { - item = tv_list_find(l, before); + item = tv_list_find(l, (int)before); if (item == NULL) { semsg(_(e_listidx), (int64_t)before); l = NULL; @@ -4751,8 +4748,8 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); rettv->v_type = VAR_STRING; - rettv->vval.v_string = xmalloc(len + 1); - vim_vsnprintf_typval(rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); + rettv->vval.v_string = xmalloc((size_t)len + 1); + vim_vsnprintf_typval(rettv->vval.v_string, (size_t)len + 1, "%p", dummy_ap, argvars); } /// "jobpid(id)" function @@ -4770,7 +4767,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4796,7 +4793,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, true); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, true); if (!data) { return; } @@ -4806,8 +4803,8 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, (uint16_t)argvars[1].vval.v_number, + (uint16_t)argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -4922,7 +4919,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; i++) { size_t len = strlen(required_env_vars[i]); - dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len); + dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); if (!dv) { const char *env_var = os_getenv(required_env_vars[i]); if (env_var) { @@ -5071,7 +5068,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Channel *data = find_job(argvars[0].vval.v_number, false); + Channel *data = find_job((uint64_t)argvars[0].vval.v_number, false); if (!data) { return; } @@ -5105,7 +5102,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) ui_busy_start(); list_T *args = argvars[0].vval.v_list; - Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); + Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs)); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // Validate, prepare jobs for waiting. @@ -5113,7 +5110,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || !(chan = find_channel((uint64_t)TV_LIST_ITEM_TV(arg)->vval.v_number)) || chan->streamtype != kChannelStreamProc) { jobs[i] = NULL; // Invalid job. } else if (process_is_stopped(&chan->stream.proc)) { @@ -5136,7 +5133,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) int remaining = -1; uint64_t before = 0; if (argvars[1].v_type == VAR_NUMBER && argvars[1].vval.v_number >= 0) { - remaining = argvars[1].vval.v_number; + remaining = (int)argvars[1].vval.v_number; before = os_hrtime(); } @@ -5272,7 +5269,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) { - rettv->v_type = out_type; + rettv->v_type = (VarType)out_type; if (out_type != VAR_NUMBER) { rettv->vval.v_string = NULL; } @@ -5293,7 +5290,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) // input variables char *str_in = (in_type == VAR_STRING) ? argvars[2].vval.v_string : NULL; - int int_in = argvars[2].vval.v_number; + int int_in = (int)argvars[2].vval.v_number; // output variables char **str_out = (out_type == VAR_STRING) ? &rettv->vval.v_string : NULL; @@ -5477,11 +5474,11 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, goto theend; } if (l != NULL) { - idx = tv_list_uidx(l, start); + idx = tv_list_uidx(l, (int)start); if (idx == -1) { goto theend; } - li = tv_list_find(l, idx); + li = tv_list_find(l, (int)idx); } else { if (start < 0) { start = 0; @@ -5493,7 +5490,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, // otherwise skip part of the string. Differs when pattern is "^" // or "\<". if (argvars[3].v_type != VAR_UNKNOWN) { - startcol = start; + startcol = (colnr_T)start; } else { str += start; len -= start; @@ -5737,7 +5734,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = tv_get_number_chk(&argvars[2], NULL); + prot = (int)tv_get_number_chk(&argvars[2], NULL); if (prot == -1) { return; } @@ -5905,7 +5902,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret msgpack_unpacked_init(&unpacked); for (size_t offset = 0; offset < (size_t)len;) { const msgpack_unpack_return result - = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, len, &offset); + = msgpack_unpack_next(&unpacked, blob->bv_ga.ga_data, (size_t)len, &offset); if (msgpackparse_convert_item(unpacked.data, result, ret_list, true) != OK) { break; @@ -6051,9 +6048,9 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { - char *s = xmalloc(len + 1); + char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1); + (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -6197,7 +6194,7 @@ static void init_srand(uint32_t *const x) // Reading /dev/urandom doesn't work, fall back to time(). #endif // uncrustify:off - *x = time(NULL); + *x = (uint32_t)time(NULL); #ifndef MSWIN } #endif @@ -6275,10 +6272,10 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tvw->v_type != VAR_NUMBER) { goto theend; } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - uint32_t w = tvw->vval.v_number; + uint32_t x = (uint32_t)tvx->vval.v_number; + uint32_t y = (uint32_t)tvy->vval.v_number; + uint32_t z = (uint32_t)tvz->vval.v_number; + uint32_t w = (uint32_t)tvw->vval.v_number; result = shuffle_xoshiro128starstar(&x, &y, &z, &w); @@ -6310,7 +6307,7 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) init_srand(&x); } else { bool error = false; - x = tv_get_number_chk(&argvars[0], &error); + x = (uint32_t)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -6477,7 +6474,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); while (maxline < 0 || tv_list_len(l) < maxline) { - readlen = (int)fread(buf, 1, io_size, fd); + readlen = (int)fread(buf, 1, (size_t)io_size, fd); // This for loop processes what was read, but is also entered at end // of file so that either: @@ -6491,7 +6488,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) p++) { if (*p == '\n' || readlen <= 0) { char_u *s = NULL; - size_t len = p - start; + size_t len = (size_t)(p - start); // Finished a line. Remove CRs before NL. if (readlen > 0 && !binary) { @@ -6512,9 +6509,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* Change "prev" buffer to be the right size. This way * the bytes are only copied once, and very long lines are * allocated only once. */ - s = xrealloc(prev, prevlen + len + 1); + s = xrealloc(prev, (size_t)prevlen + len + 1); memcpy(s + prevlen, start, len); - s[prevlen + len] = NUL; + s[(size_t)prevlen + len] = NUL; prev = NULL; // the list will own the string prevlen = prevsize = 0; } @@ -6569,7 +6566,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) dest = buf; } if (readlen > p - buf + 1) { - memmove(dest, p + 1, readlen - (p - buf) - 1); + memmove(dest, p + 1, (size_t)readlen - (size_t)(p - buf) - 1); } readlen -= 3 - adjust_prevlen; prevlen -= adjust_prevlen; @@ -6596,10 +6593,10 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long growmin = (long)((p - start) * 2 + prevlen); prevsize = grow50pc > growmin ? grow50pc : growmin; } - prev = xrealloc(prev, prevsize); + prev = xrealloc(prev, (size_t)prevsize); } // Add the line part to end of "prev". - memmove(prev + prevlen, start, p - start); + memmove(prev + prevlen, start, (size_t)(p - start)); prevlen += (long)(p - start); } } // while @@ -6648,7 +6645,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); - buf[0] = get_register_name(get_unname_register()); + 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); @@ -6699,7 +6696,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL union { struct { int32_t low, high; } split; proftime_T prof; - } u = { .split.high = n1, .split.low = n2 }; + } u = { .split.high = (int32_t)n1, .split.low = (int32_t)n2 }; *tm = u.prof; @@ -6816,15 +6813,15 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (slen == 0) { return; } - const size_t len = slen * n; + const size_t len = slen * (size_t)n; // Detect overflow. - if (len / n != slen) { + if (len / (size_t)n != slen) { return; } char *const r = xmallocz(len); for (varnumber_T i = 0; i < n; i++) { - memmove(r + i * slen, p, slen); + memmove(r + (size_t)i * slen, p, slen); } rettv->vval.v_string = r; @@ -6938,9 +6935,9 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); const size_t p_len = strlen(p); - cpy = xmallocz(p_len + len); + cpy = xmallocz(p_len + (size_t)len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len + 1); + xstrlcat(cpy + p_len, remain, (size_t)len + 1); xfree(p); p = cpy; @@ -7504,7 +7501,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Allocate extra memory for the argument vector and the NULL pointer int argvl = argsl + 2; - char **argv = xmalloc(sizeof(char_u *) * argvl); + char **argv = xmalloc(sizeof(char_u *) * (size_t)argvl); // Copy program name argv[0] = xstrdup(argvars[0].vval.v_string); @@ -7547,13 +7544,13 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - uint64_t id = argvars[0].vval.v_number; + uint64_t id = (uint64_t)argvars[0].vval.v_number; if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { const char *error; - rettv->vval.v_number = channel_close(argvars[0].vval.v_number, - kChannelPartRpc, &error); + rettv->vval.v_number = + channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error); if (!rettv->vval.v_number) { emsg(error); } @@ -7574,7 +7571,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = grid->attrs[grid->line_offset[row] + col]; + c = grid->attrs[grid->line_offset[row] + (size_t)col]; } rettv->vval.v_number = c; } @@ -7585,15 +7582,15 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) int c; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + col]); + c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col]); } rettv->vval.v_number = c; } @@ -7602,8 +7599,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -7612,7 +7609,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } int pcc[MAX_MCO]; - int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + col], pcc); + int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc); int composing_len = 0; while (pcc[composing_len] != 0) { composing_len++; @@ -7647,8 +7644,8 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - pos.lnum = tv_get_number(&argvars[1]); - pos.col = tv_get_number(&argvars[2]) - 1; + pos.lnum = (linenr_T)tv_get_number(&argvars[1]); + pos.col = (colnr_T)tv_get_number(&argvars[2]) - 1; pos.coladd = 0; textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); @@ -7671,8 +7668,8 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; ScreenGrid *grid; - int row = tv_get_number_chk(&argvars[0], NULL) - 1; - int col = tv_get_number_chk(&argvars[1], NULL) - 1; + int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; screenchar_adjust(&grid, &row, &col); @@ -7680,7 +7677,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + col]); + rettv->vval.v_string = (char *)vim_strsave(grid->chars[grid->line_offset[row] + (size_t)col]); } /// "search()" function @@ -7778,8 +7775,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } - retval = do_searchpair(spat, mpat, epat, dir, skip, - flags, match_pos, lnum_stop, time_limit); + retval = (int)do_searchpair(spat, mpat, epat, dir, skip, + flags, match_pos, (linenr_T)lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -8006,7 +8003,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *const l = tv_list_alloc_ret(rettv, n); + list_T *const l = tv_list_alloc_ret(rettv, (ptrdiff_t)n); for (size_t i = 0; i < n; i++) { tv_list_append_allocated_string(l, addrs[i]); } @@ -8387,7 +8384,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strregname == NULL) { return; // Type error; errmsg already given. } - char regname = (uint8_t)(*strregname); + char regname = *strregname; if (regname == 0 || regname == '@') { regname = '"'; } @@ -8466,7 +8463,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + char **lstval = xmalloc(sizeof(char *) * (((size_t)len + 1) * 2)); const char **curval = (const char **)lstval; char **allocval = lstval + len + 2; char **curallocval = allocval; @@ -8488,8 +8485,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) }); *curval++ = NULL; - write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, - block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, (colnr_T)block_len); free_lstval: while (curallocval > allocval) { @@ -8501,8 +8497,8 @@ free_lstval: if (strval == NULL) { return; } - write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), - append, yank_type, block_len); + write_reg_contents_ex(regname, (const char_u *)strval, (ssize_t)STRLEN(strval), + append, yank_type, (colnr_T)block_len); } if (pointreg != 0) { get_yank_register(pointreg, YREG_YANK); @@ -8602,7 +8598,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (col < 0) { return; // type error; errmsg already given } - rettv->vval.v_number = get_sw_value_col(curbuf, col); + rettv->vval.v_number = get_sw_value_col(curbuf, (colnr_T)col); return; } rettv->vval.v_number = get_sw_value(curbuf); @@ -8764,7 +8760,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } str += len; - capcol -= len; + capcol -= (int)len; len = 0; } } @@ -8773,7 +8769,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) assert(len <= INT_MAX); tv_list_alloc_ret(rettv, 2); - tv_list_append_string(rettv->vval.v_list, word, len); + tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -8806,7 +8802,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = tv_get_number_chk(&argvars[1], &typeerr); + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { goto f_spellsuggest_return; } @@ -8978,7 +8974,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - base = tv_get_number(&argvars[1]); + base = (int)tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { emsg(_(e_invarg)); return; @@ -9089,7 +9085,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } charidx--; - byteidx += utf_ptr2len(str + byteidx); + byteidx += (size_t)utf_ptr2len(str + byteidx); } } @@ -9147,7 +9143,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = tv_get_number_chk(&argvars[1], NULL); + skipcc = (int)tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { emsg(_(e_invarg)); @@ -9168,7 +9164,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = tv_get_number(&argvars[1]); + col = (int)tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -9198,12 +9194,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) nchar--; } } else { - nbyte = nchar; + nbyte = (int)nchar; } } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = tv_get_number(&argvars[2]); + int charlen = (int)tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -9215,7 +9211,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) charlen--; } } else { - len = slen - nbyte; // default: all bytes that are available. + len = (int)slen - nbyte; // default: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -9224,12 +9220,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += nbyte; nbyte = 0; } else if ((size_t)nbyte > slen) { - nbyte = slen; + nbyte = (int)slen; } if (len < 0) { len = 0; } else if (nbyte + len > (int)slen) { - len = slen - nbyte; + len = (int)slen - nbyte; } rettv->v_type = VAR_STRING; @@ -9251,7 +9247,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[2].v_type != VAR_UNKNOWN) { len = tv_get_number(&argvars[2]); } else { - len = slen - n; // Default len: all bytes that are available. + len = (varnumber_T)slen - n; // Default len: all bytes that are available. } // Only return the overlap between the specified part and the actual @@ -9260,19 +9256,19 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) len += n; n = 0; } else if (n > (varnumber_T)slen) { - n = slen; + n = (varnumber_T)slen; } if (len < 0) { len = 0; } else if (n + len > (varnumber_T)slen) { - len = slen - n; + len = (varnumber_T)slen - n; } if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { int off; // length in characters - for (off = n; off < (int)slen && len > 0; len--) { + for (off = (int)n; off < (int)slen && len > 0; len--) { off += utfc_ptr2len(p + off); } len = off - n; @@ -9380,7 +9376,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = tv_get_number_chk(&argvars[1], &error); + retList = (int)tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -9453,7 +9449,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = tv_get_number_chk(&argvars[2], &transerr); + const int trans = (int)tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -9550,7 +9546,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synIDtrans(id)" function static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = tv_get_number(&argvars[0]); + int id = (int)tv_get_number(&argvars[0]); if (id > 0) { id = syn_get_final_id(id); @@ -9877,10 +9873,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool overlapped = false; const bool detach = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); + uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, overlapped, detach, stdin_mode, - cwd, term_width, curwin->w_height_inner, + cwd, term_width, (uint16_t)curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -9914,7 +9910,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); @@ -9955,8 +9951,8 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) if (!timer->paused && paused) { time_watcher_stop(&timer->tw); } else if (timer->paused && !paused) { - time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, - timer->timeout); + time_watcher_start(&timer->tw, timer_due_cb, (uint64_t)timer->timeout, + (uint64_t)timer->timeout); } timer->paused = paused; } @@ -9981,7 +9977,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = tv_get_number(&di->di_tv); + repeat = (int)tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -9992,8 +9988,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!callback_from_typval(&callback, &argvars[1])) { return; } - rettv->vval.v_number = - timer_start(tv_get_number(&argvars[0]), repeat, &callback); + rettv->vval.v_number = (varnumber_T)timer_start(tv_get_number(&argvars[0]), repeat, &callback); } /// "timer_stop(timerid)" function @@ -10190,7 +10185,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - rettv->vval.v_string = (char *)vim_strnsave(head, tail - head); + rettv->vval.v_string = (char *)vim_strnsave(head, (size_t)(tail - head)); } /// "type(expr)" function @@ -10294,7 +10289,7 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u str[2]; rettv->v_type = VAR_STRING; - str[0] = curbuf->b_visual_mode_eval; + str[0] = (char_u)curbuf->b_visual_mode_eval; str[1] = NUL; rettv->vval.v_string = (char *)vim_strsave(str); @@ -10508,29 +10503,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = tv_get_number(&di->di_tv); + curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = tv_get_number(&di->di_tv); + curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = tv_get_number(&di->di_tv); + curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = tv_get_number(&di->di_tv); + curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, tv_get_number(&di->di_tv)); + set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = tv_get_number(&di->di_tv); + curwin->w_topfill = (int)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = tv_get_number(&di->di_tv); + curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = tv_get_number(&di->di_tv); + curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); } check_cursor(); -- cgit From 02efdb4d587242122df99b347a25fd4c96b0ca97 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 29 Jul 2022 14:44:18 +0800 Subject: refactor: fix clang and PVS warnings (#19569) The last commit didn't actually disable V1028 because of a typo. Fix the typo so it is actually disabled. --- src/nvim/eval/userfunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 7283fb3cec..a90148bf23 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1268,7 +1268,7 @@ void free_all_functions(void) // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { tv_clear(current_funccal->rettv); - cleanup_function_call(current_funccal); + cleanup_function_call(current_funccal); // -V595 if (current_funccal == NULL && funccal_stack != NULL) { restore_funccal(); } -- cgit From 29d5ca7d66796a306f63d454ad1588df8c58e5aa Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 30 Jul 2022 16:11:02 +0200 Subject: vim-patch:9.0.0111: "nocombine" is missing from synIDattr() Problem: "nocombine" is missing from synIDattr(). Solution: Add "nocombine". (Munif Tanjim, closes vim/vim#10816) https://github.com/vim/vim/commit/de78632c41d870d5254e9ccd285f53674b955f4e --- src/nvim/eval/funcs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 255a58fede..f552c9916e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9498,8 +9498,12 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = highlight_has_attr(id, HL_ITALIC, modec); } break; - case 'n': // name - p = get_highlight_name_ext(NULL, id - 1, false); + case 'n': + if (TOLOWER_ASC(what[1]) == 'o') { // nocombine + p = highlight_has_attr(id, HL_NOCOMBINE, modec); + } else { // name + p = get_highlight_name_ext(NULL, id - 1, false); + } break; case 'r': // reverse p = highlight_has_attr(id, HL_INVERSE, modec); -- cgit From 824a729628950d72834b98faf28d18b7a94eefb2 Mon Sep 17 00:00:00 2001 From: Dundar Goc Date: Tue, 19 Jul 2022 15:30:57 +0200 Subject: refactor: replace char_u with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/eval/funcs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 255a58fede..2c62f56d5f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2130,11 +2130,11 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *errormsg = NULL; rettv->v_type = VAR_STRING; - char_u *cmdstr = (char_u *)xstrdup(tv_get_string(&argvars[0])); + char *cmdstr = xstrdup(tv_get_string(&argvars[0])); exarg_T eap = { - .cmd = (char *)cmdstr, - .arg = (char *)cmdstr, + .cmd = cmdstr, + .arg = cmdstr, .usefilter = false, .nextcmd = NULL, .cmdidx = CMD_USER, @@ -2145,7 +2145,7 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (errormsg != NULL && *errormsg != NUL) { emsg(errormsg); } - rettv->vval.v_string = (char *)cmdstr; + rettv->vval.v_string = cmdstr; } /// "flatten(list[, {maxdepth}])" function @@ -8342,7 +8342,7 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *const block_len) FUNC_ATTR_NONNULL_ALL { - char_u *stropt = *pp; + char *stropt = (char *)(*pp); switch (*stropt) { case 'v': case 'c': // character-wise selection @@ -8364,7 +8364,7 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c default: return FAIL; } - *pp = stropt; + *pp = (char_u *)stropt; return OK; } -- cgit From c1652bdcb5b5ca95b5ae328cec582f23816b70dd Mon Sep 17 00:00:00 2001 From: Shougo Date: Sun, 31 Jul 2022 14:13:19 +0900 Subject: cmdheight=0: fix bugs part2 (#19185) --- src/nvim/eval/funcs.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f552c9916e..7454e9ddab 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2899,6 +2899,11 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) no_mapping--; allow_keys--; + if (!ui_has_messages()) { + // redraw the screen after getchar() + update_screen(CLEAR); + } + set_vim_var_nr(VV_MOUSE_WIN, 0); set_vim_var_nr(VV_MOUSE_WINID, 0); set_vim_var_nr(VV_MOUSE_LNUM, 0); -- cgit