diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 542 |
1 files changed, 333 insertions, 209 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8cf077ec19..76deedfad0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1700,12 +1700,13 @@ static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) } error = TRUE; } else { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL) - error = TRUE; - else { - /* handle d.key, l[idx], f(expr) */ + } + if (get_var_tv(name, len, &tv, NULL, true, false) == FAIL) { + error = true; + } else { + // handle d.key, l[idx], f(expr) arg_subsc = arg; if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL) error = TRUE; @@ -2176,10 +2177,10 @@ get_lval ( if (len == -1) clear_tv(&var1); break; - } - /* existing variable, need to check if it can be changed */ - else if (var_check_ro(lp->ll_di->di_flags, name)) + } else if (var_check_ro(lp->ll_di->di_flags, name, false)) { + // existing variable, need to check if it can be changed return NULL; + } if (len == -1) clear_tv(&var1); @@ -2274,11 +2275,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch if (op != NULL && *op != '=') { typval_T tv; - /* handle +=, -= and .= */ + // handle +=, -= and .= + di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, TRUE, FALSE) == OK) { - if (tv_op(&tv, rettv, op) == OK) - set_var(lp->ll_name, &tv, FALSE); + &tv, &di, true, false) == OK) { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, false) && + !tv_check_lock(di->di_tv.v_lock, lp->ll_name, false))) + && tv_op(&tv, rettv, op) == OK) { + set_var(lp->ll_name, &tv, false); + } clear_tv(&tv); } } else @@ -2286,16 +2292,17 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch *endp = cc; } } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name)) - ; - else if (lp->ll_range) { + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, false)) { + } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + for (listitem_T *ri = rettv->vval.v_list->lv_first; + ri != NULL && ll_li != NULL; ) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return; } ri = ri->li_next; @@ -2891,9 +2898,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) ret = FAIL; *name_end = cc; } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name)) + && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, false)) || (lp->ll_dict != NULL - && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name))) { + && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, false))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2902,7 +2909,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name)) { + if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, false)) { return false; } ll_li = li; @@ -2975,9 +2982,9 @@ int do_unlet(char_u *name, int forceit) hi = hash_find(ht, varname); if (!HASHITEM_EMPTY(hi)) { di = HI2DI(hi); - if (var_check_fixed(di->di_flags, name) - || var_check_ro(di->di_flags, name) - || tv_check_lock(d->dv_lock, name)) { + if (var_check_fixed(di->di_flags, name, false) + || var_check_ro(di->di_flags, name, false) + || tv_check_lock(d->dv_lock, name, false)) { return FAIL; } typval_T oldtv; @@ -3045,12 +3052,13 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) li = li->li_next; ++lp->ll_n1; } - } else if (lp->ll_list != NULL) - /* (un)lock a List item. */ + } else if (lp->ll_list != NULL) { + // (un)lock a List item. item_lock(&lp->ll_li->li_tv, deep, lock); - else - /* un(lock) a Dictionary item. */ + } else { + // (un)lock a Dictionary item. item_lock(&lp->ll_di->di_tv, deep, lock); + } return ret; } @@ -3572,9 +3580,10 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) type = TYPE_SEQUAL; break; case 'i': if (p[1] == 's') { - if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') + if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') { len = 5; - if (!vim_isIDc(p[len])) { + } + if (!isalnum(p[len]) && p[len] != '_') { type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL; type_is = TRUE; } @@ -4249,7 +4258,7 @@ static int eval7( ret = FAIL; } } else if (evaluate) { - ret = get_var_tv(s, len, rettv, true, false); + ret = get_var_tv(s, len, rettv, NULL, true, false); } else { ret = OK; } @@ -7288,8 +7297,8 @@ static struct fst { { "maparg", 1, 4, f_maparg }, { "mapcheck", 1, 3, f_mapcheck }, { "match", 2, 4, f_match }, - { "matchadd", 2, 4, f_matchadd }, - { "matchaddpos", 2, 4, f_matchaddpos }, + { "matchadd", 2, 5, f_matchadd }, + { "matchaddpos", 2, 5, f_matchaddpos }, { "matcharg", 1, 1, f_matcharg }, { "matchdelete", 1, 1, f_matchdelete }, { "matchend", 2, 4, f_matchend }, @@ -7341,10 +7350,10 @@ static struct fst { { "setcharsearch", 1, 1, f_setcharsearch }, { "setcmdpos", 1, 1, f_setcmdpos }, { "setline", 2, 2, f_setline }, - { "setloclist", 2, 3, f_setloclist }, + { "setloclist", 2, 4, f_setloclist }, { "setmatches", 1, 1, f_setmatches }, { "setpos", 2, 2, f_setpos }, - { "setqflist", 1, 2, f_setqflist }, + { "setqflist", 1, 3, f_setqflist }, { "setreg", 2, 3, f_setreg }, { "settabvar", 3, 3, f_settabvar }, { "settabwinvar", 4, 4, f_settabwinvar }, @@ -7363,7 +7372,7 @@ static struct fst { { "sqrt", 1, 1, f_sqrt }, { "str2float", 1, 1, f_str2float }, { "str2nr", 1, 2, f_str2nr }, - { "strchars", 1, 1, f_strchars }, + { "strchars", 1, 2, f_strchars }, { "strdisplaywidth", 1, 2, f_strdisplaywidth }, { "strftime", 1, 2, f_strftime }, { "stridx", 2, 3, f_stridx }, @@ -7881,7 +7890,8 @@ static void f_add(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 1; /* Default: Failed */ if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("add() argument"))) { + && !tv_check_lock(l->lv_lock, + (char_u *)N_("add() argument"), true)) { list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } @@ -9156,9 +9166,10 @@ static void f_exists(typval_T *argvars, typval_T *rettv) name = p; len = get_name_len(&p, &tofree, TRUE, FALSE); if (len > 0) { - if (tofree != NULL) + if (tofree != NULL) { name = tofree; - n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK); + } + n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { /* handle d.key, l[idx], f(expr) */ n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); @@ -9256,7 +9267,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) hashitem_T *hi2; int todo; bool watched = is_watched(d1); - char *arg_errmsg = N_("extend() argument"); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); todo = (int)d2->dv_hashtab.ht_used; for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { @@ -9290,8 +9301,8 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { typval_T oldtv; - if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg)) - || var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg))) { + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di1->di_flags, arg_errmsg, true)) { break; } @@ -9317,7 +9328,7 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) */ static void f_extend(typval_T *argvars, typval_T *rettv) { - char *arg_errmsg = N_("extend() argument"); + char_u *arg_errmsg = (char_u *)N_("extend() argument"); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { list_T *l1, *l2; @@ -9327,7 +9338,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, (char_u *)_(arg_errmsg)) + if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, true) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); @@ -9357,7 +9368,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv) d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, (char_u *)_(arg_errmsg)) + if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, true) && d2 != NULL) { /* Check the third argument. */ if (argvars[2].v_type != VAR_UNKNOWN) { @@ -9503,19 +9514,19 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int rem; int todo; char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); - char *arg_errmsg = (map ? N_("map() argument") - : N_("filter() argument")); + char_u *arg_errmsg = (char_u *)(map ? N_("map() argument") + : N_("filter() argument")); int save_did_emsg; int idx = 0; if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))) { + || (!map && tv_check_lock(l->lv_lock, arg_errmsg, true))) { return; } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))) { + || (!map && tv_check_lock(d->dv_lock, arg_errmsg, true))) { return; } } else { @@ -9549,8 +9560,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) di = HI2DI(hi); if (map - && (tv_check_lock(di->di_tv.v_lock, (char_u *)_(arg_errmsg)) - || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg)))) { + && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true))) { break; } @@ -9560,8 +9571,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (r == FAIL || did_emsg) break; if (!map && rem) { - if (var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) - || var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { + if (var_check_fixed(di->di_flags, arg_errmsg, true) + || var_check_ro(di->di_flags, arg_errmsg, true)) { break; } dictitem_remove(d, di); @@ -9573,7 +9584,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_type = VAR_NUMBER; for (li = l->lv_first; li != NULL; li = nli) { - if (map && tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) { + if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, true)) { break; } nli = li->li_next; @@ -10438,6 +10449,14 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv) dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); dict_add_nr_str(dict, "id", (long)cur->id, NULL); + + if (cur->conceal_char) { + char_u buf[MB_MAXBYTES + 1]; + + buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; + dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + } + list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } @@ -10609,9 +10628,10 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv) varname = get_tv_string_chk(&argvars[1]); tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { - /* Set tp to be our tabpage, temporarily. Also set the window to the - * first window in the tabpage, otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, tp->tp_firstwin, tp, TRUE) == OK) { + // Set tp to be our tabpage, temporarily. Also set the window to the + // first window in the tabpage, otherwise the window is not valid. + win_T *window = tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; + if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) { // look up the variable // Let gettabvar({nr}, "") return the "t:" dictionary. v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, FALSE); @@ -10704,11 +10724,11 @@ getwinvar ( int off /* 1 for gettabwinvar() */ ) { - win_T *win, *oldcurwin; - char_u *varname; - dictitem_T *v; - tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; + win_T *win, *oldcurwin; + char_u *varname; + dictitem_T *v; + tabpage_T *tp = NULL; + tabpage_T *oldtabpage = NULL; bool done = false; if (off == 1) @@ -10723,12 +10743,16 @@ getwinvar ( rettv->vval.v_string = NULL; if (win != NULL && varname != NULL) { - /* Set curwin to be our win, temporarily. Also set the tabpage, - * otherwise the window is not valid. */ - if (switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE) == OK) { - if (*varname == '&') { /* window-local-option */ - if (get_option_tv(&varname, rettv, 1) == OK) + // Set curwin to be our win, temporarily. Also set the tabpage, + // otherwise the window is not valid. Only do this when needed, + // autocommands get blocked. + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { + if (*varname == '&') { // window-local-option + if (get_option_tv(&varname, rettv, 1) == OK) { done = true; + } } else { // Look up the variable. // Let getwinvar({nr}, "") return the "w:" dictionary. @@ -10740,8 +10764,10 @@ getwinvar ( } } - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); + if (need_switch_win) { + // restore previous notion of curwin + restore_win(oldcurwin, oldtabpage, true); + } } if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) @@ -10956,6 +10982,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) #if !defined(UNIX) "system", // TODO(SplinterOfChaos): This IS defined for UNIX! #endif + "tablineat", "tag_binary", "tag_old_static", "termresponse", @@ -11013,8 +11040,6 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); - } else if (STRICMP(name, "gui_running") == 0) { - n = ui_rgb_attached(); } } @@ -11469,14 +11494,18 @@ static void f_insert(typval_T *argvars, typval_T *rettv) list_T *l; int error = FALSE; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("insert() argument"))) { - if (argvars[2].v_type != VAR_UNKNOWN) + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("insert() argument"), true)) { + if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + } + if (error) { + // type error; errmsg already given + return; + } if (before == l->lv_len) item = NULL; @@ -12499,7 +12528,8 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ int prio = 10; /* default priority */ int id = -1; - int error = FALSE; + int error = false; + char_u *conceal_char = NULL; rettv->vval.v_number = -1; @@ -12507,17 +12537,31 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv) return; if (argvars[2].v_type != VAR_UNKNOWN) { prio = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + if (argvars[3].v_type != VAR_UNKNOWN) { id = get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) { + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", false); + } + } + } } - if (error == TRUE) + if (error == true) { return; + } if (id >= 1 && id <= 3) { EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL @@ -12545,12 +12589,24 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ int error = false; int prio = 10; int id = -1; + char_u *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + prio = get_tv_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = get_tv_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + if (dict_find(argvars[4].vval.v_dict, + (char_u *)"conceal", -1) != NULL) { + conceal_char = get_dict_string(argvars[4].vval.v_dict, + (char_u *)"conceal", false); + } } + } } if (error == true) { return; @@ -12562,7 +12618,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l); + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); } /* @@ -13930,20 +13987,20 @@ static void f_remove(typval_T *argvars, typval_T *rettv) char_u *key; dict_T *d; dictitem_T *di; - char *arg_errmsg = N_("remove() argument"); + char_u *arg_errmsg = (char_u *)N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) { + } else if ((d = argvars[0].vval.v_dict) != NULL + && !tv_check_lock(d->dv_lock, arg_errmsg, true)) { key = get_tv_string_chk(&argvars[1]); if (key != NULL) { di = dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); - } else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg)) - && !var_check_ro(di->di_flags, (char_u *)_(arg_errmsg))) { + } else if (!var_check_fixed(di->di_flags, arg_errmsg, true) + && !var_check_ro(di->di_flags, arg_errmsg, true)) { *rettv = di->di_tv; init_tv(&di->di_tv); dictitem_remove(d, di); @@ -13953,11 +14010,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv) } } } - } else if (argvars[0].v_type != VAR_LIST) + } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) { - int error = FALSE; + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, arg_errmsg, true)) { + int error = (int)false; idx = get_tv_number_chk(&argvars[1], &error); if (error) @@ -14231,10 +14288,11 @@ static void f_reverse(typval_T *argvars, typval_T *rettv) list_T *l; listitem_T *li, *ni; - if (argvars[0].v_type != VAR_LIST) + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("reverse() argument"))) { + } else if ((l = argvars[0].vval.v_list) != NULL + && !tv_check_lock(l->lv_lock, + (char_u *)N_("reverse() argument"), true)) { li = l->lv_last; l->lv_first = l->lv_last = NULL; l->lv_len = 0; @@ -15183,14 +15241,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv) appended_lines_mark(lcount, added); } - -/* - * Used by "setqflist()" and "setloclist()" functions - */ -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv) +/// Create quickfix/location list from VimL values +/// +/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid +/// list_arg, action_arg and title_arg arguments in which case errors out, +/// including VAR_UNKNOWN parameters. +/// +/// @param[in,out] wp Window to create location list for. May be NULL in +/// which case quickfix list will be created. +/// @param[in] list_arg Quickfix list contents. +/// @param[in] action_arg Action to perform: append to an existing list, +/// replace its content or create a new one. +/// @param[in] title_arg New list title. Defaults to caller function name. +/// @param[out] rettv Return value: 0 in case of success, -1 otherwise. +static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, + typval_T *title_arg, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) { char_u *act; int action = ' '; + char_u *title = NULL; rettv->vval.v_number = -1; @@ -15199,7 +15269,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, else { list_T *l = list_arg->vval.v_list; - if (action_arg->v_type == VAR_STRING) { + if (action_arg->v_type != VAR_UNKNOWN) { act = get_tv_string_chk(action_arg); if (act == NULL) return; /* type error; errmsg already given */ @@ -15207,9 +15277,20 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, action = *act; } - if (l != NULL && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) + if (title_arg->v_type == VAR_STRING) { + title = get_tv_string_chk(title_arg); + if (!title) { + return; // type error; errmsg already given + } + } + + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } + + if (l && set_errorlist(wp, l, action, title) == OK) { rettv->vval.v_number = 0; + } } } @@ -15223,8 +15304,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = -1; win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); + if (win != NULL) { + set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); + } } /* @@ -15270,8 +15352,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) int i = 0; char_u buf[5]; dictitem_T *di; - d = li->li_tv.vval.v_dict; + d = li->li_tv.vval.v_dict; if (dict_find(d, (char_u *)"pattern", -1) == NULL) { if (s == NULL) { s = list_alloc(); @@ -15296,15 +15378,19 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv) } } + char_u *group = get_dict_string(d, (char_u *)"group", false); + int priority = get_dict_number(d, (char_u *)"priority"); + int id = get_dict_number(d, (char_u *)"id"); + char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL + ? get_dict_string(d, (char_u *)"conceal", + false) + : NULL; if (i == 0) { - match_add(curwin, get_dict_string(d, (char_u *)"group", false), + match_add(curwin, group, get_dict_string(d, (char_u *)"pattern", false), - (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id"), NULL); + priority, id, NULL, conceal); } else { - match_add(curwin, get_dict_string(d, (char_u *)"group", false), - NULL, (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id"), s); + match_add(curwin, group, NULL, priority, id, s, conceal); list_unref(s); s = NULL; } @@ -15356,7 +15442,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); + set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv); } /* @@ -15530,26 +15616,32 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname = get_tv_string_chk(&argvars[off + 1]); varp = &argvars[off + 2]; - if (win != NULL && varname != NULL && varp != NULL - && switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == OK) { - if (*varname == '&') { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, TRUE); - xfree(winvarname); + if (win != NULL && varname != NULL && varp != NULL) { + bool need_switch_win = tp != curtab || win != curwin; + if (!need_switch_win + || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { + if (*varname == '&') { + long numval; + char_u *strval; + int error = false; + + ++varname; + numval = get_tv_number_chk(varp, &error); + strval = get_tv_string_buf_chk(varp, nbuf); + if (!error && strval != NULL) { + set_option_value(varname, numval, strval, OPT_LOCAL); + } + } else { + winvarname = xmalloc(STRLEN(varname) + 3); + STRCPY(winvarname, "w:"); + STRCPY(winvarname + 2, varname); + set_var(winvarname, varp, true); + xfree(winvarname); + } + } + if (need_switch_win) { + restore_win(save_curwin, save_curtab, true); } - restore_win(save_curwin, save_curtab, TRUE); } } @@ -15768,8 +15860,12 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) { + if (l == NULL || + tv_check_lock(l->lv_lock, + (char_u *)(sort + ? N_("sort() argument") + : N_("uniq() argument")), + true)) { return; } rettv->vval.v_list = l; @@ -16240,13 +16336,23 @@ static void f_strlen(typval_T *argvars, typval_T *rettv) static void f_strchars(typval_T *argvars, typval_T *rettv) { char_u *s = get_tv_string(&argvars[0]); + int skipcc = 0; varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); - while (*s != NUL) { - mb_cptr2char_adv(&s); - ++len; + if (argvars[1].v_type != VAR_UNKNOWN) { + skipcc = get_tv_number_chk(&argvars[1], NULL); + } + if (skipcc < 0 || skipcc > 1) { + EMSG(_(e_invarg)); + } else { + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; } - rettv->vval.v_number = len; } /* @@ -18048,23 +18154,23 @@ void set_vim_var_list(int idx, list_T *val) } /// Set Dictionary v: variable to "val". -void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL +void set_vim_var_dict(int idx, dict_T *val) { dict_unref(vimvars[idx].vv_dict); + vimvars[idx].vv_dict = val; - // Set readonly - int todo = (int)val->dv_hashtab.ht_used; - for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { - if (HASHITEM_EMPTY(hi)) { - continue; + if (val != NULL) { + ++val->dv_refcount; + // Set readonly + size_t todo = val->dv_hashtab.ht_used; + for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { + if (HASHITEM_EMPTY(hi)) { + continue; + } + --todo; + HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } - - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } - - vimvars[idx].vv_dict = val; - ++val->dv_refcount; } /* @@ -18182,10 +18288,11 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) static int get_var_tv ( char_u *name, - int len, /* length of "name" */ - typval_T *rettv, /* NULL when only checking existence */ - int verbose, /* may give error message */ - int no_autoload /* do not use script autoloading */ + int len, // length of "name" + typval_T *rettv, // NULL when only checking existence + dictitem_T **dip, // non-NULL when typval's dict item is needed + int verbose, // may give error message + int no_autoload // do not use script autoloading ) { int ret = OK; @@ -18211,8 +18318,12 @@ get_var_tv ( */ else { v = find_var(name, NULL, no_autoload); - if (v != NULL) + if (v != NULL) { tv = &v->di_tv; + if (dip != NULL) { + *dip = v; + } + } } if (tv == NULL) { @@ -18603,6 +18714,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d) hashitem_T *hi; *d = NULL; + if (name[0] == NUL) { + return NULL; + } if (name[1] != ':') { // name has implicit scope if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) { @@ -18652,6 +18766,7 @@ end: } // Find the hashtab used for a variable name. +// Return NULL if the name is not valid. // Set "varname" to the start of name without ':'. static hashtab_T *find_var_ht(uint8_t *name, uint8_t **varname) { @@ -18875,10 +18990,11 @@ set_var ( return; if (v != NULL) { - /* existing variable, need to clear the value */ - if (var_check_ro(v->di_flags, name) - || tv_check_lock(v->di_tv.v_lock, name)) + // existing variable, need to clear the value + if (var_check_ro(v->di_flags, name, false) + || tv_check_lock(v->di_tv.v_lock, name, false)) { return; + } if (v->di_tv.v_type != tv->v_type && !((v->di_tv.v_type == VAR_STRING || v->di_tv.v_type == VAR_NUMBER) @@ -18893,10 +19009,8 @@ set_var ( return; } - /* - * Handle setting internal v: variables separately: we don't change - * the type. - */ + // Handle setting internal v: variables separately where needed to + // prevent changing the type. if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); @@ -18907,9 +19021,8 @@ set_var ( v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } - } else if (v->di_tv.v_type != VAR_NUMBER) - EMSG2(_(e_intern2), "set_var()"); - else { + return; + } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); if (STRCMP(varname, "searchforward") == 0) set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); @@ -18917,8 +19030,10 @@ set_var ( no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } + return; + } else if (v->di_tv.v_type != tv->v_type) { + EMSG2(_(e_intern2), "set_var()"); } - return; } if (watched) { @@ -18963,34 +19078,31 @@ set_var ( } } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is read-only. - * Also give an error message. - */ -static int var_check_ro(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is read-only. +// Also give an error message. +static bool var_check_ro(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_RO) { - EMSG2(_(e_readonlyvar), name); - return TRUE; + EMSG2(_(e_readonlyvar), use_gettext ? (char_u *)_(name) : name); + return true; } if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - EMSG2(_(e_readonlysbx), name); - return TRUE; + EMSG2(_(e_readonlysbx), use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } -/* - * Return TRUE if di_flags "flags" indicates variable "name" is fixed. - * Also give an error message. - */ -static int var_check_fixed(int flags, char_u *name) +// Return true if di_flags "flags" indicates variable "name" is fixed. +// Also give an error message. +static bool var_check_fixed(int flags, char_u *name, bool use_gettext) { if (flags & DI_FLAGS_FIX) { - EMSG2(_("E795: Cannot delete variable %s"), name); - return TRUE; + EMSG2(_("E795: Cannot delete variable %s"), + use_gettext ? (char_u *)_(name) : name); + return true; } - return FALSE; + return false; } /* @@ -19038,23 +19150,28 @@ static int valid_varname(char_u *varname) return TRUE; } -/* - * Return TRUE if typeval "tv" is set to be locked (immutable). - * Also give an error message, using "name". - */ -static int tv_check_lock(int lock, char_u *name) +// Return true if typeval "tv" is set to be locked (immutable). +// Also give an error message, using "name" or _("name") when use_gettext is +// true. +static bool tv_check_lock(int lock, char_u *name, bool use_gettext) { if (lock & VAR_LOCKED) { EMSG2(_("E741: Value is locked: %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } if (lock & VAR_FIXED) { EMSG2(_("E742: Cannot change value of %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; + name == NULL + ? (char_u *)_("Unknown") + : use_gettext ? (char_u *)_(name) + : name); + return true; } - return FALSE; + return false; } /* @@ -19647,7 +19764,10 @@ void ex_function(exarg_T *eap) break; } } - ++p; /* skip the ')' */ + if (*p != ')') { + goto erret; + } + ++p; // skip the ')' /* find extra arguments "range", "dict" and "abort" */ for (;; ) { @@ -19871,13 +19991,14 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg)) + if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, false)) { + // Can't add a function to a locked dictionary goto erret; - } - /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg)) + } + } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, false)) { + // Can't change an existing function if it is locked goto erret; + } /* Give the function a sequential number. Can only be used with a * Funcref! */ @@ -20855,14 +20976,18 @@ call_user_func ( save_sourcing_name = sourcing_name; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 1; - sourcing_name = xmalloc((save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) - + STRLEN(fp->uf_name) + 13); + // need space for function name + ("function " + 3) or "[number]" + size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) + + STRLEN(fp->uf_name) + 20; + sourcing_name = xmalloc(len); { if (save_sourcing_name != NULL - && STRNCMP(save_sourcing_name, "function ", 9) == 0) - sprintf((char *)sourcing_name, "%s..", save_sourcing_name); - else + && STRNCMP(save_sourcing_name, "function ", 9) == 0) { + vim_snprintf((char *)sourcing_name, len, "%s[%zu]..", + save_sourcing_name, save_sourcing_lnum); + } else { STRCPY(sourcing_name, "function "); + } cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); if (p_verbose >= 12) { @@ -22128,7 +22253,6 @@ static void on_process_exit(Process *proc, int status, void *d) char msg[22]; snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); terminal_close(data->term, msg); - apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, curbuf); } if (data->status_ptr) { |