From 4cc0d6b854b44c0b8466e0a84bbc9e350cda8c4f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 1 Feb 2023 21:53:32 +0800 Subject: vim-patch:9.0.1271: using sizeof() and subtract array size is tricky (#22087) Problem: Using sizeof() and subtract array size is tricky. Solution: Use offsetof() instead. (closes vim/vim#11926) https://github.com/vim/vim/commit/1b438a8228a415720efb5ca1c0503f5467292e8e --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 3e593151fc..9ed245d6c4 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1347,7 +1347,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // Make sure dict is valid assert(dict != NULL); - v = xmalloc(sizeof(dictitem_T) + strlen(varname)); + v = xmalloc(offsetof(dictitem_T, di_key) + strlen(varname) + 1); STRCPY(v->di_key, varname); if (hash_add(ht, (char *)v->di_key) == FAIL) { xfree(v); -- cgit From 7224c889e0d5d70b99ae377036baa6377c33a568 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 10:25:24 +0100 Subject: build: enable MSVC level 3 warnings (#21934) MSVC has 4 different warning levels: 1 (severe), 2 (significant), 3 (production quality) and 4 (informational). Enabling level 3 warnings mostly revealed conversion problems, similar to GCC/clang -Wconversion flag. --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 9ed245d6c4..3d9b476863 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -696,7 +696,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo if (!failed) { if (opt_type != gov_string || s != NULL) { - char *err = set_option_value(arg, n, s, scope); + char *err = set_option_value(arg, (long)n, s, scope); arg_end = p; if (err != NULL) { emsg(_(err)); -- cgit From 27177e581902967dcf4f2f426464da1b636ca420 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:14:24 +0100 Subject: refactor: reduce scope of locals as per the style guide (#22211) --- src/nvim/eval/vars.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 3d9b476863..d80bdc70f6 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -369,12 +369,10 @@ int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_ /// @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 == '[') { + const char *s; // "[var, var]": find the matching ']'. - p = arg; + const char *p = arg; for (;;) { p = skipwhite(p + 1); // skip whites after '[', ';' or ',' s = skip_var_one((char *)p); @@ -575,7 +573,6 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { char *arg_end = NULL; - int len; // ":let $VAR = expr": Set environment variable. if (*arg == '$') { @@ -586,7 +583,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo // Find the end of the name. arg++; char *name = arg; - len = get_env_len((const char **)&arg); + int len = get_env_len((const char **)&arg); if (len == 0) { semsg(_(e_invarg2), name - 1); } else { @@ -722,12 +719,10 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo && vim_strchr(endchars, (uint8_t)(*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); + char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); p = (const char *)ptofree; @@ -861,10 +856,9 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ { int forceit = eap->forceit; int ret = OK; - int cc; if (lp->ll_tv == NULL) { - cc = (uint8_t)(*name_end); + int cc = (uint8_t)(*name_end); *name_end = NUL; // Environment variable, normal name or expanded name. -- cgit From 3ad845882413944a8d6ed3bd9da68c6b4cee0afb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Mar 2023 13:47:01 +0800 Subject: vim-patch:8.2.1067: expression "!expr->func()" does not work (#22585) Problem: Expression "!expr->func()" does not work. Solution: Apply plus and minus earlier. (closes vim/vim#6348) https://github.com/vim/vim/commit/0b1cd52ff6bf690388f892be686a91721a082e55 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index d80bdc70f6..ed2453bd59 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -508,7 +508,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true, name, &name) == FAIL) { + if (handle_subscript(&arg, &tv, true, true) == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -1715,7 +1715,7 @@ bool var_exists(const char *var) 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; + n = handle_subscript(&var, &tv, true, false) == OK; if (n) { tv_clear(&tv); } -- cgit From 371823d407d7d7519735131bcad4670c62a731a7 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:13:53 +0200 Subject: refactor: make error message definitions const message.c functions now take const char * as a format. Error message definitions can be made const. --- src/nvim/eval/vars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index ed2453bd59..6252adf6a1 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -50,8 +50,8 @@ #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"); +static const char *e_letunexp = N_("E18: Unexpected characters in :let"); +static const 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. @@ -693,7 +693,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo if (!failed) { if (opt_type != gov_string || s != NULL) { - char *err = set_option_value(arg, (long)n, s, scope); + const char *err = set_option_value(arg, (long)n, s, scope); arg_end = p; if (err != NULL) { emsg(_(err)); -- cgit From 1d2a29f75ba7d094c8e7444c9b249a4a7211c93c Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:39:04 +0200 Subject: refactor: make char * parameters const in message.c Add const to char * parameters in message.c functions and remove some redundant casts. --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 6252adf6a1..43847cb986 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1206,7 +1206,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t msg_putchar(' '); } - msg_outtrans((char *)string); + msg_outtrans(string); if (type == VAR_FUNC || type == VAR_PARTIAL) { msg_puts("()"); -- cgit From 9408f2dcf7cade2631688300e9b58eed6bc5219a Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:40:57 +0200 Subject: refactor: remove redundant const char * casts --- src/nvim/eval/vars.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 43847cb986..e0f7af5718 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -215,7 +215,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const) emsg(_(e_invarg)); } else if (!ends_excmd(*arg)) { // ":let var1 var2" - arg = (char *)list_arg_vars(eap, (const char *)arg, &first); + arg = (char *)list_arg_vars(eap, arg, &first); } else if (!eap->skip) { // ":let" list_glob_vars(&first); @@ -551,7 +551,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) xfree(tofree); } - arg = (const char *)skipwhite(arg); + arg = skipwhite(arg); } return arg; @@ -601,7 +601,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo char *s = vim_getenv(name); if (s != NULL) { tofree = concat_str(s, p); - p = (const char *)tofree; + p = tofree; xfree(s); } } @@ -725,7 +725,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { ptofree = concat_str(s, p); - p = (const char *)ptofree; + p = ptofree; xfree(s); } } @@ -798,7 +798,7 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca do { if (*arg == '$') { - lv.ll_name = (const char *)arg; + lv.ll_name = arg; lv.ll_tv = NULL; arg++; if (get_env_len((const char **)&arg) == 0) { @@ -1169,7 +1169,7 @@ void delete_var(hashtab_T *ht, hashitem_T *hi) 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((char *)v->di_key), + list_one_var_a(prefix, v->di_key, (ptrdiff_t)strlen((char *)v->di_key), v->di_tv.v_type, (s == NULL ? "" : s), first); xfree(s); } -- cgit From 04933b1ea968f958d2541dd65fd33ebb503caac3 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:08:16 +0200 Subject: refactor: remove redundant casts --- src/nvim/eval/vars.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e0f7af5718..77b40fbf11 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -197,10 +197,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const) int var_count = 0; int semicolon = 0; char op[2]; - char *argend; + const char *argend; int first = true; - argend = (char *)skip_var_list(arg, &var_count, &semicolon); + argend = skip_var_list(arg, &var_count, &semicolon); if (argend == NULL) { return; } @@ -235,8 +235,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const) if (!eap->skip) { op[0] = '='; op[1] = NUL; - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); } tv_clear(&rettv); } @@ -267,8 +266,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const) } emsg_skip--; } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, - is_const, (char *)op); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); tv_clear(&rettv); } } @@ -375,7 +373,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) const char *p = arg; for (;;) { p = skipwhite(p + 1); // skip whites after '[', ';' or ',' - s = skip_var_one((char *)p); + s = skip_var_one(p); if (s == p) { semsg(_(e_invarg2), p); return NULL; @@ -398,7 +396,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) } return p + 1; } - return skip_var_one((char *)arg); + return skip_var_one(arg); } /// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, @@ -430,7 +428,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs // apply :filter /pat/ to variable name xstrlcpy(buf, prefix, IOSIZE); - xstrlcat(buf, (char *)di->di_key, IOSIZE); + xstrlcat(buf, di->di_key, IOSIZE); if (message_filtered(buf)) { continue; } @@ -915,7 +913,7 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ if (watched) { tv_copy(&di->di_tv, &oldtv); // need to save key because dictitem_remove will free it - key = xstrdup((char *)di->di_key); + key = xstrdup(di->di_key); } tv_dict_item_remove(d, di); @@ -1169,7 +1167,7 @@ void delete_var(hashtab_T *ht, hashitem_T *hi) 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, v->di_key, (ptrdiff_t)strlen((char *)v->di_key), + list_one_var_a(prefix, v->di_key, (ptrdiff_t)strlen(v->di_key), v->di_tv.v_type, (s == NULL ? "" : s), first); xfree(s); } @@ -1343,7 +1341,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, v = xmalloc(offsetof(dictitem_T, di_key) + strlen(varname) + 1); STRCPY(v->di_key, varname); - if (hash_add(ht, (char *)v->di_key) == FAIL) { + if (hash_add(ht, v->di_key) == FAIL) { xfree(v); return; } @@ -1362,7 +1360,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, } if (watched) { - tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_dict_watcher_notify(dict, v->di_key, &v->di_tv, &oldtv); tv_clear(&oldtv); } -- cgit From 2d78e656b715119ca11d131a1a932f22f1b4ad36 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:43:00 +0200 Subject: refactor: remove redundant casts --- src/nvim/eval/vars.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 77b40fbf11..701f190a06 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -406,8 +406,8 @@ 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); + return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, + NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); } /// List variables for hashtab "ht" with prefix "prefix". -- cgit From 0e4086b74189c2b31ce63c6c5e85124edaf20d08 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 10 Apr 2023 18:06:59 +0800 Subject: fix(eval): prevent double-free in garbage collection (#22990) --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 701f190a06..9120cc4471 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1149,7 +1149,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val) } } hash_clear(ht); - ht->ht_used = 0; + hash_init(ht); } /// Delete a variable from hashtab "ht" at item "hi". -- cgit From 8f69c5ed450337b9f77c50f9ee0d3eb32f649ca6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 07:11:59 +0800 Subject: vim-patch:8.2.{0695,0725,0734,0753,0818,0819,0822} (#23075) vim-patch:8.2.0695: Vim9: cannot define a function inside a function Problem: Vim9: cannot define a function inside a function. Solution: Initial support for :def inside :def. https://github.com/vim/vim/commit/04b12697838b232b8b17c553ccc74cf1f1bdb81c vim-patch:8.2.0725: Vim9: cannot call a function declared later in Vim9 script Problem: Vim9: cannot call a function declared later in Vim9 script. Solution: Make two passes through the script file. https://github.com/vim/vim/commit/09689a02840be40fa7bb10b1921fb5bc5b2908f1 vim-patch:8.2.0734: Vim9: leaking memory when using :finish Problem: Vim9: leaking memory when using :finish. Solution: Do not check for next line in third pass. https://github.com/vim/vim/commit/04816717dfea6e2469ff4c9d40f68b59aaf03724 vim-patch:8.2.0753: Vim9: expressions are evaluated in the discovery phase Problem: Vim9: expressions are evaluated in the discovery phase. Solution: Bail out if an expression is not a constant. Require a type for declared constants. https://github.com/vim/vim/commit/32e351179eacfc84f64cd5029e221582d400bb38 vim-patch:8.2.0818: Vim9: using a discovery phase doesn't work well Problem: Vim9: using a discovery phase doesn't work well. Solution: Remove the discovery phase, instead compile a function only when it is used. Add :defcompile to compile def functions earlier. https://github.com/vim/vim/commit/822ba24743af9ee1b5e7f656a7a61a38f3638bca vim-patch:8.2.0819: compiler warning for unused variable Problem: Compiler warning for unused variable. Solution: Remove the variable. https://github.com/vim/vim/commit/f40e51a880a95f94dbbbecc9476559506c2cc345 vim-patch:8.2.0822: Vim9: code left over from discovery phase Problem: Vim9: code left over from discovery phase. Solution: Remove the dead code. https://github.com/vim/vim/commit/2eec37926db6d31beb36f162ac00357a30c093c8 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 9120cc4471..a3dc0cfc04 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -175,21 +175,13 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) /// ":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) +void ex_let(exarg_T *eap) { + const bool is_const = eap->cmdidx == CMD_const; char *arg = eap->arg; char *expr = NULL; typval_T rettv; @@ -208,8 +200,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const) argend--; } expr = skipwhite(argend); - if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL - && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) { + bool concat = strncmp(expr, "..=", 3) == 0; + bool has_assign = *expr == '=' || (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL + && expr[1] == '='); + if (!has_assign && !concat) { // ":let" without "=": list variables if (*arg == '[') { emsg(_(e_invarg)); @@ -240,6 +234,8 @@ static void ex_let_const(exarg_T *eap, const bool is_const) tv_clear(&rettv); } } else { + rettv.v_type = VAR_UNKNOWN; + op[0] = '='; op[1] = NUL; if (*expr != '=') { @@ -259,7 +255,8 @@ static void ex_let_const(exarg_T *eap, const bool is_const) if (eap->skip) { emsg_skip++; } - i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); + int eval_flags = eap->skip ? 0 : EVAL_EVALUATE; + i = eval0(expr, &rettv, &eap->nextcmd, eval_flags); if (eap->skip) { if (i != FAIL) { tv_clear(&rettv); @@ -506,7 +503,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true) == FAIL) { + if (handle_subscript(&arg, &tv, EVAL_EVALUATE, true) == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -1713,7 +1710,7 @@ bool var_exists(const char *var) 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) == OK; + n = handle_subscript(&var, &tv, EVAL_EVALUATE, false) == OK; if (n) { tv_clear(&tv); } -- cgit From bd83b587b18bb6f2ac555a992fa5b7d907de7e79 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 07:22:01 +0800 Subject: vim-patch:8.2.1047: Vim9: script cannot use line continuation like :def function Problem: Vim9: script cannot use line continuation like in a :def function. Solution: Pass the getline function pointer to the eval() functions. Use it for addition and multiplication operators. https://github.com/vim/vim/commit/5409f5d8c95007216ae1190565a7a8ee9ebd7100 Omit source_nextline() and eval_next_non_blank(): Vim9 script only. N/A patches for version.c: vim-patch:8.2.1048: build failure without the eval feature Problem: Build failure without the eval feature. Solution: Add dummy typedef. https://github.com/vim/vim/commit/9d40c63c7dc8c3eb3886c58dcd334bc7f37eceba vim-patch:8.2.1052: build failure with older compilers Problem: Build failure with older compilers. Solution: Move declaration to start of block. https://github.com/vim/vim/commit/7acde51832f383f9a6d2e740cd0420b433ea841a Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index a3dc0cfc04..02e526d7b6 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -255,13 +255,18 @@ void ex_let(exarg_T *eap) if (eap->skip) { emsg_skip++; } - int eval_flags = eap->skip ? 0 : EVAL_EVALUATE; - i = eval0(expr, &rettv, &eap->nextcmd, eval_flags); + evalarg_T evalarg = { + .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, + .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, + }; + i = eval0(expr, &rettv, &eap->nextcmd, &evalarg); + if (eap->skip) { + emsg_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, op); tv_clear(&rettv); -- cgit From 8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 09:11:37 +0800 Subject: vim-patch:8.2.1049: Vim9: leaking memory when using continuation line Problem: Vim9: leaking memory when using continuation line. Solution: Keep a pointer to the continuation line in evalarg_T. Centralize checking for a next command. https://github.com/vim/vim/commit/b171fb179053fa631fec74911b5fb9374cb6a8a1 Omit eval_next_line(): Vim9 script only. vim-patch:8.2.1050: missing change in struct Problem: Missing change in struct. Solution: Add missing change. https://github.com/vim/vim/commit/65a8ed37f7bc61fbe5c612a7b0eb0dfc16ad3e11 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 02e526d7b6..bad676500b 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -259,7 +259,7 @@ void ex_let(exarg_T *eap) .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, }; - i = eval0(expr, &rettv, &eap->nextcmd, &evalarg); + i = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) { emsg_skip--; } -- cgit From e8c25aac8d864e7033bcfe4640ee44054035f61d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 09:50:51 +0800 Subject: vim-patch:9.0.1447: condition is always true Problem: Condition is always true. Solution: Remove the useless condition. (closes vim/vim#12253) https://github.com/vim/vim/commit/474891bc89e657100bd37c29129451a0e601879d --- src/nvim/eval/vars.c | 74 +++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index bad676500b..79414acac9 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -185,7 +185,6 @@ void ex_let(exarg_T *eap) char *arg = eap->arg; char *expr = NULL; typval_T rettv; - int i; int var_count = 0; int semicolon = 0; char op[2]; @@ -221,7 +220,10 @@ void ex_let(exarg_T *eap) list_vim_vars(&first); } eap->nextcmd = check_nextcmd(arg); - } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { + return; + } + + if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { // HERE document list_T *l = heredoc_get(eap, expr + 3); if (l != NULL) { @@ -233,44 +235,44 @@ void ex_let(exarg_T *eap) } tv_clear(&rettv); } - } else { - rettv.v_type = VAR_UNKNOWN; - - op[0] = '='; - op[1] = NUL; - if (*expr != '=') { - if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) { - op[0] = *expr; // +=, -=, *=, /=, %= or .= - if (expr[0] == '.' && expr[1] == '.') { // ..= - expr++; - } - } - expr += 2; - } else { - expr += 1; - } + return; + } - expr = skipwhite(expr); + rettv.v_type = VAR_UNKNOWN; - if (eap->skip) { - emsg_skip++; - } - evalarg_T evalarg = { - .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, - .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, - }; - i = eval0(expr, &rettv, eap, &evalarg); - if (eap->skip) { - emsg_skip--; - } - if (eap->skip) { - if (i != FAIL) { - tv_clear(&rettv); + op[0] = '='; + op[1] = NUL; + if (*expr != '=') { + if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; } - } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); - tv_clear(&rettv); } + expr += 2; + } else { + expr += 1; + } + + expr = skipwhite(expr); + + if (eap->skip) { + emsg_skip++; + } + evalarg_T evalarg = { + .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, + .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, + }; + int eval_res = eval0(expr, &rettv, eap, &evalarg); + if (eap->skip) { + emsg_skip--; + } + + if (!eap->skip && eval_res != FAIL) { + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); + } + if (eval_res != FAIL) { + tv_clear(&rettv); } } -- cgit From d927128fccad1c234e4b87321ff0d6392b9d69d5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:03:04 +0800 Subject: vim-patch:8.2.1071: Vim9: no line break allowed inside a lambda Problem: Vim9: no line break allowed inside a lambda. Solution: Handle line break inside a lambda in Vim9 script. https://github.com/vim/vim/commit/e40fbc2ca9fda07332a4da5af1fcaba91bed865b Omit skip_expr_concatenate(). Apply the change to skip_expr() instead. Omit eval_ga: Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 79414acac9..c566129202 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -267,6 +267,7 @@ void ex_let(exarg_T *eap) if (eap->skip) { emsg_skip--; } + xfree(evalarg.eval_tofree); if (!eap->skip && eval_res != FAIL) { (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); @@ -510,7 +511,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, EVAL_EVALUATE, true) == FAIL) { + if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -1717,7 +1718,7 @@ bool var_exists(const char *var) 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, EVAL_EVALUATE, false) == OK; + n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK; if (n) { tv_clear(&tv); } -- cgit From e99f28e57d69cfa1bd38ab531a0954f4f875ca47 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:32:47 +0800 Subject: vim-patch:8.2.1076: Vim9: no line break allowed in :if expression Problem: Vim9: no line break allowed in :if expression. Solution: Skip linebreak. https://github.com/vim/vim/commit/faf8626b79e380fe81e7ae2439a535ed7619d27b Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index c566129202..868d03a115 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -267,7 +267,7 @@ void ex_let(exarg_T *eap) if (eap->skip) { emsg_skip--; } - xfree(evalarg.eval_tofree); + clear_evalarg(&evalarg, eap); if (!eap->skip && eval_res != FAIL) { (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); -- cgit From 4b84b2e2aa3d7ab6a4e346c7439826700682f5f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:38:14 +0800 Subject: vim-patch:8.2.1079: Vim9: no line break allowed in a while loop Problem: Vim9: no line break allowed in a while loop. Solution: Update stored loop lines when finding line breaks. https://github.com/vim/vim/commit/d5053d015a957b343ad9c9e45e0abd2978f10cf0 Omit getline_peek(): Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 868d03a115..4bce555d75 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -261,8 +261,11 @@ void ex_let(exarg_T *eap) } evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, - .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, }; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } int eval_res = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) { emsg_skip--; -- cgit From 9c66b48316d85d24ee92d917765700713862aa2d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:57:22 +0800 Subject: vim-patch:8.2.3216: Vim9: crash when using variable in a loop at script level Problem: Vim9: crash when using variable in a loop at script level. Solution: Do not clear the variable if a function was defined. Do not create a new entry in sn_var_vals every time. (closes vim/vim#8628) https://github.com/vim/vim/commit/2eb6fc3b52148f961e804ec2be361d531ff770d8 Omit eval_cstack: Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 4bce555d75..c4a9823c0a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -259,13 +259,8 @@ void ex_let(exarg_T *eap) if (eap->skip) { emsg_skip++; } - evalarg_T evalarg = { - .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, - }; - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, eap->skip); int eval_res = eval0(expr, &rettv, eap, &evalarg); if (eap->skip) { emsg_skip--; -- cgit From 90efe85a99c7986acccc8af370cfb179ae18aeff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 19:28:39 +0800 Subject: vim-patch:8.2.1111: inconsistent naming of get_list_tv() and eval_dict() (#23086) Problem: Inconsistent naming of get_list_tv() and eval_dict(). Solution: Rename get_list_tv() to eval_list(). Similarly for eval_number(), eval_string(), eval_lit_string() and a few others. https://github.com/vim/vim/commit/9a78e6df17033223ebdf499f2b02b2538601c52d Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index c4a9823c0a..b8a8f39437 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -503,8 +503,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) if (tofree != NULL) { name = tofree; } - if (get_var_tv(name, len, &tv, NULL, true, false) - == FAIL) { + if (eval_variable(name, len, &tv, NULL, true, false) == FAIL) { error = true; } else { // handle d.key, l[idx], f(expr) @@ -1076,8 +1075,8 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap /// @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 eval_variable(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose, + bool no_autoload) { int ret = OK; typval_T *tv = NULL; @@ -1564,7 +1563,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv, tv_dict_set_ret(rettv, opts); done = true; } - } else if (get_option_tv(&varname, rettv, true) == OK) { + } else if (eval_option(&varname, rettv, true) == OK) { // Local option done = true; } @@ -1713,7 +1712,7 @@ bool var_exists(const char *var) if (tofree != NULL) { name = tofree; } - n = get_var_tv(name, len, &tv, NULL, false, true) == OK; + n = eval_variable(name, len, &tv, NULL, false, true) == OK; if (n) { // Handle d.key, l[idx], f(expr). n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK; -- cgit From 3ad8c08acc506555667a070cf83c410ac9334f1e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 19:45:54 +0800 Subject: vim-patch:8.2.4770: cannot easily mix expression and heredoc Problem: Cannot easily mix expression and heredoc. Solution: Support in heredoc. (Yegappan Lakshmanan, closes vim/vim#10138) https://github.com/vim/vim/commit/efbfa867a146fcd93fdec2435597aa4ae7f1325c Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval/vars.c | 119 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 15 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index b8a8f39437..3905cf82a6 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -53,6 +53,56 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); +/// Evaluate all the Vim expressions (`=expr`) in string "str" and return the +/// resulting string. The caller must free the returned string. +static char *eval_all_expr_in_str(char *str) +{ + garray_T ga; + ga_init(&ga, 1, 80); + char *p = str; + + // Look for `=expr`, evaluate the expression and replace `=expr` with the + // result. + while (*p != NUL) { + char *s = p; + while (*p != NUL && (*p != '`' || p[1] != '=')) { + p++; + } + ga_concat_len(&ga, s, (size_t)(p - s)); + if (*p == NUL) { + break; // no backtick expression found + } + s = p; + p += 2; // skip `= + + int status = *p == NUL ? OK : skip_expr(&p, NULL); + if (status == FAIL || *p != '`') { + // invalid expression or missing ending backtick + if (status != FAIL) { + emsg(_("E1083: Missing backtick")); + } + xfree(ga.ga_data); + return NULL; + } + s += 2; // skip `= + char save_c = *p; + *p = NUL; + char *exprval = eval_to_string(s, true); + *p = save_c; + p++; + if (exprval == NULL) { + // expression evaluation failed + xfree(ga.ga_data); + return NULL; + } + ga_concat(&ga, exprval); + xfree(exprval); + } + ga_append(&ga, NUL); + + return ga.ga_data; +} + /// Get a list of lines from a HERE document. The here document is a list of /// lines surrounded by a marker. /// cmd << {marker} @@ -65,7 +115,7 @@ static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s") /// marker, then the leading indentation before the lines (matching the /// indentation in the 'cmd' line) is stripped. /// -/// @return a List with {lines} or NULL. +/// @return a List with {lines} or NULL on failure. static list_T *heredoc_get(exarg_T *eap, char *cmd) { char *marker; @@ -81,20 +131,33 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) // 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++; + bool evalstr = false; + bool eval_failed = false; + while (true) { + 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; + + continue; + } + if (strncmp(cmd, "eval", 4) == 0 + && (cmd[4] == NUL || ascii_iswhite(cmd[4]))) { + cmd = skipwhite(cmd + 4); + evalstr = true; + continue; } - text_indent_len = -1; + break; } // The marker is the next word. @@ -136,6 +199,14 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) xfree(theline); break; } + + // If expression evaluation failed in the heredoc, then skip till the + // end marker. + if (eval_failed) { + xfree(theline); + continue; + } + if (text_indent_len == -1 && *theline != NUL) { // set the text indent from the first line. p = theline; @@ -155,11 +226,29 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) } } - tv_list_append_string(l, theline + ti, -1); + char *str = theline + ti; + if (evalstr) { + str = eval_all_expr_in_str(str); + if (str == NULL) { + // expression evaluation failed + xfree(theline); + eval_failed = true; + continue; + } + xfree(theline); + theline = str; + } + + tv_list_append_string(l, str, -1); xfree(theline); } xfree(text_indent); + if (eval_failed) { + // expression evaluation in the heredoc failed + tv_list_free(l); + return NULL; + } return l; } -- cgit From 3c16e75ae194f728c703032084a8f6dd0833a563 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 21:00:08 +0800 Subject: vim-patch:8.2.4783: Coverity warns for leaking memory Problem: Coverity warns for leaking memory. Solution: Use another strategy freeing "theline". https://github.com/vim/vim/commit/42ccb8d74700506936567b0eb6d11def5e25e1dd Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 3905cf82a6..048b5ee2aa 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -178,12 +178,14 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) return NULL; } + char *theline = NULL; list_T *l = tv_list_alloc(0); for (;;) { int mi = 0; int ti = 0; - char *theline = eap->getline(NUL, eap->cookie, 0, false); + xfree(theline); + theline = eap->getline(NUL, eap->cookie, 0, false); if (theline == NULL) { semsg(_("E990: Missing end marker '%s'"), marker); break; @@ -196,14 +198,12 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) mi = marker_indent_len; } if (strcmp(marker, theline + mi) == 0) { - xfree(theline); break; } // If expression evaluation failed in the heredoc, then skip till the // end marker. if (eval_failed) { - xfree(theline); continue; } @@ -231,7 +231,6 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) str = eval_all_expr_in_str(str); if (str == NULL) { // expression evaluation failed - xfree(theline); eval_failed = true; continue; } @@ -240,8 +239,8 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) } tv_list_append_string(l, str, -1); - xfree(theline); } + xfree(theline); xfree(text_indent); if (eval_failed) { -- cgit From 2cf8f01e7d0469b592bacecd5f224b4fe3149a62 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 21:06:15 +0800 Subject: vim-patch:8.2.4840: heredoc expression evaluated even when skipping Problem: Heredoc expression evaluated even when skipping. Solution: Don't evaluate when "skip" is set. (closes vim/vim#10306) https://github.com/vim/vim/commit/05c7f5d3d03440da6f69604f8c06c4e3d90d2a26 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 048b5ee2aa..a8d1e01152 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -227,7 +227,7 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) } char *str = theline + ti; - if (evalstr) { + if (evalstr && !eap->skip) { str = eval_all_expr_in_str(str); if (str == NULL) { // expression evaluation failed -- cgit From bacb5021d4eff33c67eb659fb01125b2abcacd79 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 21:08:00 +0800 Subject: vim-patch:8.2.4883: string interpolation only works in heredoc Problem: String interpolation only works in heredoc. Solution: Support interpolated strings. Use syntax for heredoc consistent with strings, similar to C#. (closes vim/vim#10327) https://github.com/vim/vim/commit/2eaef106e4a7fc9dc74a7e672b5f550ec1f9786e Cherry-pick Test_Debugger_breakadd_expr() from Vim. Co-authored-by: LemonBoy --- src/nvim/eval/vars.c | 81 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 29 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index a8d1e01152..b86c49fd98 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -53,50 +53,73 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); -/// Evaluate all the Vim expressions (`=expr`) in string "str" and return the +/// Evaluate all the Vim expressions ({expr}) in string "str" and return the /// resulting string. The caller must free the returned string. -static char *eval_all_expr_in_str(char *str) +char *eval_all_expr_in_str(char *str) { garray_T ga; ga_init(&ga, 1, 80); char *p = str; - // Look for `=expr`, evaluate the expression and replace `=expr` with the - // result. while (*p != NUL) { - char *s = p; - while (*p != NUL && (*p != '`' || p[1] != '=')) { - p++; + bool escaped_brace = false; + + // Look for a block start. + char *lit_start = p; + while (*p != '{' && *p != '}' && *p != NUL) { + ++p; + } + + if (*p != NUL && *p == p[1]) { + // Escaped brace, unescape and continue. + // Include the brace in the literal string. + ++p; + escaped_brace = true; + } else if (*p == '}') { + semsg(_(e_stray_closing_curly_str), str); + ga_clear(&ga); + return NULL; } - ga_concat_len(&ga, s, (size_t)(p - s)); + + // Append the literal part. + ga_concat_len(&ga, lit_start, (size_t)(p - lit_start)); + if (*p == NUL) { - break; // no backtick expression found + break; } - s = p; - p += 2; // skip `= - int status = *p == NUL ? OK : skip_expr(&p, NULL); - if (status == FAIL || *p != '`') { - // invalid expression or missing ending backtick - if (status != FAIL) { - emsg(_("E1083: Missing backtick")); - } - xfree(ga.ga_data); + if (escaped_brace) { + // Skip the second brace. + ++p; + continue; + } + + // Skip the opening {. + char *block_start = ++p; + char *block_end = block_start; + if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL) { + ga_clear(&ga); return NULL; } - s += 2; // skip `= - char save_c = *p; - *p = NUL; - char *exprval = eval_to_string(s, true); - *p = save_c; - p++; - if (exprval == NULL) { - // expression evaluation failed - xfree(ga.ga_data); + block_end = skipwhite(block_end); + // The block must be closed by a }. + if (*block_end != '}') { + semsg(_(e_missing_close_curly_str), str); + ga_clear(&ga); return NULL; } - ga_concat(&ga, exprval); - xfree(exprval); + char save_c = *block_end; + *block_end = NUL; + char *expr_val = eval_to_string(block_start, true); + *block_end = save_c; + if (expr_val == NULL) { + ga_clear(&ga); + return NULL; + } + ga_concat(&ga, expr_val); + xfree(expr_val); + + p = block_end + 1; } ga_append(&ga, NUL); -- cgit From ef9af89da753235c64cbd8b7d700c686bc94dad7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 17:51:39 +0800 Subject: vim-patch:8.2.4930: interpolated string expression requires escaping Problem: Interpolated string expression requires escaping. Solution: Do not require escaping in the expression. https://github.com/vim/vim/commit/0abc2871c105882ed1c1effb9a7757fad8a395bd Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 70 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index b86c49fd98..7ae7b5a57a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -53,8 +53,42 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); -/// Evaluate all the Vim expressions ({expr}) in string "str" and return the -/// resulting string. The caller must free the returned string. +/// Evaluate one Vim expression {expr} in string "p" and append the +/// resulting string to "gap". "p" points to the opening "{". +/// Return a pointer to the character after "}", NULL for an error. +char *eval_one_expr_in_str(char *p, garray_T *gap) +{ + char *block_start = skipwhite(p + 1); // skip the opening { + char *block_end = block_start; + + if (*block_start == NUL) { + semsg(_(e_missing_close_curly_str), p); + return NULL; + } + if (skip_expr(&block_end, NULL) == FAIL) { + return NULL; + } + block_end = skipwhite(block_end); + if (*block_end != '}') { + semsg(_(e_missing_close_curly_str), p); + return NULL; + } + *block_end = NUL; + char *expr_val = eval_to_string(block_start, true); + *block_end = '}'; + if (expr_val == NULL) { + return NULL; + } + ga_concat(gap, expr_val); + xfree(expr_val); + + return block_end + 1; +} + +/// Evaluate all the Vim expressions {expr} in "str" and return the resulting +/// string in allocated memory. "{{" is reduced to "{" and "}}" to "}". +/// Used for a heredoc assignment. +/// Returns NULL for an error. char *eval_all_expr_in_str(char *str) { garray_T ga; @@ -67,13 +101,13 @@ char *eval_all_expr_in_str(char *str) // Look for a block start. char *lit_start = p; while (*p != '{' && *p != '}' && *p != NUL) { - ++p; + p++; } if (*p != NUL && *p == p[1]) { // Escaped brace, unescape and continue. // Include the brace in the literal string. - ++p; + p++; escaped_brace = true; } else if (*p == '}') { semsg(_(e_stray_closing_curly_str), str); @@ -90,36 +124,16 @@ char *eval_all_expr_in_str(char *str) if (escaped_brace) { // Skip the second brace. - ++p; + p++; continue; } - // Skip the opening {. - char *block_start = ++p; - char *block_end = block_start; - if (*block_start != NUL && skip_expr(&block_end, NULL) == FAIL) { - ga_clear(&ga); - return NULL; - } - block_end = skipwhite(block_end); - // The block must be closed by a }. - if (*block_end != '}') { - semsg(_(e_missing_close_curly_str), str); + // Evaluate the expression and append the result. + p = eval_one_expr_in_str(p, &ga); + if (p == NULL) { ga_clear(&ga); return NULL; } - char save_c = *block_end; - *block_end = NUL; - char *expr_val = eval_to_string(block_start, true); - *block_end = save_c; - if (expr_val == NULL) { - ga_clear(&ga); - return NULL; - } - ga_concat(&ga, expr_val); - xfree(expr_val); - - p = block_end + 1; } ga_append(&ga, NUL); -- cgit From 29efd54e0284727a7dde5608e5eedaef9c00c65f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 18:19:47 +0800 Subject: vim-patch:8.2.4934: string interpolation fails when not evaluating Problem: String interpolation fails when not evaluating. Solution: Skip the expression when not evaluating. (closes vim/vim#10398) https://github.com/vim/vim/commit/70c41241c2701f26a99085e433925a206ca265a3 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 7ae7b5a57a..d3ca2624eb 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -55,8 +55,9 @@ static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s") /// Evaluate one Vim expression {expr} in string "p" and append the /// resulting string to "gap". "p" points to the opening "{". +/// When "evaluate" is false only skip over the expression. /// Return a pointer to the character after "}", NULL for an error. -char *eval_one_expr_in_str(char *p, garray_T *gap) +char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate) { char *block_start = skipwhite(p + 1); // skip the opening { char *block_end = block_start; @@ -73,14 +74,16 @@ char *eval_one_expr_in_str(char *p, garray_T *gap) semsg(_(e_missing_close_curly_str), p); return NULL; } - *block_end = NUL; - char *expr_val = eval_to_string(block_start, true); - *block_end = '}'; - if (expr_val == NULL) { - return NULL; + if (evaluate) { + *block_end = NUL; + char *expr_val = eval_to_string(block_start, true); + *block_end = '}'; + if (expr_val == NULL) { + return NULL; + } + ga_concat(gap, expr_val); + xfree(expr_val); } - ga_concat(gap, expr_val); - xfree(expr_val); return block_end + 1; } @@ -129,7 +132,7 @@ char *eval_all_expr_in_str(char *str) } // Evaluate the expression and append the result. - p = eval_one_expr_in_str(p, &ga); + p = eval_one_expr_in_str(p, &ga, true); if (p == NULL) { ga_clear(&ga); return NULL; -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/eval/vars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index d3ca2624eb..593ba9f6c3 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -220,7 +220,7 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) char *theline = NULL; list_T *l = tv_list_alloc(0); - for (;;) { + while (true) { int mi = 0; int ti = 0; @@ -502,7 +502,7 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) const char *s; // "[var, var]": find the matching ']'. const char *p = arg; - for (;;) { + while (true) { p = skipwhite(p + 1); // skip whites after '[', ';' or ',' s = skip_var_one(p); if (s == p) { @@ -1012,7 +1012,7 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ // Delete a range of List items. listitem_T *const first_li = lp->ll_li; listitem_T *last_li = first_li; - for (;;) { + while (true) { listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, lp->ll_name, -- cgit From 4bcf8c15b3079ca72d6557890b50b35565fcd577 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Apr 2023 08:12:32 +0800 Subject: vim-patch:8.2.0578: heredoc for interfaces does not support "trim" Problem: Heredoc for interfaces does not support "trim". Solution: Update the script heredoc support to be same as the :let command. (Yegappan Lakshmanan, closes vim/vim#5916) https://github.com/vim/vim/commit/6c2b7b8055b96463f78abb70f58c4c6d6d4b9d55 --- src/nvim/eval/vars.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 593ba9f6c3..dee3867a5a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -155,14 +155,19 @@ char *eval_all_expr_in_str(char *str) /// marker, then the leading indentation before the lines (matching the /// indentation in the 'cmd' line) is stripped. /// +/// When getting lines for an embedded script (e.g. python, lua, perl, ruby, +/// tcl, mzscheme), "script_get" is set to true. In this case, if the marker is +/// missing, then '.' is accepted as a marker. +/// /// @return a List with {lines} or NULL on failure. -static list_T *heredoc_get(exarg_T *eap, char *cmd) +list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) { char *marker; char *p; int marker_indent_len = 0; int text_indent_len = 0; char *text_indent = NULL; + char dot[] = "."; if (eap->getline == NULL) { emsg(_("E991: cannot use =<< here")); @@ -214,8 +219,14 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd) return NULL; } } else { - emsg(_("E172: Missing marker")); - return NULL; + // When getting lines for an embedded script, if the marker is missing, + // accept '.' as the marker. + if (script_get) { + marker = dot; + } else { + emsg(_("E172: Missing marker")); + return NULL; + } } char *theline = NULL; @@ -353,7 +364,7 @@ void ex_let(exarg_T *eap) if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') { // HERE document - list_T *l = heredoc_get(eap, expr + 3); + list_T *l = heredoc_get(eap, expr + 3, false); if (l != NULL) { tv_list_set_ret(&rettv, l); if (!eap->skip) { -- cgit From 2eb1f62e29c54fe4d3cebcff388ea6c239313980 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Apr 2023 08:50:04 +0800 Subject: vim-patch:8.2.0672: heredoc in scripts does not accept lower case marker Problem: Heredoc in scripts does not accept lower case marker. Solution: Allow lower case only in non-Vim scripts. (Ken Takata, closes vim/vim#6019) https://github.com/vim/vim/commit/6ab0953fefe31fef91e40752a675ceb60fc2fe03 --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index dee3867a5a..9a653db657 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -214,7 +214,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) return NULL; } *p = NUL; - if (islower((uint8_t)(*marker))) { + if (!script_get && islower((uint8_t)(*marker))) { emsg(_("E221: Marker cannot start with lower case letter")); return NULL; } -- cgit From 7b6d041baed712b071acfa8bb71727a5f5e27561 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Apr 2023 09:23:31 +0800 Subject: fix(heredoc): allow missing end marker for scripts Also do not crash when getting heredoc fails. --- src/nvim/eval/vars.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 9a653db657..de6c4bab60 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -163,7 +163,6 @@ char *eval_all_expr_in_str(char *str) list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) { char *marker; - char *p; int marker_indent_len = 0; int text_indent_len = 0; char *text_indent = NULL; @@ -187,7 +186,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) // 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; + char *p = *eap->cmdlinep; while (ascii_iswhite(*p)) { p++; marker_indent_len++; @@ -208,7 +207,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) // The marker is the next word. if (*cmd != NUL && *cmd != '"') { marker = skipwhite(cmd); - p = skiptowhite(marker); + char *p = skiptowhite(marker); if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { semsg(_(e_trailing_arg), p); return NULL; @@ -238,7 +237,9 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) xfree(theline); theline = eap->getline(NUL, eap->cookie, 0, false); if (theline == NULL) { - semsg(_("E990: Missing end marker '%s'"), marker); + if (!script_get) { + semsg(_("E990: Missing end marker '%s'"), marker); + } break; } @@ -260,7 +261,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) if (text_indent_len == -1 && *theline != NUL) { // set the text indent from the first line. - p = theline; + char *p = theline; text_indent_len = 0; while (ascii_iswhite(*p)) { p++; -- cgit From 88cfb49bee3c9102082c7010acb92244e4ad1348 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 May 2023 07:14:39 +0800 Subject: vim-patch:8.2.4890: inconsistent capitalization in error messages Problem: Inconsistent capitalization in error messages. Solution: Make capitalization consistent. (Doug Kearns) https://github.com/vim/vim/commit/cf030578b26460643dca4a40e7f2e3bc19c749aa Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index de6c4bab60..64177d13e8 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -52,6 +52,10 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); +static const char e_setting_str_to_value_with_wrong_type[] + = N_("E963: Setting %s to value with wrong type"); +static const char e_cannot_use_heredoc_here[] + = N_("E991: Cannot use =<< here"); /// Evaluate one Vim expression {expr} in string "p" and append the /// resulting string to "gap". "p" points to the opening "{". @@ -169,7 +173,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) char dot[] = "."; if (eap->getline == NULL) { - emsg(_("E991: cannot use =<< here")); + emsg(_(e_cannot_use_heredoc_here)); return NULL; } @@ -1457,7 +1461,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, } return; } else if (v->di_tv.v_type != tv->v_type) { - semsg(_("E963: setting %s to value with wrong type"), name); + semsg(_(e_setting_str_to_value_with_wrong_type), name); return; } } -- cgit From f215a2aeaacb9635232cfad82de9a9674a86969c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 30 May 2023 22:56:27 +0800 Subject: vim-patch:8.2.3689: ex_let_one() is too long (#23830) Problem: ex_let_one() is too long. Solution: Split into multiple functions. https://github.com/vim/vim/commit/3ccb5795168793e1b119a028da4035f77cef9f69 Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 348 ++++++++++++++++++++++++++++----------------------- 1 file changed, 190 insertions(+), 158 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 64177d13e8..21b25b64f4 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -701,6 +701,189 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } +/// Set an environment variable, part of ex_let_one(). +static char *ex_let_env(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock an environment variable")); + return NULL; + } + + // Find the end of the name. + char *arg_end = NULL; + arg++; + char *name = arg; + int len = get_env_len((const char **)&arg); + if (len == 0) { + semsg(_(e_invarg2), name - 1); + } else { + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) { + emsg(_(e_letunexp)); + } else if (!check_secure()) { + char *tofree = NULL; + 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 = concat_str(s, p); + p = tofree; + xfree(s); + } + } + if (p != NULL) { + vim_setenv_ext(name, p); + arg_end = arg; + } + name[len] = c1; + xfree(tofree); + } + } + return arg_end; +} + +/// Set an option, part of ex_let_one(). +static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock an option")); + return NULL; + } + + // Find the end of the name. + char *arg_end = NULL; + int scope; + char *const p = (char *)find_option_end((const char **)&arg, &scope); + if (p == NULL + || (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { + emsg(_(e_letunexp)); + } else { + varnumber_T n = 0; + getoption_T opt_type; + long numval; + char *stringval = NULL; + const char *s = NULL; + bool failed = false; + uint32_t opt_p_flags; + char *tofree = NULL; + + const char c1 = *p; + *p = NUL; + + opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); + 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); + } + + if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { + // If the option can be set to a function reference or a lambda + // and the passed value is a function reference, then convert it to + // the name (string) of the function reference. + s = tofree = encode_tv2string(tv, NULL); + } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + // Avoid setting a string option to the text "v:false" or similar. + s = tv_get_string_chk(tv); + } + + if (op != NULL && *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 { + // number or bool + if (opt_type == gov_number || opt_type == gov_bool) { + 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; + } + s = NULL; + } else if (opt_type == gov_string && stringval != NULL && s != NULL) { + // string + char *const oldstringval = stringval; + stringval = concat_str(stringval, s); + xfree(oldstringval); + s = stringval; + } + } + } + + if (!failed) { + if (opt_type != gov_string || s != NULL) { + const char *err = set_option_value(arg, (long)n, s, scope); + arg_end = p; + if (err != NULL) { + emsg(_(err)); + } + } else { + emsg(_(e_stringreq)); + } + } + *p = c1; + xfree(stringval); + xfree(tofree); + } + return arg_end; +} + +/// Set a register, part of ex_let_one(). +static char *ex_let_register(char *arg, typval_T *const tv, const bool is_const, + const char *const endchars, const char *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (is_const) { + emsg(_("E996: Cannot lock a register")); + return NULL; + } + + char *arg_end = NULL; + arg++; + if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { + semsg(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { + emsg(_(e_letunexp)); + } else { + char *ptofree = NULL; + const char *p = tv_get_string_chk(tv); + if (p != NULL && op != NULL && *op == '.') { + char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); + if (s != NULL) { + ptofree = concat_str(s, p); + p = ptofree; + xfree(s); + } + } + if (p != NULL) { + write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); + arg_end = arg + 1; + } + xfree(ptofree); + } + return arg_end; +} + /// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value /// /// @param[in] arg Start of the variable name. @@ -718,172 +901,21 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo { char *arg_end = 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; - int len = get_env_len((const char **)&arg); - if (len == 0) { - semsg(_(e_invarg2), name - 1); - } else { - if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg))) == NULL) { - emsg(_(e_letunexp)); - } else if (!check_secure()) { - char *tofree = NULL; - 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 = concat_str(s, p); - p = tofree; - xfree(s); - } - } - if (p != NULL) { - vim_setenv_ext(name, p); - arg_end = arg; - } - name[len] = c1; - xfree(tofree); - } - } + // ":let $VAR = expr": Set environment variable. + return ex_let_env(arg, tv, is_const, endchars, op); + } else if (*arg == '&') { // ":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. - int scope; - char *const p = (char *)find_option_end((const char **)&arg, &scope); - if (p == NULL - || (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { - emsg(_(e_letunexp)); - } else { - varnumber_T n = 0; - getoption_T opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - bool failed = false; - uint32_t opt_p_flags; - char *tofree = NULL; - - const char c1 = *p; - *p = NUL; - - opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); - 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); - } - - if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { - // If the option can be set to a function reference or a lambda - // and the passed value is a function reference, then convert it to - // the name (string) of the function reference. - s = tofree = encode_tv2string(tv, NULL); - } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - // Avoid setting a string option to the text "v:false" or similar. - s = tv_get_string_chk(tv); - } - - if (op != NULL && *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 { - // number or bool - if (opt_type == gov_number || opt_type == gov_bool) { - 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; - } - s = NULL; - } else if (opt_type == gov_string && stringval != NULL && s != NULL) { - // string - char *const oldstringval = stringval; - stringval = concat_str(stringval, s); - xfree(oldstringval); - s = stringval; - } - } - } - - if (!failed) { - if (opt_type != gov_string || s != NULL) { - const char *err = set_option_value(arg, (long)n, s, scope); - arg_end = p; - if (err != NULL) { - emsg(_(err)); - } - } else { - emsg(_(e_stringreq)); - } - } - *p = c1; - xfree(stringval); - xfree(tofree); - } - // ":let @r = expr": Set register contents. + return ex_let_option(arg, tv, is_const, endchars, op); } else if (*arg == '@') { - if (is_const) { - emsg(_("E996: Cannot lock a register")); - return NULL; - } - arg++; - if (op != NULL && vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) { - semsg(_(e_letwrong), op); - } else if (endchars != NULL - && vim_strchr(endchars, (uint8_t)(*skipwhite(arg + 1))) == NULL) { - emsg(_(e_letunexp)); - } else { - char *ptofree = NULL; - const char *p = tv_get_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - char *s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); - if (s != NULL) { - ptofree = concat_str(s, p); - p = ptofree; - xfree(s); - } - } - if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, (ssize_t)strlen(p), false); - arg_end = arg + 1; - } - xfree(ptofree); - } + // ":let @r = expr": Set register contents. + return ex_let_register(arg, tv, is_const, endchars, op); + } else if (eval_isnamec1(*arg) || *arg == '{') { // ":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, (uint8_t)(*skipwhite(p))) == NULL) { -- cgit From b3d5138fd0066fda26ef7724a542ae45eb42fc84 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 7 Jun 2023 06:05:16 +0600 Subject: refactor(options): remove `getoption_T` and introduce `OptVal` (#23850) Removes the `getoption_T` struct and also introduces the `OptVal` struct to unify the methods of getting/setting different option value types. This is the first of many PRs to reduce code duplication in the Vim option code as well as to make options easier to maintain. It also increases the flexibility and extensibility of options. Which opens the door for things like Array and Dictionary options. --- src/nvim/eval/vars.c | 228 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 86 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 21b25b64f4..0663c3c54c 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -766,84 +766,84 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); - } else { - varnumber_T n = 0; - getoption_T opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - bool failed = false; - uint32_t opt_p_flags; - char *tofree = NULL; - - const char c1 = *p; - *p = NUL; + return NULL; + } - opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); - 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); - } - - if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { - // If the option can be set to a function reference or a lambda - // and the passed value is a function reference, then convert it to - // the name (string) of the function reference. - s = tofree = encode_tv2string(tv, NULL); - } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - // Avoid setting a string option to the text "v:false" or similar. - s = tv_get_string_chk(tv); - } - - if (op != NULL && *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 { - // number or bool - if (opt_type == gov_number || opt_type == gov_bool) { - 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; - } - s = NULL; - } else if (opt_type == gov_string && stringval != NULL && s != NULL) { - // string - char *const oldstringval = stringval; - stringval = concat_str(stringval, s); - xfree(oldstringval); - s = stringval; + bool hidden; + bool error; + const char c1 = *p; + *p = NUL; + + OptVal curval = get_option_value(arg, NULL, scope, &hidden); + OptVal newval = tv_to_optval(tv, arg, scope, &error); + + // Ignore errors for num types + if (newval.type != kOptValTypeNumber && newval.type != kOptValTypeBoolean && error) { + goto end; + } + + // Don't assume current and new values are of the same type in order to future-proof the code for + // when an option can have multiple types. + const bool is_num = ((curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean) + && (newval.type == kOptValTypeNumber || newval.type == kOptValTypeBoolean)); + const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString; + + if (op != NULL && *op != '=') { + if (!hidden && ((is_num && *op == '.') || (is_string && *op != '.'))) { + semsg(_(e_letwrong), op); + goto end; + } else { + // number or bool + if (!hidden && is_num) { + Integer cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; + Integer new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; + + switch (*op) { + case '+': + new_n = cur_n + new_n; break; + case '-': + new_n = cur_n - new_n; break; + case '*': + new_n = cur_n * new_n; break; + case '/': + new_n = num_divide(cur_n, new_n); break; + case '%': + new_n = num_modulus(cur_n, new_n); break; } - } - } - if (!failed) { - if (opt_type != gov_string || s != NULL) { - const char *err = set_option_value(arg, (long)n, s, scope); - arg_end = p; - if (err != NULL) { - emsg(_(err)); + // clamp boolean values + if (newval.type == kOptValTypeBoolean && (new_n > 1 || new_n < -1)) { + new_n = (new_n > 1) ? 1 : -1; } - } else { - emsg(_(e_stringreq)); + + newval = kOptValTypeNumber ? NUMBER_OPTVAL(new_n) : BOOLEAN_OPTVAL((TriState)new_n); + } else if (!hidden && is_string && curval.data.string.data != NULL + && newval.data.string.data != NULL) { + // string + OptVal newval_old = newval; + newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data)); + optval_free(newval_old); } } - *p = c1; - xfree(stringval); - xfree(tofree); } + + // If new value is a string and is NULL, show an error if it's not a hidden option. + // For hidden options, just pass the value to `set_option_value` and let it fail silently. + if (hidden || newval.type != kOptValTypeString || newval.data.string.data != NULL) { + const char *err = set_option_value(arg, newval, scope); + arg_end = p; + if (err != NULL) { + emsg(_(err)); + } + } else { + emsg(_(e_stringreq)); + } + +end: + *p = c1; + optval_free(curval); + optval_free(newval); + return arg_end; } @@ -1809,28 +1809,84 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) 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. -static void set_option_from_tv(const char *varname, typval_T *varp) +/// Convert typval to option value for a particular option. +/// +/// @param[in] tv typval to convert. +/// @param[in] option Option name. +/// @param[in] scope Option scope. +/// @param[out] error Whether an error occured. +/// +/// @return Typval converted to OptVal. Must be freed by caller. +/// Returns NIL_OPTVAL for invalid option name. +static OptVal tv_to_optval(typval_T *tv, const char *option, int scope, bool *error) { - long numval = 0; - const char *strval; - bool error = false; + OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; - - if (varp->v_type == VAR_BOOL) { - if (is_string_option(varname)) { + uint32_t flags; + bool err = false; + + OptVal curval = get_option_value(option, &flags, scope, NULL); + + // TODO(famiu): Delegate all of these type-checks to set_option_value() + if (curval.type == kOptValTypeNil) { + // Invalid option name, + value = NIL_OPTVAL; + } else if ((flags & P_FUNC) && tv_is_func(*tv)) { + // If the option can be set to a function reference or a lambda + // and the passed value is a function reference, then convert it to + // the name (string) of the function reference. + char *strval = encode_tv2string(tv, NULL); + err = strval == NULL; + value = CSTR_AS_OPTVAL(strval); + } else if (flags & (P_NUM | P_BOOL)) { + varnumber_T n = tv_get_number_chk(tv, &err); + // This could be either "0" or a string that's not a number. So we need to check if it's + // actually a number. + if (!err && tv->v_type == VAR_STRING && n == 0) { + unsigned idx; + for (idx = 0; tv->vval.v_string[idx] == '0'; idx++) {} + if (tv->vval.v_string[idx] != NUL || idx == 0) { + // There's another character after zeros or the string is empty. + // In both cases, we are trying to set a num option using a string. + semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); + } + } + value = (flags & P_NUM) ? NUMBER_OPTVAL(n) + : BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); + } else if (flags & P_STRING || is_tty_option(option)) { + // Avoid setting string option to a boolean. + if (tv->v_type == VAR_BOOL) { + err = true; emsg(_(e_stringreq)); - return; + } else { + const char *strval = tv_get_string_buf_chk(tv, nbuf); + err = strval == NULL; + value = CSTR_TO_OPTVAL(strval); } - 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); + abort(); // This should never happen. } - if (!error && strval != NULL) { - set_option_value_give_err(varname, numval, strval, OPT_LOCAL); + + if (error != NULL) { + *error = err; } + optval_free(curval); + return value; +} + +/// Set option "varname" to the value of "varp" for the current buffer/window. +static void set_option_from_tv(const char *varname, typval_T *varp) +{ + bool error = false; + OptVal value = tv_to_optval(varp, varname, OPT_LOCAL, &error); + + if (!error && value.type == kOptValTypeNil) { + semsg(_(e_unknown_option2), varname); + } else if (!error) { + set_option_value_give_err(varname, value, OPT_LOCAL); + } + + optval_free(value); } /// "setwinvar()" and "settabwinvar()" functions -- cgit From a0cb53eca7a04326dd857cf33fac1154aff7773a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 12 Jun 2023 15:38:53 +0800 Subject: vim-patch:8.2.2533: Vim9: cannot use a range with :unlet Problem: Vim9: cannot use a range with :unlet. Solution: Implement ISN_UNLETRANGE. https://github.com/vim/vim/commit/5b5ae29bd3d7b832b6f15320430f7f191e0abd1f Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 0663c3c54c..f988a49e6c 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1056,25 +1056,10 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ 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; - while (true) { - listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (value_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; - } - last_li = lp->ll_li; + if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_name_len, + lp->ll_n1, !lp->ll_empty2, lp->ll_n2) == FAIL) { + return FAIL; } - tv_list_remove_items(lp->ll_list, first_li, last_li); } else { if (lp->ll_list != NULL) { // unlet a List item. @@ -1107,6 +1092,31 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ return ret; } +/// Unlet one item or a range of items from a list. +/// Return OK or FAIL. +static int list_unlet_range(list_T *const l, listitem_T *const li_first, const char *const name, + const size_t name_len, const long n1_arg, const bool has_n2, + const long n2) +{ + assert(l != NULL); + // Delete a range of List items. + listitem_T *li_last = li_first; + long n1 = n1_arg; + while (true) { + if (value_check_lock(TV_LIST_ITEM_TV(li_last)->v_lock, name, name_len)) { + return FAIL; + } + listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last); + n1++; + if (li == NULL || (has_n2 && n2 < n1)) { + break; + } + li_last = li; + } + tv_list_remove_items(l, li_first, li_last); + return OK; +} + /// unlet a variable /// /// @param[in] name Variable name to unlet. -- cgit From a7e5d4238a00d5bfa5809b2860047eca2d565e62 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 12 Jun 2023 21:58:40 +0800 Subject: vim-patch:8.2.4600: Vim9: not enough test coverage for executing :def function (#24001) Problem: Vim9: not enough test coverage for executing :def function. Solution: Add a few more tests. Fix inconsistencies. https://github.com/vim/vim/commit/6b8c7ba062ca4b50e8f983e0485be7afa4eef691 Cherry-pick a blank line in test_listdict.vim from patch 8.2.3842. Co-authored-by: Bram Moolenaar --- src/nvim/eval/vars.c | 56 +++++++++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index f988a49e6c..7ba751490b 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1056,36 +1056,31 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { - if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_name_len, - lp->ll_n1, !lp->ll_empty2, lp->ll_n2) == FAIL) { - return FAIL; - } + tv_list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_n1, !lp->ll_empty2, lp->ll_n2); + } else if (lp->ll_list != NULL) { + // unlet a List item. + tv_list_item_remove(lp->ll_list, lp->ll_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; + // 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(di->di_key); - } + if (watched) { + tv_copy(&di->di_tv, &oldtv); + // need to save key because dictitem_remove will free it + key = xstrdup(di->di_key); + } - tv_dict_item_remove(d, di); + tv_dict_item_remove(d, di); - if (watched) { - tv_dict_watcher_notify(d, key, NULL, &oldtv); - tv_clear(&oldtv); - xfree(key); - } + if (watched) { + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); + xfree(key); } } @@ -1094,18 +1089,14 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ /// Unlet one item or a range of items from a list. /// Return OK or FAIL. -static int list_unlet_range(list_T *const l, listitem_T *const li_first, const char *const name, - const size_t name_len, const long n1_arg, const bool has_n2, - const long n2) +static void tv_list_unlet_range(list_T *const l, listitem_T *const li_first, const long n1_arg, + const bool has_n2, const long n2) { assert(l != NULL); // Delete a range of List items. listitem_T *li_last = li_first; long n1 = n1_arg; while (true) { - if (value_check_lock(TV_LIST_ITEM_TV(li_last)->v_lock, name, name_len)) { - return FAIL; - } listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last); n1++; if (li == NULL || (has_n2 && n2 < n1)) { @@ -1114,7 +1105,6 @@ static int list_unlet_range(list_T *const l, listitem_T *const li_first, const c li_last = li; } tv_list_remove_items(l, li_first, li_last); - return OK; } /// unlet a variable -- cgit From 78d77c03de579845fcaa761e7339c93fcd74efb2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 15 Jun 2023 08:05:26 +0800 Subject: vim-patch:9.0.1631: passing wrong variable type to option gives multiple errors (#24026) Problem: Passing a wrong variable type to an option gives multiple errors. Solution: Bail out early on failure. (closes vim/vim#12504) https://github.com/vim/vim/commit/4c7cb372c17a84c8a35254d93eb37cb854cd39da --- src/nvim/eval/vars.c | 148 +++++++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 76 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 7ba751490b..35ae558006 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -769,17 +769,28 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, return NULL; } - bool hidden; - bool error; const char c1 = *p; *p = NUL; - OptVal curval = get_option_value(arg, NULL, scope, &hidden); - OptVal newval = tv_to_optval(tv, arg, scope, &error); + uint32_t opt_p_flags; + bool hidden; + OptVal curval = get_option_value(arg, &opt_p_flags, scope, &hidden); + OptVal newval = NIL_OPTVAL; + if (curval.type == kOptValTypeNil && arg[0] != 't' && arg[1] != '_') { + semsg(_(e_unknown_option2), arg); + goto theend; + } + if (op != NULL && *op != '=' + && ((curval.type != kOptValTypeString && *op == '.') + || (curval.type == kOptValTypeString && *op != '.'))) { + semsg(_(e_letwrong), op); + goto theend; + } - // Ignore errors for num types - if (newval.type != kOptValTypeNumber && newval.type != kOptValTypeBoolean && error) { - goto end; + bool error; + newval = tv_to_optval(tv, arg, opt_p_flags, &error); + if (error) { + goto theend; } // Don't assume current and new values are of the same type in order to future-proof the code for @@ -789,61 +800,47 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString; if (op != NULL && *op != '=') { - if (!hidden && ((is_num && *op == '.') || (is_string && *op != '.'))) { - semsg(_(e_letwrong), op); - goto end; - } else { - // number or bool - if (!hidden && is_num) { - Integer cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; - Integer new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; - - switch (*op) { - case '+': - new_n = cur_n + new_n; break; - case '-': - new_n = cur_n - new_n; break; - case '*': - new_n = cur_n * new_n; break; - case '/': - new_n = num_divide(cur_n, new_n); break; - case '%': - new_n = num_modulus(cur_n, new_n); break; - } - - // clamp boolean values - if (newval.type == kOptValTypeBoolean && (new_n > 1 || new_n < -1)) { - new_n = (new_n > 1) ? 1 : -1; - } + if (!hidden && is_num) { // number or bool + Integer cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; + Integer new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; + + switch (*op) { + case '+': + new_n = cur_n + new_n; break; + case '-': + new_n = cur_n - new_n; break; + case '*': + new_n = cur_n * new_n; break; + case '/': + new_n = num_divide(cur_n, new_n); break; + case '%': + new_n = num_modulus(cur_n, new_n); break; + } - newval = kOptValTypeNumber ? NUMBER_OPTVAL(new_n) : BOOLEAN_OPTVAL((TriState)new_n); - } else if (!hidden && is_string && curval.data.string.data != NULL - && newval.data.string.data != NULL) { - // string - OptVal newval_old = newval; - newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data)); - optval_free(newval_old); + // clamp boolean values + if (newval.type == kOptValTypeBoolean && (new_n > 1 || new_n < -1)) { + new_n = (new_n > 1) ? 1 : -1; } + + newval = kOptValTypeNumber ? NUMBER_OPTVAL(new_n) : BOOLEAN_OPTVAL((TriState)new_n); + } else if (!hidden && is_string + && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string + OptVal newval_old = newval; + newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data)); + optval_free(newval_old); } } - // If new value is a string and is NULL, show an error if it's not a hidden option. - // For hidden options, just pass the value to `set_option_value` and let it fail silently. - if (hidden || newval.type != kOptValTypeString || newval.data.string.data != NULL) { - const char *err = set_option_value(arg, newval, scope); - arg_end = p; - if (err != NULL) { - emsg(_(err)); - } - } else { - emsg(_(e_stringreq)); + const char *err = set_option_value(arg, newval, scope); + arg_end = p; + if (err != NULL) { + emsg(_(err)); } -end: +theend: *p = c1; optval_free(curval); optval_free(newval); - return arg_end; } @@ -1813,25 +1810,18 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// /// @param[in] tv typval to convert. /// @param[in] option Option name. -/// @param[in] scope Option scope. +/// @param[in] flags Option flags. /// @param[out] error Whether an error occured. /// /// @return Typval converted to OptVal. Must be freed by caller. /// Returns NIL_OPTVAL for invalid option name. -static OptVal tv_to_optval(typval_T *tv, const char *option, int scope, bool *error) +static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error) { OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; - uint32_t flags; bool err = false; - OptVal curval = get_option_value(option, &flags, scope, NULL); - - // TODO(famiu): Delegate all of these type-checks to set_option_value() - if (curval.type == kOptValTypeNil) { - // Invalid option name, - value = NIL_OPTVAL; - } else if ((flags & P_FUNC) && tv_is_func(*tv)) { + if ((flags & P_FUNC) && tv_is_func(*tv)) { // If the option can be set to a function reference or a lambda // and the passed value is a function reference, then convert it to // the name (string) of the function reference. @@ -1839,29 +1829,31 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, int scope, bool *er err = strval == NULL; value = CSTR_AS_OPTVAL(strval); } else if (flags & (P_NUM | P_BOOL)) { - varnumber_T n = tv_get_number_chk(tv, &err); - // This could be either "0" or a string that's not a number. So we need to check if it's - // actually a number. + varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err) + : tv_get_bool_chk(tv, &err); + // This could be either "0" or a string that's not a number. + // So we need to check if it's actually a number. if (!err && tv->v_type == VAR_STRING && n == 0) { unsigned idx; for (idx = 0; tv->vval.v_string[idx] == '0'; idx++) {} if (tv->vval.v_string[idx] != NUL || idx == 0) { // There's another character after zeros or the string is empty. // In both cases, we are trying to set a num option using a string. + err = true; semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } value = (flags & P_NUM) ? NUMBER_OPTVAL(n) : BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); - } else if (flags & P_STRING || is_tty_option(option)) { - // Avoid setting string option to a boolean. - if (tv->v_type == VAR_BOOL) { - err = true; - emsg(_(e_stringreq)); - } else { + } else if ((flags & P_STRING) || is_tty_option(option)) { + // Avoid setting string option to a boolean or a special value. + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { const char *strval = tv_get_string_buf_chk(tv, nbuf); err = strval == NULL; value = CSTR_TO_OPTVAL(strval); + } else if (flags & P_STRING) { + err = true; + emsg(_(e_stringreq)); } } else { abort(); // This should never happen. @@ -1870,19 +1862,23 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, int scope, bool *er if (error != NULL) { *error = err; } - optval_free(curval); return value; } /// Set option "varname" to the value of "varp" for the current buffer/window. static void set_option_from_tv(const char *varname, typval_T *varp) { + int opt_idx = findoption(varname); + if (opt_idx < 0) { + semsg(_(e_unknown_option2), varname); + return; + } + uint32_t opt_p_flags = get_option(opt_idx)->flags; + bool error = false; - OptVal value = tv_to_optval(varp, varname, OPT_LOCAL, &error); + OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error); - if (!error && value.type == kOptValTypeNil) { - semsg(_(e_unknown_option2), varname); - } else if (!error) { + if (!error) { set_option_value_give_err(varname, value, OPT_LOCAL); } -- cgit From 1f8fb7c00048bc217bb9a2bf29c58630d1810d6e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 15 Jun 2023 12:36:21 +0800 Subject: fix(:let): fix error when applying operator to boolean option (#24030) --- src/nvim/eval/vars.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 35ae558006..9b6427fef7 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -817,12 +817,11 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, new_n = num_modulus(cur_n, new_n); break; } - // clamp boolean values - if (newval.type == kOptValTypeBoolean && (new_n > 1 || new_n < -1)) { - new_n = (new_n > 1) ? 1 : -1; + if (curval.type == kOptValTypeNumber) { + newval = NUMBER_OPTVAL(new_n); + } else { + newval = BOOLEAN_OPTVAL(new_n == 0 ? kFalse : (new_n >= 1 ? kTrue : kNone)); } - - newval = kOptValTypeNumber ? NUMBER_OPTVAL(new_n) : BOOLEAN_OPTVAL((TriState)new_n); } else if (!hidden && is_string && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string OptVal newval_old = newval; -- cgit From a6e74c1f0a2bbf03f5b99c167b549018f4c8fb0d Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 14 Sep 2023 06:05:27 +0200 Subject: docs: fix typos and other small fixes (#25005) Co-authored-by: nuid64 Co-authored-by: Mike Smith <10135646+mikesmithgh@users.noreply.github.com> Co-authored-by: XTY Co-authored-by: Empa Co-authored-by: kyu08 <49891479+kyu08@users.noreply.github.com> --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 9b6427fef7..e5b1b88eef 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1810,7 +1810,7 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) /// @param[in] tv typval to convert. /// @param[in] option Option name. /// @param[in] flags Option flags. -/// @param[out] error Whether an error occured. +/// @param[out] error Whether an error occurred. /// /// @return Typval converted to OptVal. Must be freed by caller. /// Returns NIL_OPTVAL for invalid option name. -- cgit From 4d3a38ac074fff7e2a4bede4cee7699bdd55ffdc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 24 Sep 2023 10:57:09 +0800 Subject: fix(api, lua): handle setting v: variables properly (#25325) --- src/nvim/eval/vars.c | 145 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 57 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index e5b1b88eef..5d1da956ee 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -52,8 +52,8 @@ static const char *e_letunexp = N_("E18: Unexpected characters in :let"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); -static const char e_setting_str_to_value_with_wrong_type[] - = N_("E963: Setting %s to value with wrong type"); +static const char e_setting_v_str_to_value_with_wrong_type[] + = N_("E963: Setting v:%s to value with wrong type"); static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here"); @@ -1389,6 +1389,62 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t } } +/// Additional handling for setting a v: variable. +/// +/// @return true if the variable should be set normally, +/// false if nothing else needs to be done. +bool before_set_vvar(const char *const varname, dictitem_T *const di, typval_T *const tv, + const bool copy, const bool watched, bool *const type_error) +{ + if (di->di_tv.v_type == VAR_STRING) { + typval_T oldtv = TV_INITIAL_VALUE; + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } + XFREE_CLEAR(di->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 (di->di_tv.vval.v_string == NULL) { + di->di_tv.vval.v_string = xstrdup(val); + } + } else { + // Take over the string to avoid an extra alloc/free. + di->di_tv.vval.v_string = tv->vval.v_string; + tv->vval.v_string = NULL; + } + // Notify watchers + if (watched) { + tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv); + tv_clear(&oldtv); + } + return false; + } else if (di->di_tv.v_type == VAR_NUMBER) { + typval_T oldtv = TV_INITIAL_VALUE; + if (watched) { + tv_copy(&di->di_tv, &oldtv); + } + di->di_tv.vval.v_number = tv_get_number(tv); + if (strcmp(varname, "searchforward") == 0) { + set_search_direction(di->di_tv.vval.v_number ? '/' : '?'); + } else if (strcmp(varname, "hlsearch") == 0) { + no_hlsearch = !di->di_tv.vval.v_number; + redraw_all_later(UPD_SOME_VALID); + } + // Notify watchers + if (watched) { + tv_dict_watcher_notify(&vimvardict, varname, &di->di_tv, &oldtv); + tv_clear(&oldtv); + } + return false; + } else if (di->di_tv.v_type != tv->v_type) { + *type_error = true; + return false; + } + return true; +} + /// Set variable to the given value /// /// If the variable already exists, the value is updated. Otherwise the variable @@ -1418,31 +1474,29 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, 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); + dict_T *dict; + hashtab_T *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); + const size_t varname_len = name_len - (size_t)(varname - name); + dictitem_T *di = find_var_in_ht(ht, 0, varname, varname_len, 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 (di == NULL) { + di = find_var_in_scoped_ht(name, name_len, true); } - if (tv_is_func(*tv) && var_wrong_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && var_wrong_func_name(name, di == NULL)) { return; } typval_T oldtv = TV_INITIAL_VALUE; - if (v != NULL) { + if (di != NULL) { if (is_const) { emsg(_(e_cannot_mod)); return; @@ -1452,9 +1506,9 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // - Whether the variable is read-only // - Whether the variable value is locked // - Whether the variable is locked - if (var_check_ro(v->di_flags, name, name_len) - || value_check_lock(v->di_tv.v_lock, name, name_len) - || var_check_lock(v->di_flags, name, name_len)) { + if (var_check_ro(di->di_flags, name, name_len) + || value_check_lock(di->di_tv.v_lock, name, name_len) + || var_check_lock(di->di_flags, name, name_len)) { return; } @@ -1462,42 +1516,19 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // 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(UPD_SOME_VALID); - } - return; - } else if (v->di_tv.v_type != tv->v_type) { - semsg(_(e_setting_str_to_value_with_wrong_type), name); - return; + bool type_error = false; + if (is_vimvarht(ht) + && !before_set_vvar(varname, di, tv, copy, watched, &type_error)) { + if (type_error) { + semsg(_(e_setting_v_str_to_value_with_wrong_type), varname); } + return; } if (watched) { - tv_copy(&v->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); } - tv_clear(&v->di_tv); + tv_clear(&di->di_tv); } else { // Add a new variable. // Can't add "v:" or "a:" variable. if (is_vimvarht(ht) || ht == get_funccal_args_ht()) { @@ -1513,28 +1544,28 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // Make sure dict is valid assert(dict != NULL); - v = xmalloc(offsetof(dictitem_T, di_key) + strlen(varname) + 1); - STRCPY(v->di_key, varname); - if (hash_add(ht, v->di_key) == FAIL) { - xfree(v); + di = xmalloc(offsetof(dictitem_T, di_key) + varname_len + 1); + memcpy(di->di_key, varname, varname_len + 1); + if (hash_add(ht, di->di_key) == FAIL) { + xfree(di); return; } - v->di_flags = DI_FLAGS_ALLOC; + di->di_flags = DI_FLAGS_ALLOC; if (is_const) { - v->di_flags |= DI_FLAGS_LOCK; + di->di_flags |= DI_FLAGS_LOCK; } } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - tv_copy(tv, &v->di_tv); + tv_copy(tv, &di->di_tv); } else { - v->di_tv = *tv; - v->di_tv.v_lock = VAR_UNLOCKED; + di->di_tv = *tv; + di->di_tv.v_lock = VAR_UNLOCKED; tv_init(tv); } if (watched) { - tv_dict_watcher_notify(dict, v->di_key, &v->di_tv, &oldtv); + tv_dict_watcher_notify(dict, di->di_key, &di->di_tv, &oldtv); tv_clear(&oldtv); } @@ -1542,7 +1573,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv, // 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); + tv_item_lock(&di->di_tv, DICT_MAXNEST, true, true); } } -- cgit From f91cd31d7d9d70006e0000592637d5d997eab52c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 27 Sep 2023 21:46:39 +0200 Subject: refactor(messages): fold msg_outtrans_attr into msg_outtrans problem: there are too many different functions in message.c solution: fold some of the functions into themselves --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 5d1da956ee..60cf0f8054 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1378,7 +1378,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t msg_putchar(' '); } - msg_outtrans(string); + msg_outtrans(string, 0); if (type == VAR_FUNC || type == VAR_PARTIAL) { msg_puts("()"); -- cgit From b85f1dafc7c0a19704135617454f1c66f41202c1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 27 Sep 2023 22:21:17 +0200 Subject: refactor(messages): fold msg_attr into msg problem: there are too many different functions in message.c solution: fold some of the functions into themselves --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 60cf0f8054..8273e8434d 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1352,7 +1352,7 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first) 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" + // don't use msg() to avoid overwriting "v:statusmsg" msg_start(); msg_puts(prefix); if (name != NULL) { // "a:" vars don't have a name stored -- cgit From af7d317f3ff31d5ac5d8724b5057a422e1451b54 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 26 Sep 2023 22:36:08 +0200 Subject: refactor: remove long long is 32-bits even on 64-bit windows which makes the type suboptimal for a codebase meant to be cross-platform. --- src/nvim/eval/vars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 8273e8434d..880836cc9f 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -801,8 +801,8 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, if (op != NULL && *op != '=') { if (!hidden && is_num) { // number or bool - Integer cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; - Integer new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; + OptInt cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; + OptInt new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; switch (*op) { case '+': @@ -1873,7 +1873,7 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } - value = (flags & P_NUM) ? NUMBER_OPTVAL(n) + value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); } else if ((flags & P_STRING) || is_tty_option(option)) { // Avoid setting string option to a boolean or a special value. -- cgit From 8e11c18d4962c5367a0549bdb2288323545852b6 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Sep 2023 16:10:28 +0200 Subject: refactor(message): msg_puts_attr_len -> msg_puts_len --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 880836cc9f..64ba37eaac 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1356,7 +1356,7 @@ static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t 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_puts_len(name, name_len, 0); } msg_putchar(' '); msg_advance(22); -- cgit From cf8b2c0e74fd5e723b0c15c2ce84e6900fd322d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 12:05:28 +0800 Subject: build(iwyu): add a few more _defs.h mappings (#25435) --- src/nvim/eval/vars.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 64ba37eaac..44b987b36e 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "nvim/ascii.h" @@ -19,7 +20,6 @@ #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" @@ -27,6 +27,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/hashtab.h" -- cgit From dc6d0d2daf69e2fdadda81feb97906dbc962a239 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 14:41:34 +0800 Subject: refactor: reorganize option header files (#25437) - Move vimoption_T to option.h - option_defs.h is for option-related types - option_vars.h corresponds to Vim's option.h - option_defs.h and option_vars.h don't include each other --- src/nvim/eval/vars.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 44b987b36e..f9dcfb3d9d 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -36,6 +36,7 @@ #include "nvim/message.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/os/os.h" #include "nvim/search.h" #include "nvim/strings.h" -- cgit From 93b9c889465ee6a55e71c1fd681c1c6b1d5ed060 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 27 Sep 2023 23:30:17 +0600 Subject: refactor(options): unify set_num_option and set_bool_option --- src/nvim/eval/vars.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index f9dcfb3d9d..4b314ca338 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -822,7 +822,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, if (curval.type == kOptValTypeNumber) { newval = NUMBER_OPTVAL(new_n); } else { - newval = BOOLEAN_OPTVAL(new_n == 0 ? kFalse : (new_n >= 1 ? kTrue : kNone)); + newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n)); } } else if (!hidden && is_string && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string @@ -1875,8 +1875,7 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); } } - value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) - : BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); + value = (flags & P_NUM) ? NUMBER_OPTVAL((OptInt)n) : BOOLEAN_OPTVAL(TRISTATE_FROM_INT(n)); } else if ((flags & P_STRING) || is_tty_option(option)) { // Avoid setting string option to a boolean or a special value. if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { -- cgit From 5df4fdf253f9c9cc35f9f5f16c6d0ba9d87b4c71 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 8 Oct 2023 22:13:15 +0600 Subject: refactor(options)!: make OptionSet `v:` values use typval BREAKING CHANGE: This breaks the OptionSet autocommand, as the `v:` values associated with it (`v:option_new`, `v:option_old`, `v:option_oldlocal` and `v:option_oldglobal`) are now the same type as the option, instead of all option values being converted to strings. --- src/nvim/eval/vars.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 4b314ca338..ed400b2ee9 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -20,6 +20,7 @@ #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" @@ -1896,6 +1897,45 @@ static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, boo return value; } +/// Convert an option value to typval. +/// +/// @param[in] value Option value to convert. +/// +/// @return OptVal converted to typval. +typval_T optval_as_tv(OptVal value) +{ + typval_T rettv = { .v_type = VAR_SPECIAL, .vval = { .v_special = kSpecialVarNull } }; + + switch (value.type) { + case kOptValTypeNil: + break; + case kOptValTypeBoolean: + switch (value.data.boolean) { + case kTrue: + rettv.v_type = VAR_BOOL; + rettv.vval.v_bool = kBoolVarTrue; + break; + case kFalse: + rettv.v_type = VAR_BOOL; + rettv.vval.v_bool = kBoolVarFalse; + break; + case kNone: + break; // return v:null for None boolean value + } + break; + case kOptValTypeNumber: + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = value.data.number; + break; + case kOptValTypeString: + rettv.v_type = VAR_STRING; + rettv.vval.v_string = value.data.string.data; + break; + } + + return rettv; +} + /// 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) { -- cgit From acc646ad8fc3ef11fcc63b69f3d8484e4a91accd Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Sep 2023 14:58:48 +0200 Subject: refactor: the long goodbye long is 32 bits on windows, while it is 64 bits on other architectures. This makes the type suboptimal for a codebase meant to be cross-platform. Replace it with more appropriate integer types. --- src/nvim/eval/vars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index ed400b2ee9..33256b78e1 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1088,13 +1088,13 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ /// Unlet one item or a range of items from a list. /// Return OK or FAIL. -static void tv_list_unlet_range(list_T *const l, listitem_T *const li_first, const long n1_arg, - const bool has_n2, const long n2) +static void tv_list_unlet_range(list_T *const l, listitem_T *const li_first, const int n1_arg, + const bool has_n2, const int n2) { assert(l != NULL); // Delete a range of List items. listitem_T *li_last = li_first; - long n1 = n1_arg; + int n1 = n1_arg; while (true) { listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last); n1++; -- cgit From cd63a9addd6e1114c3524fa041ece560550cfe7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 10 Nov 2023 08:39:21 +0800 Subject: refactor: change some xstrndup() and xstrnsave() to xmemdupz() (#25959) When the given length is exactly the number of bytes to copy, xmemdupz() makes the intention clearer. --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 33256b78e1..ed79d8a681 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -274,7 +274,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) p++; text_indent_len++; } - text_indent = xstrnsave(theline, (size_t)text_indent_len); + text_indent = xmemdupz(theline, (size_t)text_indent_len); } // with "trim": skip the indent matching the first line if (text_indent != NULL) { -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/eval/vars.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index ed79d8a681..8cc3903f7a 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -1,6 +1,3 @@ -// 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 -- cgit From bb4b4576e384c71890b4df4fa4f1ae76fad3a59d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 16 Nov 2023 10:55:54 +0800 Subject: refactor: iwyu (#26062) --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 8cc3903f7a..3fd33720c9 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -17,7 +17,6 @@ #include "nvim/eval/encode.h" #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" @@ -34,6 +33,7 @@ #include "nvim/message.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/os/os.h" #include "nvim/search.h" -- cgit From a827003e3052c6d9ee7bdb71518182e9bd76317d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 25 Nov 2023 11:32:32 +0100 Subject: build: rework IWYU mapping files Create mapping to most of the C spec and some POSIX specific functions. This is more robust than relying files shipped with IWYU. --- src/nvim/eval/vars.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 3fd33720c9..cf02bbe224 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "nvim/ascii.h" #include "nvim/autocmd.h" -- cgit From 8b428ca8b79ebb7b36c3e403ff3bcb6924a635a6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 16:00:21 +0100 Subject: build(IWYU): fix includes for func_attr.h --- src/nvim/eval/vars.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index cf02bbe224..9464e71b87 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -25,6 +25,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" -- cgit From 6c14ae6bfaf51415b555e9a6b85d1d280976358d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:27:32 +0100 Subject: refactor: rename types.h to types_defs.h --- src/nvim/eval/vars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 9464e71b87..75274d41af 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -40,7 +40,7 @@ #include "nvim/os/os.h" #include "nvim/search.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/vim.h" #include "nvim/window.h" -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/eval/vars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval/vars.c') diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 75274d41af..2968f75f4d 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -9,7 +9,7 @@ #include #include -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" @@ -30,7 +30,7 @@ #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/hashtab.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/ops.h" @@ -41,7 +41,7 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/types_defs.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit