aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c185
1 files changed, 120 insertions, 65 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0cad5fd6c1..cccf1e50ff 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -736,7 +736,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (s == NULL || *s == NUL) {
return FAIL;
}
- if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
+ if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
return FAIL;
}
@@ -746,7 +746,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (s == NULL || *s == NUL) {
return FAIL;
}
- if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL,
+ if (call_func(s, -1, rettv, argc, argv, NULL,
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
return FAIL;
}
@@ -760,7 +760,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv,
if (eval1_emsg(&s, rettv, true) == FAIL) {
return FAIL;
}
- if (*s != NUL) { // check for trailing chars after expr
+ if (*skipwhite(s) != NUL) { // check for trailing chars after expr
tv_clear(rettv);
emsgf(_(e_invexpr2), s);
return FAIL;
@@ -1679,7 +1679,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
arg = (const char *)find_name_end((char_u *)arg, NULL, NULL,
FNE_INCL_BR | FNE_CHECK_START);
if (!ascii_iswhite(*arg) && !ends_excmd(*arg)) {
- emsg_severe = TRUE;
+ emsg_severe = true;
EMSG(_(e_trailing));
break;
}
@@ -1692,7 +1692,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
/* This is mainly to keep test 49 working: when expanding
* curly braces fails overrule the exception error message. */
if (len < 0 && !aborting()) {
- emsg_severe = TRUE;
+ emsg_severe = true;
EMSG2(_(e_invarg2), arg);
break;
}
@@ -2007,7 +2007,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
* expression evaluation has been cancelled due to an
* aborting error, an interrupt, or an exception. */
if (!aborting() && !quiet) {
- emsg_severe = TRUE;
+ emsg_severe = true;
EMSG2(_(e_invarg2), name);
return NULL;
}
@@ -2086,6 +2086,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv,
tv_clear(&var1);
return NULL;
}
+ p = skipwhite(p);
}
// Optionally get the second index [ :expr].
@@ -2674,6 +2675,7 @@ void ex_lockvar(exarg_T *eap)
static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
{
char_u *arg = argstart;
+ char_u *name_end;
bool error = false;
lval_T lv;
@@ -2686,43 +2688,43 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep)
return;
}
os_unsetenv(name);
- arg = skipwhite(arg);
- continue;
- }
-
- // Parse the name and find the end.
- char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true,
- eap->skip || error,
- 0, FNE_CHECK_START);
- if (lv.ll_name == NULL) {
- error = true; // error, but continue parsing.
- }
- if (name_end == NULL || (!ascii_iswhite(*name_end)
- && !ends_excmd(*name_end))) {
- if (name_end != NULL) {
- emsg_severe = TRUE;
- EMSG(_(e_trailing));
+ name_end = arg;
+ } else {
+ // Parse the name and find the end.
+ name_end = get_lval(arg, NULL, &lv, true, eap->skip || error,
+ 0, FNE_CHECK_START);
+ if (lv.ll_name == NULL) {
+ error = true; // error, but continue parsing.
+ }
+ if (name_end == NULL
+ || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) {
+ if (name_end != NULL) {
+ emsg_severe = true;
+ EMSG(_(e_trailing));
+ }
+ if (!(eap->skip || error)) {
+ clear_lval(&lv);
+ }
+ break;
}
- if (!(eap->skip || error))
- clear_lval(&lv);
- break;
- }
- if (!error && !eap->skip) {
- if (eap->cmdidx == CMD_unlet) {
- if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL)
- error = TRUE;
- } else {
- if (do_lock_var(&lv, name_end, deep,
- eap->cmdidx == CMD_lockvar) == FAIL) {
- error = true;
+ if (!error && !eap->skip) {
+ if (eap->cmdidx == CMD_unlet) {
+ if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL) {
+ error = true;
+ }
+ } else {
+ if (do_lock_var(&lv, name_end, deep,
+ eap->cmdidx == CMD_lockvar) == FAIL) {
+ error = true;
+ }
}
}
- }
-
- if (!eap->skip)
- clear_lval(&lv);
+ if (!eap->skip) {
+ clear_lval(&lv);
+ }
+ }
arg = skipwhite(name_end);
} while (!ends_excmd(*arg));
@@ -2992,7 +2994,6 @@ char_u *get_user_var_name(expand_T *xp, int idx)
static size_t tdone;
static size_t vidx;
static hashitem_T *hi;
- hashtab_T *ht;
if (idx == 0) {
gdone = bdone = wdone = vidx = 0;
@@ -3013,7 +3014,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
}
// b: variables
- ht = &curbuf->b_vars->dv_hashtab;
+ // In cmdwin, the alternative buffer should be used.
+ hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
+ ? &prevwin->w_buffer->b_vars->dv_hashtab
+ : &curbuf->b_vars->dv_hashtab;
if (bdone < ht->ht_used) {
if (bdone++ == 0)
hi = ht->ht_array;
@@ -3025,7 +3029,10 @@ char_u *get_user_var_name(expand_T *xp, int idx)
}
// w: variables
- ht = &curwin->w_vars->dv_hashtab;
+ // In cmdwin, the alternative window should be used.
+ ht = (cmdwin_type != 0 && get_cmdline_type() == NUL)
+ ? &prevwin->w_vars->dv_hashtab
+ : &curwin->w_vars->dv_hashtab;
if (wdone < ht->ht_used) {
if (wdone++ == 0)
hi = ht->ht_array;
@@ -3800,8 +3807,9 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
*/
for (;; ) {
op = **arg;
- if (op != '*' && op != '/' && op != '%')
+ if (op != '*' && op != '/' && op != '%') {
break;
+ }
if (evaluate) {
if (rettv->v_type == VAR_FLOAT) {
@@ -3903,6 +3911,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string)
// (expression) nested expression
// [expr, expr] List
// {key: val, key: val} Dictionary
+// #{key: val, key: val} Dictionary with literal keys
//
// Also handle:
// ! in front logical NOT
@@ -4010,11 +4019,21 @@ static int eval7(
case '[': ret = get_list_tv(arg, rettv, evaluate);
break;
+ // Dictionary: #{key: val, key: val}
+ case '#':
+ if ((*arg)[1] == '{') {
+ (*arg)++;
+ ret = dict_get_tv(arg, rettv, evaluate, true);
+ } else {
+ ret = NOTDONE;
+ }
+ break;
+
// Lambda: {arg, arg -> expr}
- // Dictionary: {key: val, key: val}
+ // Dictionary: {'key': val, 'key': val}
case '{': ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) {
- ret = dict_get_tv(arg, rettv, evaluate);
+ ret = dict_get_tv(arg, rettv, evaluate, false);
}
break;
@@ -4516,7 +4535,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv,
static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
- char_u *name;
unsigned int extra = 0;
/*
@@ -4524,11 +4542,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
*/
for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
- ++p;
- /* A "\<x>" form occupies at least 4 characters, and produces up
- * to 6 characters: reserve space for 2 extra */
- if (*p == '<')
- extra += 2;
+ p++;
+ // A "\<x>" form occupies at least 4 characters, and produces up
+ // to 21 characters (3 * 6 for the char and 3 for a modifier):
+ // reserve space for 18 extra.
+ // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+ if (*p == '<') {
+ extra += 18;
+ }
}
}
@@ -4547,7 +4568,8 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
* Copy the string into allocated memory, handling backslashed
* characters.
*/
- name = xmalloc(p - *arg + extra);
+ const int len = (int)(p - *arg + extra);
+ char_u *name = xmalloc(len);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = name;
@@ -4614,6 +4636,9 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
if (extra != 0) {
name += extra;
+ if (name >= rettv->vval.v_string + len) {
+ iemsg("get_string_tv() used more space than allocated");
+ }
break;
}
FALLTHROUGH;
@@ -5339,11 +5364,31 @@ static inline bool set_ref_dict(dict_T *dict, int copyID)
return false;
}
-/*
- * Allocate a variable for a Dictionary and fill it from "*arg".
- * Return OK or FAIL. Returns NOTDONE for {expr}.
- */
-static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate)
+
+// Get the key for *{key: val} into "tv" and advance "arg".
+// Return FAIL when there is no valid key.
+static int get_literal_key(char_u **arg, typval_T *tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char_u *p;
+
+ if (!ASCII_ISALNUM(**arg) && **arg != '_' && **arg != '-') {
+ return FAIL;
+ }
+ for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {
+ }
+ tv->v_type = VAR_STRING;
+ tv->vval.v_string = vim_strnsave(*arg, (int)(p - *arg));
+
+ *arg = skipwhite(p);
+ return OK;
+}
+
+// Allocate a variable for a Dictionary and fill it from "*arg".
+// "literal" is true for *{key: val}
+// Return OK or FAIL. Returns NOTDONE for {expr}.
+static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate,
+ bool literal)
{
dict_T *d = NULL;
typval_T tvkey;
@@ -5364,7 +5409,7 @@ static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate)
if (eval1(&start, &tv, false) == FAIL) { // recursive!
return FAIL;
}
- if (*start == '}') {
+ if (*skipwhite(start) == '}') {
return NOTDONE;
}
}
@@ -5377,7 +5422,9 @@ static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate)
*arg = skipwhite(*arg + 1);
while (**arg != '}' && **arg != NUL) {
- if (eval1(arg, &tvkey, evaluate) == FAIL) { // recursive!
+ if ((literal
+ ? get_literal_key(arg, &tvkey)
+ : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive!
goto failret;
}
if (**arg != ':') {
@@ -6960,9 +7007,10 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append,
if (!append && lnum <= curbuf->b_ml.ml_line_count) {
// Existing line, replace it.
+ int old_len = (int)STRLEN(ml_get(lnum));
if (u_savesub(lnum) == OK
&& ml_replace(lnum, (char_u *)line, true) == OK) {
- changed_bytes(lnum, 0);
+ inserted_bytes(lnum, 0, old_len, STRLEN(line));
if (is_curbuf && lnum == curwin->w_cursor.lnum) {
check_cursor_col();
}
@@ -7073,7 +7121,7 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
do {
size_t dir_len;
const char *dir;
- iter = vim_env_iter(':', dirs, iter, &dir, &dir_len);
+ iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len);
if (dir != NULL && dir_len > 0) {
char *dir_with_nvim = xmemdupz(dir, dir_len);
dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true);
@@ -7261,7 +7309,7 @@ bool callback_call(Callback *const callback, const int argcount_in,
}
int dummy;
- return call_func(name, (int)STRLEN(name), rettv, argcount_in, argvars_in,
+ return call_func(name, -1, rettv, argcount_in, argvars_in,
NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true, partial, NULL);
}
@@ -8483,7 +8531,7 @@ handle_subscript(
} else {
s = (char_u *)"";
}
- ret = get_func_tv(s, lua ? slen : (int)STRLEN(s), rettv, (char_u **)arg,
+ ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, pt, selfdict);
@@ -9044,7 +9092,7 @@ static void set_var_const(const char *name, const size_t name_len,
}
if (is_const) {
- v->di_tv.v_lock |= VAR_LOCKED;
+ tv_item_lock(&v->di_tv, 1, true);
}
}
@@ -10381,10 +10429,13 @@ void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
list_T *args = tv_list_alloc(1);
tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
- *rettv = eval_call_provider(name, "eval", args);
+ *rettv = eval_call_provider(name, "eval", args, false);
}
-typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
+/// @param discard Clears the value returned by the provider and returns
+/// an empty typval_T.
+typval_T eval_call_provider(char *provider, char *method, list_T *arguments,
+ bool discard)
{
if (!eval_has_provider(provider)) {
emsgf("E319: No \"%s\" provider found. Run \":checkhealth provider\"",
@@ -10443,6 +10494,10 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
provider_call_nesting--;
assert(provider_call_nesting >= 0);
+ if (discard) {
+ tv_clear(&rettv);
+ }
+
return rettv;
}