diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 1969 |
1 files changed, 1000 insertions, 969 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 577aa67c60..284185083e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -110,9 +110,6 @@ #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ -#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not - be freed. */ - #define AUTOLOAD_CHAR '#' /* Character used as separator in autoload function/variable names. */ @@ -218,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL}; static int echo_attr = 0; /* attributes used for ":echo" */ +/// Describe data to return from find_some_match() +typedef enum { + kSomeMatch, ///< Data for match(). + kSomeMatchEnd, ///< Data for matchend(). + kSomeMatchList, ///< Data for matchlist(). + kSomeMatchStr, ///< Data for matchstr(). + kSomeMatchStrPos, ///< Data for matchstrpos(). +} SomeMatchType; + /// trans_function_name() flags typedef enum { TFN_INT = 1, ///< May use internal function name @@ -396,7 +402,6 @@ static struct vimvar { VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), @@ -563,9 +568,9 @@ void eval_init(void) dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = tv_list_alloc(); - type_list->lv_lock = VAR_FIXED; - type_list->lv_refcount = 1; + list_T *const type_list = tv_list_alloc(0); + tv_list_set_lock(type_list, VAR_FIXED); + tv_list_ref(type_list); dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { @@ -586,7 +591,7 @@ void eval_init(void) dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); set_vim_var_nr(VV_STDERR, CHAN_STDERR); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); @@ -1014,7 +1019,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { tv_list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) { + if (tv_list_len(tv.vval.v_list) > 0) { ga_append(&ga, NL); } } @@ -1102,10 +1107,11 @@ static void restore_vimvar(int idx, typval_T *save_tv) vimvars[idx].vv_tv = *save_tv; if (vimvars[idx].vv_type == VAR_UNKNOWN) { hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "restore_vimvar()"); - else + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { hash_remove(&vimvarht, hi); + } } } @@ -1143,27 +1149,31 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) return list; } -/* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ -int get_spellword(list_T *list, const char **pp) +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in] list List to get values from. +/// @param[out] ret_word Suggested word. Not initialized if return value is +/// -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word) { - listitem_T *li; - - li = list->lv_first; - if (li == NULL) { + if (tv_list_len(list) != 2) { + EMSG(_("E5700: Expression from 'spellsuggest' must yield lists with " + "exactly two values")); return -1; } - *pp = tv_get_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) { + *ret_word = tv_list_find_str(list, 0); + if (*ret_word == NULL) { return -1; } - return tv_get_number(&li->li_tv); + return tv_list_find_nr(list, -1, NULL); } @@ -1269,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc, char *call_func_retstr(const char *const func, const int argc, const char_u *const *const argv, const bool safe) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; // All arguments are passed as strings, no conversion to number. @@ -1504,9 +1514,6 @@ ex_let_vars ( ) { char_u *arg = arg_start; - list_T *l; - int i; - listitem_T *item; typval_T ltv; if (*arg != '[') { @@ -1518,46 +1525,52 @@ ex_let_vars ( return OK; } - /* - * ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - */ - if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) { + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { EMSG(_(e_listreq)); return FAIL; } + list_T *const l = tv->vval.v_list; - i = tv_list_len(l); - if (semicolon == 0 && var_count < i) { + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { EMSG(_("E687: Less targets than List items")); return FAIL; } - if (var_count - semicolon > i) { + if (var_count - semicolon > len) { EMSG(_("E688: More targets than List items")); return FAIL; } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); - item = l->lv_first; + listitem_T *item = tv_list_first(l); + size_t rest_len = tv_list_len(l); while (*arg != ']') { arg = skipwhite(arg + 1); - arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars); - item = item->li_next; - if (arg == NULL) + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", + nextchars); + if (arg == NULL) { return FAIL; + } + rest_len--; + item = TV_LIST_ITEM_NEXT(l, item); arg = skipwhite(arg); if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = tv_list_alloc(); + list_T *const rest_list = tv_list_alloc(rest_len); while (item != NULL) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); } ltv.v_type = VAR_LIST; - ltv.v_lock = 0; - ltv.vval.v_list = l; - l->lv_refcount = 1; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); arg = ex_let_one(skipwhite(arg + 1), <v, false, (char_u *)"]", nextchars); @@ -1567,7 +1580,7 @@ ex_let_vars ( } break; } else if (*arg != ',' && *arg != ']') { - EMSG2(_(e_intern2), "ex_let_vars()"); + internal_error("ex_let_vars()"); return FAIL; } } @@ -2072,6 +2085,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Loop until no more [idx] or .key is following. */ lp->ll_tv = &v->di_tv; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) && !(lp->ll_tv->v_type == VAR_DICT @@ -2122,9 +2137,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { EMSG(_(e_dictrange)); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST @@ -2132,9 +2145,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_("E709: [:] requires a List value")); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } p = skipwhite(p + 1); @@ -2143,16 +2154,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_empty2 = false; if (eval1(&p, &var2, true) == FAIL) { // Recursive! - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (!tv_check_str(&var2)) { // Not a number or string. - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); tv_clear(&var2); return NULL; } @@ -2165,12 +2172,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_missbrac)); } - if (!empty1) { - tv_clear(&var1); - } - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var1); + tv_clear(&var2); return NULL; } @@ -2224,9 +2227,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_dictkey), key); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (len == -1) { @@ -2234,32 +2235,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_newkey = vim_strnsave(key, len); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); lp->ll_tv = &lp->ll_di->di_tv; } else { // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = (long)tv_get_number(&var1); // Is number or string. - tv_clear(&var1); + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); } + tv_clear(&var1); + lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); @@ -2270,9 +2267,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var2); if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); } @@ -2310,10 +2305,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } - lp->ll_tv = &lp->ll_li->li_tv; + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } } + tv_clear(&var1); return p; } @@ -2375,47 +2371,55 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, 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; + for (listitem_T *ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, + if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + (const char *)lp->ll_name, TV_CSTRING)) { return; } - ri = ri->li_next; + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { break; } - ll_li = ll_li->li_next; + ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); ll_n1++; } /* * Assign the List values to the list items. */ - for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { if (op != NULL && *op != '=') { - eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), + (const char *)op); } else { - tv_clear(&lp->ll_li->li_tv); - tv_copy(&ri->li_tv, &lp->ll_li->li_tv); + tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); + tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); } - ri = ri->li_next; - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { break; - if (lp->ll_li->li_next == NULL) { + } + assert(lp->ll_li != NULL); + if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { // Need to add an empty item. tv_list_append_number(lp->ll_list, 0); - assert(lp->ll_li->li_next); + // ll_li may have become invalid after append, don’t use it. + lp->ll_li = tv_list_last(lp->ll_list); // Valid again. + } else { + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); } - lp->ll_li = lp->ll_li->li_next; - ++lp->ll_n1; + lp->ll_n1++; } - if (ri != NULL) + if (ri != NULL) { EMSG(_("E710: List value has more items than target")); - else if (lp->ll_empty2 - ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL) - : lp->ll_n1 != lp->ll_n2) + } else if (lp->ll_empty2 + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) + : lp->ll_n1 != lp->ll_n2) { EMSG(_("E711: List value has not enough items")); + } } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; @@ -2460,9 +2464,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { + assert(lp->ll_newkey != NULL); tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; + assert(di->di_key != NULL); tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } @@ -2514,7 +2520,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * list being used in "tv". */ fi->fi_list = l; tv_list_watch_add(l, &fi->fi_lw); - fi->fi_lw.lw_item = l->lv_first; + fi->fi_lw.lw_item = tv_list_first(l); } } } @@ -2532,21 +2538,18 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * Return TRUE when a valid item was found, FALSE when at end of list or * something wrong. */ -int next_for_item(void *fi_void, char_u *arg) +bool next_for_item(void *fi_void, char_u *arg) { - forinfo_T *fi = (forinfo_T *)fi_void; - int result; - listitem_T *item; + forinfo_T *fi = (forinfo_T *)fi_void; - item = fi->fi_lw.lw_item; - if (item == NULL) - result = FALSE; - else { - fi->fi_lw.lw_item = item->li_next; - result = (ex_let_vars(arg, &item->li_tv, TRUE, - fi->fi_semicolon, fi->fi_varcount, NULL) == OK); + listitem_T *item = fi->fi_lw.lw_item; + if (item == NULL) { + return false; + } else { + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, NULL) == OK); } - return result; } // TODO(ZyX-I): move to eval/ex_cmds @@ -2870,7 +2873,10 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) } *name_end = cc; } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && tv_check_lock(tv_list_locked(lp->ll_list), + (const char *)lp->ll_name, lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, @@ -2878,27 +2884,26 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { - listitem_T *li; - listitem_T *ll_li = lp->ll_li; - int ll_n1 = lp->ll_n1; - - 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, (const char *)lp->ll_name, + assert(lp->ll_list != NULL); + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + (const char *)lp->ll_name, lp->ll_name_len)) { return false; } - ll_li = li; - ll_n1++; - } - - /* Delete a range of List items. */ - while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - li = lp->ll_li->li_next; - tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; - ++lp->ll_n1; + lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } } + tv_list_remove_items(lp->ll_list, first_li, last_li); } else { if (lp->ll_list != NULL) { // unlet a List item. @@ -2906,6 +2911,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) } 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; @@ -2960,7 +2966,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit) d = di->di_tv.vval.v_dict; } if (d == NULL) { - EMSG2(_(e_intern2), "do_unlet()"); + internal_error("do_unlet()"); return FAIL; } hashitem_T *hi = hash_find(ht, (const char_u *)varname); @@ -3043,13 +3049,13 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - tv_item_lock(&li->li_tv, deep, lock); - li = li->li_next; - ++lp->ll_n1; + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - tv_item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock); } else { // (un)lock a Dictionary item. tv_item_lock(&lp->ll_di->di_tv, deep, lock); @@ -4508,18 +4514,18 @@ eval_index ( if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { n2 = -1; } - l = tv_list_alloc(); + l = tv_list_alloc(n2 - n1 + 1); item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); } tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - l->lv_refcount++; + tv_list_ref(l); } else { - tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1); tv_clear(rettv); *rettv = var1; } @@ -4864,35 +4870,34 @@ void partial_unref(partial_T *pt) static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; - typval_T tv; - listitem_T *item; if (evaluate) { - l = tv_list_alloc(); + l = tv_list_alloc(kListLenShouldKnow); } *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ + typval_T tv; + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! goto failret; + } if (evaluate) { - item = tv_list_item_alloc(); - item->li_tv = tv; - item->li_tv.v_lock = 0; - tv_list_append(l, item); + tv.v_lock = VAR_UNLOCKED; + tv_list_append_owned_tv(l, tv); } - if (**arg == ']') + if (**arg == ']') { break; + } if (**arg != ',') { - EMSG2(_("E696: Missing comma in List: %s"), *arg); + emsgf(_("E696: Missing comma in List: %s"), *arg); goto failret; } *arg = skipwhite(*arg + 1); } if (**arg != ']') { - EMSG2(_("E697: Missing end of List ']': %s"), *arg); + emsgf(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { tv_list_free(l); @@ -4904,7 +4909,7 @@ failret: if (evaluate) { rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - ++l->lv_refcount; + tv_list_ref(l); } return OK; @@ -5172,6 +5177,8 @@ bool garbage_collect(bool testing) ABORTING(set_ref_list)(sub.additional_elements, copyID); } + ABORTING(set_ref_in_quickfix)(copyID); + bool did_free = false; if (!abort) { // 2. Free lists and dictionaries that are not referenced. @@ -5239,8 +5246,8 @@ static int free_unref_items(int copyID) // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { - if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -5260,7 +5267,7 @@ static int free_unref_items(int copyID) for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -5325,15 +5332,16 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) list_T *cur_l = l; for (;;) { - if (!abort) { - // Mark each item in the list. If the item contains a hashtab - // it is added to ht_stack, if it contains a list it is added to - // list_stack. - for (listitem_T *li = cur_l->lv_first; !abort && li != NULL; - li = li->li_next) { - abort = set_ref_in_item(&li->li_tv, copyID, ht_stack, &list_stack); + // Mark each item in the list. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + TV_LIST_ITER(cur_l, li, { + if (abort) { + break; } - } + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + }); if (list_stack == NULL) { break; @@ -5928,6 +5936,14 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) } #ifdef INCLUDE_GENERATED_DECLARATIONS + +#ifdef _MSC_VER +// This prevents MSVC from replacing the functions with intrinsics, +// and causing errors when trying to get their addresses in funcs.generated.h +#pragma function (ceil) +#pragma function (floor) +#endif + # include "funcs.generated.h" #endif @@ -6530,12 +6546,10 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - - rettv->vval.v_number = 1; /* Default: Failed */ + 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, "add() argument", TV_TRANSLATE)) { + list_T *const l = argvars[0].vval.v_list; + if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -6586,9 +6600,10 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) && u_save(lnum, lnum + 1) == OK) { if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - if (l == NULL) + if (l == NULL) { return; - li = l->lv_first; + } + li = tv_list_first(l); } for (;; ) { if (l == NULL) { @@ -6596,7 +6611,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (li == NULL) { break; // End of list. } else { - tv = &li->li_tv; // Append item from list. + tv = TV_LIST_ITEM_TV(li); // Append item from list. } const char *const line = tv_get_string_chk(tv); if (line == NULL) { // Type error. @@ -6608,7 +6623,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l == NULL) { break; } - li = li->li_next; + li = TV_LIST_ITEM_NEXT(l, li); } appended_lines_mark(lnum, added); @@ -6661,7 +6676,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ARGCOUNT); for (idx = 0; idx < ARGCOUNT; idx++) { tv_list_append_string(rettv->vval.v_list, (const char *)alist_name(&ARGLIST[idx]), -1); @@ -6771,7 +6786,7 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(1)); } tv_list_append_string(vimvars[VV_ERRORS].vv_list, (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); @@ -7247,30 +7262,26 @@ static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr) int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv) { - listitem_T *item; typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; int dummy; int r = 0; - for (item = args->vval.v_list->lv_first; item != NULL; - item = item->li_next) { + TV_LIST_ITER(args->vval.v_list, item, { if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { EMSG(_("E699: Too many arguments")); - break; + goto func_call_skip_call; } - /* Make a copy of each argument. This is needed to be able to set - * v_lock to VAR_FIXED in the copy without changing the original list. - */ - tv_copy(&item->li_tv, &argv[argc++]); - } + // Make a copy of each argument. This is needed to be able to set + // v_lock to VAR_FIXED in the copy without changing the original list. + tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); + }); - if (item == NULL) { - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); - } + r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, true, partial, selfdict); +func_call_skip_call: // Free the arguments. while (argc > 0) { tv_clear(&argv[--argc]); @@ -7487,7 +7498,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!undo_allowed()) return; - if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) { + if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_invarg)); return; } @@ -7595,7 +7606,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) long idx; if ((l = argvars[0].vval.v_list) != NULL) { - li = l->lv_first; + li = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -7613,9 +7624,11 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = NULL; } - for (; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) - ++n; + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) { + n++; + } + } } } else if (argvars[0].v_type == VAR_DICT) { int todo; @@ -7933,34 +7946,51 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool n = true; switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_FUNC: - n = argvars[0].vval.v_string == NULL - || *argvars[0].vval.v_string == NUL; - break; - case VAR_PARTIAL: - n = false; - break; - case VAR_NUMBER: - n = argvars[0].vval.v_number == 0; - break; - case VAR_FLOAT: - n = argvars[0].vval.v_float == 0.0; - break; - case VAR_LIST: - n = argvars[0].vval.v_list == NULL - || argvars[0].vval.v_list->lv_first == NULL; - break; - case VAR_DICT: - n = argvars[0].vval.v_dict == NULL - || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; - break; - case VAR_SPECIAL: - n = argvars[0].vval.v_special != kSpecialVarTrue; - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); - break; + case VAR_STRING: + case VAR_FUNC: { + n = argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL; + break; + } + case VAR_PARTIAL: { + n = false; + break; + } + case VAR_NUMBER: { + n = argvars[0].vval.v_number == 0; + break; + } + case VAR_FLOAT: { + n = argvars[0].vval.v_float == 0.0; + break; + } + case VAR_LIST: { + n = (tv_list_len(argvars[0].vval.v_list) == 0); + break; + } + case VAR_DICT: { + n = (tv_dict_len(argvars[0].vval.v_dict) == 0); + break; + } + case VAR_SPECIAL: { + // Using switch to get warning if SpecialVarValue receives more values. + switch (argvars[0].vval.v_special) { + case kSpecialVarTrue: { + n = false; + break; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + n = true; + break; + } + } + break; + } + case VAR_UNKNOWN: { + internal_error("f_empty(UNKNOWN)"); + break; + } } rettv->vval.v_number = n; @@ -8024,17 +8054,22 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } +typedef struct { + const list_T *const l; + const listitem_T *li; +} GetListLineCookie; + static char_u *get_list_line(int c, void *cookie, int indent) { - const listitem_T **const p = (const listitem_T **)cookie; - const listitem_T *item = *p; + GetListLineCookie *const p = (GetListLineCookie *)cookie; + const listitem_T *const item = p->li; if (item == NULL) { return NULL; } char buf[NUMBUFLEN]; - const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); - *p = item->li_next; + const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf); + p->li = TV_LIST_ITEM_NEXT(p->l, item); return (char_u *)(s == NULL ? NULL : xstrdup(s)); } @@ -8076,11 +8111,14 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, + tv_list_ref(list); + GetListLineCookie cookie = { + .l = list, + .li = tv_list_first(list), + }; + do_cmdline(NULL, get_list_line, (void *)&cookie, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + tv_list_unref(list); } msg_silent = save_msg_silent; emsg_silent = save_emsg_silent; @@ -8088,8 +8126,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + rettv->vval.v_string = capture_ga->ga_data; capture_ga = save_capture_ga; } @@ -8198,7 +8235,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); emsg_off--; if (rettv->v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (result != NULL)); if (result != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } @@ -8221,8 +8258,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -8239,7 +8276,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "menu_get(path [, modes])" function static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); int modes = MENU_ALL_MODES; if (argvars[1].v_type == VAR_STRING) { const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); @@ -8262,14 +8299,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (l1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); - (void)locked; - assert(locked == true); - } else if (l2 == NULL) { - // Do nothing - tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) { + if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -8277,7 +8307,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; // Type error; errmsg already given. } - if (before == l1->lv_len) { + if (before == tv_list_len(l1)) { item = NULL; } else { item = tv_list_find(l1, before); @@ -8286,8 +8316,9 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - } else + } else { item = NULL; + } tv_list_extend(l1, l2, item); tv_copy(&argvars[0], rettv); @@ -8406,7 +8437,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (count < 0) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); } if (*fname != NUL && !error) { @@ -8439,7 +8470,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) static void filter_map(typval_T *argvars, typval_T *rettv, int map) { typval_T *expr; - listitem_T *li, *nli; list_T *l = NULL; dictitem_T *di; hashtab_T *ht; @@ -8457,11 +8487,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int idx = 0; if (argvars[0].v_type == VAR_LIST) { + tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { + || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, + TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { + tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; @@ -8520,18 +8553,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } else { vimvars[VV_KEY].vv_type = VAR_NUMBER; - for (li = l->lv_first; li != NULL; li = nli) { + for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map - && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) { + && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } - nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL - || did_emsg) + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL + || did_emsg) { break; + } if (!map && rem) { - tv_list_item_remove(l, li); + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); } idx++; } @@ -8542,8 +8578,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - - tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8935,7 +8969,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, goto theend; } list = argvars[arg_idx].vval.v_list; - if (list == NULL || list->lv_len == 0) { + if (tv_list_len(list) == 0) { arg_idx = 0; } } @@ -8946,7 +8980,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, // result is a VAR_PARTIAL if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); - const int lv_len = (list == NULL ? 0 : list->lv_len); + const int lv_len = tv_list_len(list); pt->pt_argc = arg_len + lv_len; pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc); @@ -8955,11 +8989,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { - for (listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &pt->pt_argv[i++]); - } + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); } } @@ -9045,7 +9077,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { - tv = &li->li_tv; + tv = TV_LIST_ITEM_TV(li); } } } else if (argvars[0].v_type == VAR_DICT) { @@ -9086,7 +9118,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (tv_list_alloc_ret(rettv) != NULL) { + if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } @@ -9110,8 +9142,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Returns information about signs placed in a buffer as list of dicts. -static void get_buffer_signs(buf_T *buf, list_T *l) +static list_T *get_buffer_signs(buf_T *buf) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + list_T *const l = tv_list_alloc(kListLenMayKnow); for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); @@ -9122,6 +9156,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) tv_list_append_dict(l, d); } + return l; } /// Returns buffer options, variables and other attributes in a dictionary. @@ -9145,7 +9180,7 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer - list_T *const windows = tv_list_alloc(); + list_T *const windows = tv_list_alloc(kListLenMayKnow); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { tv_list_append_number(windows, (varnumber_T)wp->handle); @@ -9155,9 +9190,7 @@ static dict_T *get_buffer_info(buf_T *buf) if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *const signs = tv_list_alloc(); - get_buffer_signs(buf, signs); - tv_dict_add_list(dict, S_LEN("signs"), signs); + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); } return dict; @@ -9171,7 +9204,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -9230,35 +9263,33 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) { - char_u *p; - - rettv->v_type = VAR_STRING; + rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); rettv->vval.v_string = NULL; - if (retlist) { - tv_list_alloc_ret(rettv); - } - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) + if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { + if (retlist) { + tv_list_alloc_ret(rettv, 0); + } return; + } - if (!retlist) { - if (start >= 1 && start <= buf->b_ml.ml_line_count) - p = ml_get_buf(buf, start, FALSE); - else - p = (char_u *)""; - rettv->vval.v_string = vim_strsave(p); - } else { - if (end < start) - return; - - if (start < 1) + if (retlist) { + if (start < 1) { start = 1; - if (end > buf->b_ml.ml_line_count) + } + if (end > buf->b_ml.ml_line_count) { end = buf->b_ml.ml_line_count; + } + tv_list_alloc_ret(rettv, end - start + 1); while (start <= end) { tv_list_append_string(rettv->vval.v_list, (const char *)ml_get_buf(buf, start++, false), -1); } + } else { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count) + ? vim_strsave(ml_get_buf(buf, start, false)) + : NULL); } } @@ -9583,8 +9614,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - tv_list_alloc_ret(rettv); ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], @@ -9878,7 +9909,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { - end = 0; + end = lnum; retlist = false; } else { end = tv_get_lnum(&argvars[1]); @@ -9892,7 +9923,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -9927,7 +9958,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (cur != NULL) { dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { @@ -9940,7 +9971,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = tv_list_alloc(); + list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { tv_list_append_number(l, (varnumber_T)llpos->col); @@ -9989,7 +10020,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos)); tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum @@ -10062,12 +10093,12 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (return_list) { rettv->v_type = VAR_LIST; - rettv->vval.v_list = + rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = tv_list_alloc(); + rettv->vval.v_list = tv_list_alloc(0); } - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -10115,7 +10146,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); - list_T *const l = tv_list_alloc(); + list_T *const l = tv_list_alloc(kListLenMayKnow); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } @@ -10132,7 +10163,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN + ? 1 + : kListLenMayKnow)); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -10231,7 +10264,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -10456,9 +10489,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne( &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10507,7 +10540,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ga.ga_len); for (int i = 0; i < ga.ga_len; i++) { tv_list_append_string(rettv->vval.v_list, ((const char **)(ga.ga_data))[i], -1); @@ -10646,6 +10679,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "windows", "winaltkeys", "writebackup", +#if defined(HAVE_WSL) + "wsl", +#endif "nvim", }; @@ -10705,6 +10741,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } + if (STRICMP(name, "ruby") == 0 && n == true) { + char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); + if (rubyhost) { + if (*rubyhost == NUL) { + // Invalid rubyhost executable. Gem is probably not installed. + n = false; + } + xfree(rubyhost); + } + } + rettv->vval.v_number = n; } @@ -11024,39 +11071,42 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item; long idx = 0; - int ic = FALSE; + bool ic = false; rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; if (l != NULL) { - item = l->lv_first; + listitem_T *item = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - // Start at specified item. Use the cached index that tv_list_find() - // sets, so that a negative number also works. - item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); - idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[3], &error); - } - if (error) { + // Start at specified item. + idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + if (error || idx == -1) { item = NULL; + } else { + item = tv_list_find(l, idx); + assert(item != NULL); + } + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = !!tv_get_number_chk(&argvars[3], &error); + if (error) { + item = NULL; + } } } - for (; item != NULL; item = item->li_next, ++idx) - if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) { + for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { + if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) { rettv->vval.v_number = idx; break; } + } } } @@ -11085,6 +11135,7 @@ void get_user_input(const typval_T *const argvars, char defstr_buf[NUMBUFLEN]; char cancelreturn_buf[NUMBUFLEN]; char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; if (argvars[0].v_type == VAR_DICT) { if (argvars[1].v_type != VAR_UNKNOWN) { emsgf(_("E5050: {opts} must be the only argument")); @@ -11099,7 +11150,6 @@ void get_user_input(const typval_T *const argvars, if (defstr == NULL) { return; } - char def[1] = { 0 }; cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"), cancelreturn_buf, def); if (cancelreturn == NULL) { // error @@ -11220,11 +11270,10 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - listitem_T *li; int selected; int mouse_used; - if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "inputlist()"); return; } @@ -11235,15 +11284,16 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_scroll = TRUE; msg_clr_eos(); - for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts(tv_get_string(&li->li_tv)); + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + msg_puts(tv_get_string(TV_LIST_ITEM_TV(li))); msg_putchar('\n'); - } + }); - /* Ask for choice. */ + // Ask for choice. selected = prompt_for_number(&mouse_used); - if (mouse_used) + if (mouse_used) { selected -= lines_left; + } rettv->vval.v_number = selected; } @@ -11299,9 +11349,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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, N_("insert() argument"), - TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -11312,7 +11361,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } listitem_T *item = NULL; - if (before != l->lv_len) { + if (before != tv_list_len(l)) { item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); @@ -11376,7 +11425,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_dictkey), lv.ll_newkey); } else if (lv.ll_list != NULL) { // List item. - rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); + rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li)); } else { // Dictionary item. rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); @@ -11405,43 +11454,41 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, return; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); TV_DICT_ITER(tv->vval.v_dict, di, { - listitem_T *const li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + typval_T tv = { .v_lock = VAR_UNLOCKED }; switch (what) { case kDictListKeys: { - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = vim_strsave(di->di_key); + tv.v_type = VAR_STRING; + tv.vval.v_string = vim_strsave(di->di_key); break; } case kDictListValues: { - tv_copy(&di->di_tv, &li->li_tv); + tv_copy(&di->di_tv, &tv); break; } case kDictListItems: { // items() - list_T *const sub_l = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = sub_l; - sub_l->lv_refcount++; - - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = VAR_UNLOCKED; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - - sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - tv_copy(&di->di_tv, &sub_li->li_tv); + list_T *const sub_l = tv_list_alloc(2); + tv.v_type = VAR_LIST; + tv.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); + break; } } + + tv_list_append_owned_tv(rettv->vval.v_list, tv); }); } @@ -11536,15 +11583,13 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } list_T *argl = cmd_tv->vval.v_list; - int argc = argl->lv_len; + int argc = tv_list_len(argl); if (!argc) { EMSG(_(e_invarg)); // List must have at least one item. return NULL; } - assert(argl->lv_first); - - const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + const char *exe = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl))); if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; @@ -11559,15 +11604,15 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); - for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - const char *a = tv_get_string_chk(&arg->li_tv); + TV_LIST_ITER_CONST(argl, arg, { + const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg)); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } argv[i++] = xstrdup(a); - } + }); return argv; } @@ -11695,7 +11740,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *args = argvars[0].vval.v_list; - Channel **jobs = xcalloc(args->lv_len, sizeof(*jobs)); + Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -11704,10 +11749,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 for jobs that were skipped or timed out. int i = 0; - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next, i++) { + TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(chan = find_job(arg->li_tv.vval.v_number, false))) { + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER + || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { jobs[i] = NULL; } else { jobs[i] = chan; @@ -11719,7 +11764,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) multiqueue_replace_parent(chan->events, waiting_jobs); } } - } + i++; + }); int remaining = -1; uint64_t before = 0; @@ -11728,7 +11774,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) before = os_hrtime(); } - for (i = 0; i < args->lv_len; i++) { + for (i = 0; i < tv_list_len(args); i++) { if (remaining == 0) { // timed out break; @@ -11755,10 +11801,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_T *rv = tv_list_alloc(); + list_T *const rv = tv_list_alloc(tv_list_len(args)); // restore the parent queue for any jobs still alive - for (i = 0; i < args->lv_len; i++) { + for (i = 0; i < tv_list_len(args); i++) { if (jobs[i] == NULL) { tv_list_append_number(rv, -3); continue; @@ -11774,7 +11820,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) multiqueue_free(waiting_jobs); xfree(jobs); ui_busy_stop(); - rv->lv_refcount++; + tv_list_ref(rv); rettv->v_type = VAR_LIST; rettv->vval.v_list = rv; } @@ -11788,9 +11834,6 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } const char *const sep = (argvars[1].v_type == VAR_UNKNOWN ? " " : tv_get_string_chk(&argvars[1])); @@ -12161,7 +12204,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -static void find_some_match(typval_T *argvars, typval_T *rettv, int type) +static void find_some_match(typval_T *const argvars, typval_T *const rettv, + const SomeMatchType type) { char_u *str = NULL; long len = 0; @@ -12182,25 +12226,38 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) p_cpo = (char_u *)""; rettv->vval.v_number = -1; - if (type == 3 || type == 4) { - // type 3: return empty list when there are no matches. - // type 4: return ["", -1, -1, -1] - tv_list_alloc_ret(rettv); - if (type == 4) { + switch (type) { + // matchlist(): return empty list when there are no matches. + case kSomeMatchList: { + tv_list_alloc_ret(rettv, kListLenMayKnow); + break; + } + // matchstrpos(): return ["", -1, -1, -1] + case kSomeMatchStrPos: { + tv_list_alloc_ret(rettv, 4); tv_list_append_string(rettv->vval.v_list, "", 0); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); + break; + } + case kSomeMatchStr: { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + break; + } + case kSomeMatch: + case kSomeMatchEnd: { + // Do nothing: zero is default. + break; } - } else if (type == 2) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; } if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) == NULL) + if ((l = argvars[0].vval.v_list) == NULL) { goto theend; - li = l->lv_first; + } + li = tv_list_first(l); } else { expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); @@ -12220,11 +12277,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; } if (l != NULL) { - li = tv_list_find(l, start); - if (li == NULL) { + idx = tv_list_uidx(l, start); + if (idx == -1) { goto theend; } - idx = l->lv_idx; // Use the cached index. + li = tv_list_find(l, idx); } else { if (start < 0) start = 0; @@ -12260,7 +12317,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) break; } xfree(tofree); - tofree = expr = str = (char_u *)encode_tv2echo(&li->li_tv, NULL); + tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), + NULL); if (str == NULL) { break; } @@ -12275,8 +12333,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* Advance to just after the match. */ if (l != NULL) { - li = li->li_next; - ++idx; + li = TV_LIST_ITEM_NEXT(l, li); + idx++; } else { startcol = (colnr_T)(regmatch.startp[0] + (*mb_ptr2len)(regmatch.startp[0]) - str); @@ -12288,62 +12346,76 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } if (match) { - if (type == 4) { - listitem_T *li1 = rettv->vval.v_list->lv_first; - listitem_T *li2 = li1->li_next; - listitem_T *li3 = li2->li_next; - listitem_T *li4 = li3->li_next; - xfree(li1->li_tv.vval.v_string); - - int rd = (int)(regmatch.endp[0] - regmatch.startp[0]); - li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], rd); - li3->li_tv.vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); - li4->li_tv.vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); - if (l != NULL) { - li2->li_tv.vval.v_number = (varnumber_T)idx; + switch (type) { + case kSomeMatchStrPos: { + list_T *const ret_l = rettv->vval.v_list; + listitem_T *li1 = tv_list_first(ret_l); + listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); + listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); + listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); + xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); + + const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( + (const char *)regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( + regmatch.startp[0] - expr); + TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( + regmatch.endp[0] - expr); + if (l != NULL) { + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; + } + break; } - } else if (type == 3) { - int i; - - /* return list with matched string and submatches */ - for (i = 0; i < NSUBEXP; ++i) { - if (regmatch.endp[i] == NULL) { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } else { - tv_list_append_string(rettv->vval.v_list, - (const char *)regmatch.startp[i], - (regmatch.endp[i] - regmatch.startp[i])); + case kSomeMatchList: { + // Return list with matched string and submatches. + for (int i = 0; i < NSUBEXP; i++) { + if (regmatch.endp[i] == NULL) { + tv_list_append_string(rettv->vval.v_list, NULL, 0); + } else { + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); + } } + break; } - } else if (type == 2) { - // Return matched string. - if (l != NULL) { - tv_copy(&li->li_tv, rettv); - } else { - rettv->vval.v_string = (char_u *)xmemdupz( - (const char *)regmatch.startp[0], - (size_t)(regmatch.endp[0] - regmatch.startp[0])); + case kSomeMatchStr: { + // Return matched string. + if (l != NULL) { + tv_copy(TV_LIST_ITEM_TV(li), rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + break; } - } else if (l != NULL) { - rettv->vval.v_number = idx; - } else { - if (type != 0) { - rettv->vval.v_number = - (varnumber_T)(regmatch.startp[0] - str); - } else { - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - str); + case kSomeMatch: + case kSomeMatchEnd: { + if (l != NULL) { + rettv->vval.v_number = idx; + } else { + if (type == kSomeMatch) { + rettv->vval.v_number = + (varnumber_T)(regmatch.startp[0] - str); + } else { + rettv->vval.v_number = + (varnumber_T)(regmatch.endp[0] - str); + } + rettv->vval.v_number += (varnumber_T)(str - expr); + } + break; } - rettv->vval.v_number += (varnumber_T)(str - expr); } } vim_regfree(regmatch.regprog); } - if (type == 4 && l == NULL) { + if (type == kSomeMatchStrPos && l == NULL) { // matchstrpos() without a list: drop the second item - tv_list_item_remove(rettv->vval.v_list, - rettv->vval.v_list->lv_first->li_next); + list_T *const ret_l = rettv->vval.v_list; + tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); } theend: @@ -12356,7 +12428,7 @@ theend: */ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 1); + find_some_match(argvars, rettv, kSomeMatch); } /* @@ -12409,56 +12481,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - char buf[NUMBUFLEN]; - const char *const group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) { - return; - } + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } - if (argvars[1].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "matchaddpos()"); - return; - } + if (argvars[1].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } - list_T *l; - l = argvars[1].vval.v_list; - if (l == NULL) { - return; - } + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } - bool error = false; - int prio = 10; - int id = -1; - const char *conceal_char = NULL; + bool error = false; + int prio = 10; + int id = -1; + const char *conceal_char = NULL; - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - dictitem_T *di; - if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) - != NULL) { - conceal_char = tv_get_string(&di->di_tv); - } + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = tv_get_string(&di->di_tv); } } } - if (error == true) { - return; - } + } + if (error == true) { + return; + } - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) { - EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); - return; - } + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); + return; + } rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, conceal_char); @@ -12469,14 +12541,16 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + const int id = tv_get_number(&argvars[0]); - int id = tv_get_number(&argvars[0]); + tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 + ? 2 + : 0)); if (id >= 1 && id <= 3) { - matchitem_T *m; + matchitem_T *const m = (matchitem_T *)get_match(curwin, id); - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { + if (m != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)syn_id2name(m->hlg_id), -1); tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); @@ -12501,7 +12575,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 0); + find_some_match(argvars, rettv, kSomeMatchEnd); } /* @@ -12509,7 +12583,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 3); + find_some_match(argvars, rettv, kSomeMatchList); } /* @@ -12517,13 +12591,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 2); + find_some_match(argvars, rettv, kSomeMatchStr); } /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 4); + find_some_match(argvars, rettv, kSomeMatchStrPos); } /// Get maximal/minimal number value in a list or dictionary @@ -12539,41 +12613,41 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool domax) FUNC_ATTR_NONNULL_ALL { - varnumber_T n = 0; bool error = false; + rettv->vval.v_number = 0; + varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX); if (tv->v_type == VAR_LIST) { - const list_T *const l = tv->vval.v_list; - if (tv_list_len(l) != 0) { - n = tv_get_number_chk(&l->lv_first->li_tv, &error); - for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; - li = li->li_next) { - const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } - } + if (tv_list_len(tv->vval.v_list) == 0) { + return; } + TV_LIST_ITER_CONST(tv->vval.v_list, li, { + const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else if (tv->v_type == VAR_DICT) { - if (tv->vval.v_dict != NULL) { - bool first = true; - TV_DICT_ITER(tv->vval.v_dict, di, { - const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); - if (error) { - break; - } - if (first) { - n = i; - first = true; - } else if (domax ? i > n : i < n) { - n = i; - } - }); + if (tv_dict_len(tv->vval.v_dict) == 0) { + return; } + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + return; } - rettv->vval.v_number = error ? 0 : n; + rettv->vval.v_number = n; } /* @@ -12658,23 +12732,20 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; - if (list == NULL) { - return; - } + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + list_T *const list = argvars[0].vval.v_list; msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; int idx = 0; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); + TV_LIST_ITER(list, li, { + vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { + if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } - } + }); msgpack_packer_free(lpacker); } @@ -12686,12 +12757,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; - if (list == NULL || list->lv_first == NULL) { + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + const list_T *const list = argvars[0].vval.v_list; + if (tv_list_len(list) == 0) { return; } - if (list->lv_first->li_tv.v_type != VAR_STRING) { + if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) { EMSG2(_(e_invarg2), "List item is not a string"); return; } @@ -12731,13 +12802,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; - tv_list_append(ret_list, li); - if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { + typval_T tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(unpacked.data, &tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; } + tv_list_append_owned_tv(ret_list, tv); } if (result == MSGPACK_UNPACK_CONTINUE) { if (rlret == OK) { @@ -12943,7 +13013,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (stride > 0 ? end + 1 < start : end - 1 > start) { emsgf(_("E727: Start past end")); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (end - start) / stride); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } @@ -12964,9 +13034,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long prevlen = 0; /* length of data in prev */ long prevsize = 0; /* size of prev buffer */ long maxline = MAXLNUM; - long cnt = 0; - char_u *p; /* position in buf */ - char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { @@ -12977,7 +13044,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. @@ -12987,19 +13054,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - while (cnt < maxline || maxline < 0) { + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); - /* This for loop processes what was read, but is also entered at end - * of file so that either: - * - an incomplete line gets written - * - a "binary" file gets an empty line at the end if it ends in a - * newline. */ + // This for loop processes what was read, but is also entered at end + // of file so that either: + // - an incomplete line gets written + // - a "binary" file gets an empty line at the end if it ends in a + // newline. + char_u *p; // Position in buf. + char_u *start; // Start of current line. for (p = buf, start = buf; p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); - ++p) { + p++) { if (*p == '\n' || readlen <= 0) { - listitem_T *li; char_u *s = NULL; size_t len = p - start; @@ -13026,22 +13094,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - tv_list_append(rettv->vval.v_list, li); - - start = p + 1; /* step over newline */ - if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = s, + }); + + start = p + 1; // Step over newline. + if (maxline < 0) { + if (tv_list_len(l) > -maxline) { + assert(tv_list_len(l) == 1 + (-maxline)); + tv_list_item_remove(l, tv_list_first(l)); + } + } else if (tv_list_len(l) >= maxline) { + assert(tv_list_len(l) == maxline); + break; + } + if (readlen <= 0) { break; - } else if (*p == NUL) + } + } else if (*p == NUL) { *p = '\n'; - /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this - * when finding the BF and check the previous two bytes. */ - else if (*p == 0xbf && enc_utf8 && !binary) { - /* Find the two bytes before the 0xbf. If p is at buf, or buf - * + 1, these may be in the "prev" string. */ + // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this + // when finding the BF and check the previous two bytes. + } else if (*p == 0xbf && !binary) { + // Find the two bytes before the 0xbf. If p is at buf, or buf + 1, + // these may be in the "prev" string. char_u back1 = p >= buf + 1 ? p[-1] : prevlen >= 1 ? prev[prevlen - 1] : NUL; char_u back2 = p >= buf + 2 ? p[-2] @@ -13075,8 +13153,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* for */ - if ((cnt >= maxline && maxline >= 0) || readlen <= 0) + if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) { break; + } if (start < p) { /* There's part of a line in buf, store it in "prev". */ if (p - start + prevlen >= prevsize) { @@ -13100,16 +13179,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* while */ - /* - * For a negative line count use only the lines at the end of the file, - * free the rest. - */ - if (maxline < 0) - while (cnt > -maxline) { - tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - cnt--; - } - xfree(prev); fclose(fd); } @@ -13123,9 +13192,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return OK In case of success, FAIL in case of error static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL { - if (arg->v_type != VAR_LIST - || arg->vval.v_list == NULL - || arg->vval.v_list->lv_len != 2) { + if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) { return FAIL; } @@ -13190,7 +13257,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); tv_list_append_number(rettv->vval.v_list, u.split.high); tv_list_append_number(rettv->vval.v_list, u.split.low); } @@ -13249,8 +13316,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } 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, arg_errmsg, TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -13261,8 +13328,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - tv_list_remove_items(l, item, item); - *rettv = item->li_tv; + tv_list_drop_items(l, item, item); + *rettv = *TV_LIST_ITEM_TV(item); xfree(item); } else { // Remove range of items, return list with values. @@ -13274,21 +13341,17 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { int cnt = 0; - for (li = item; li != NULL; li = li->li_next) { - ++cnt; - if (li == item2) + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { break; + } } if (li == NULL) { // Didn't find "item2" after "item". emsgf(_(e_invrange)); } else { - tv_list_remove_items(l, item, item2); - l = tv_list_alloc_ret(rettv); - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); } } } @@ -13318,7 +13381,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list)); while (n-- > 0) { tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } @@ -13433,7 +13496,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q[-1] = NUL; q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path((const char_u *)buf)) { + if (q > p && !path_is_absolute((const char_u *)buf)) { // Symlink is relative to directory of argument. Replace the // symlink with the resolved name in the same directory. const size_t p_len = strlen(p); @@ -13524,21 +13587,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; 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, N_("reverse() argument"), - TV_TRANSLATE)) { - listitem_T *li = l->lv_last; - l->lv_first = l->lv_last = NULL; - l->lv_len = 0; - while (li != NULL) { - listitem_T *const ni = li->li_prev; - tv_list_append(l, li); - li = ni; - } + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("reverse() argument"), TV_TRANSLATE)) { + tv_list_reverse(l); rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - l->lv_refcount++; - l->lv_idx = l->lv_len - l->lv_idx - 1; + tv_list_ref(l); } } @@ -13760,7 +13814,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) scid_T save_current_SID; uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match; linenr_T save_sourcing_lnum; - int save_autocmd_fname_full, save_autocmd_bufnr; + int save_autocmd_bufnr; void *save_funccalp; if (l_provider_call_nesting) { @@ -13771,16 +13825,14 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_sourcing_lnum = sourcing_lnum; save_autocmd_fname = autocmd_fname; save_autocmd_match = autocmd_match; - save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_funccalp = save_funccal(); - // + current_SID = provider_caller_scope.SID; sourcing_name = provider_caller_scope.sourcing_name; sourcing_lnum = provider_caller_scope.sourcing_lnum; autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_match = provider_caller_scope.autocmd_match; - autocmd_fname_full = provider_caller_scope.autocmd_fname_full; autocmd_bufnr = provider_caller_scope.autocmd_bufnr; restore_funccal(provider_caller_scope.funccalp); } @@ -13796,7 +13848,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) sourcing_lnum = save_sourcing_lnum; autocmd_fname = save_autocmd_fname; autocmd_match = save_autocmd_match; - autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; restore_funccal(save_funccalp); } @@ -13836,14 +13887,17 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int argsl = 0; if (argvars[1].v_type == VAR_LIST) { args = argvars[1].vval.v_list; - argsl = args->lv_len; + argsl = tv_list_len(args); // Assert that all list items are strings - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - if (arg->li_tv.v_type != VAR_STRING) { - EMSG(_(e_invarg)); + int i = 0; + TV_LIST_ITER_CONST(args, arg, { + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) { + emsgf(_("E5010: List item %d of the second argument is not a string"), + i); return; } - } + i++; + }); } if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) { @@ -13861,9 +13915,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 1; // Copy arguments to the vector if (argsl > 0) { - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); - } + TV_LIST_ITER_CONST(args, arg, { + argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg))); + }); } // The last item of argv must be NULL @@ -14094,7 +14148,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; @@ -14262,18 +14316,14 @@ do_searchpair ( static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T match_pos; - int lnum = 0; - int col = 0; - int n; int flags = 0; - tv_list_alloc_ret(rettv); + const int n = search_cmn(argvars, &match_pos, &flags); - n = search_cmn(argvars, &match_pos, &flags); - if (n > 0) { - lnum = match_pos.lnum; - col = match_pos.col; - } + tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT))); + + const int lnum = (n > 0 ? match_pos.lnum : 0); + const int col = (n > 0 ? match_pos.col : 0); tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); @@ -14289,13 +14339,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, n); for (size_t i = 0; i < n; i++) { - listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)addrs[i]; - tv_list_append(l, li); + tv_list_append_allocated_string(l, addrs[i]); } xfree(addrs); } @@ -14357,8 +14403,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; if (argvars[0].vval.v_string) { - server_stop((char *) argvars[0].vval.v_string); + bool rv = server_stop((char *)argvars[0].vval.v_string); + rettv->vval.v_number = (rv ? 1 : 0); } } @@ -14503,25 +14552,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - li = l->lv_first; + li = tv_list_first(l); } else { line = tv_get_string_chk(&argvars[1]); } - /* default result is zero == OK */ + // Default result is zero == OK. for (;; ) { - if (l != NULL) { + if (argvars[1].v_type == VAR_LIST) { // List argument, get next string. if (li == NULL) { break; } - line = tv_get_string_chk(&li->li_tv); - li = li->li_next; + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); } - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { break; + } /* When coming here from Insert mode, sync undo, so that this can be * undone separately from what was previously inserted. */ @@ -14593,7 +14643,8 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) return; } const char *const act = tv_get_string_chk(action_arg); - if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { + if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') + && act[1] == NUL) { action = *act; } else { EMSG2(_(e_invact), act); @@ -14622,8 +14673,8 @@ skip_args: title = (wp ? "setloclist()" : "setqflist()"); } - list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { + list_T *const l = list_arg->vval.v_list; + if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14648,8 +14699,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *li; dict_T *d; list_T *s = NULL; @@ -14658,92 +14707,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - if ((l = argvars[0].vval.v_list) != NULL) { - - /* To some extent make sure that we are dealing with a list from - * "getmatches()". */ - li = l->lv_first; - while (li != NULL) { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) { - EMSG(_(e_invarg)); - return; - } - if (!(tv_dict_find(d, S_LEN("group")) != NULL - && (tv_dict_find(d, S_LEN("pattern")) != NULL - || tv_dict_find(d, S_LEN("pos1")) != NULL) - && tv_dict_find(d, S_LEN("priority")) != NULL - && tv_dict_find(d, S_LEN("id")) != NULL)) { - EMSG(_(e_invarg)); - return; - } - li = li->li_next; + list_T *const l = argvars[0].vval.v_list; + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int i = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + emsgf(_("E474: List item %d is either not a dictionary " + "or an empty one"), i); + return; } + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { + emsgf(_("E474: List item %d is missing one of the required keys"), i); + return; + } + i++; + }); - clear_matches(curwin); - li = l->lv_first; - bool match_add_failed = false; - while (li != NULL) { - int i = 0; - - d = li->li_tv.vval.v_dict; - dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); - if (di == NULL) { - if (s == NULL) { - s = tv_list_alloc(); - } + clear_matches(curwin); + bool match_add_failed = false; + TV_LIST_ITER_CONST(l, li, { + int i = 0; - // match from matchaddpos() - for (i = 1; i < 9; i++) { - char buf[5]; - snprintf(buf, sizeof(buf), "pos%d", i); - dictitem_T *const pos_di = tv_dict_find(d, buf, -1); - if (pos_di != NULL) { - if (pos_di->di_tv.v_type != VAR_LIST) { - return; - } + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { + if (s == NULL) { + s = tv_list_alloc(9); + } - tv_list_append_tv(s, &pos_di->di_tv); - s->lv_refcount++; - } else { - break; + // match from matchaddpos() + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { + return; } - } - } - // Note: there are three number buffers involved: - // - group_buf below. - // - numbuf in tv_dict_get_string(). - // - mybuf in tv_get_string(). - // - // If you change this code make sure that buffers will not get - // accidentally reused. - char group_buf[NUMBUFLEN]; - const char *const group = tv_dict_get_string_buf(d, "group", group_buf); - const int priority = (int)tv_dict_get_number(d, "priority"); - const int id = (int)tv_dict_get_number(d, "id"); - dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); - const char *const conceal = (conceal_di != NULL - ? tv_get_string(&conceal_di->di_tv) - : NULL); - if (i == 0) { - if (match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal) != id) { - match_add_failed = true; - } - } else { - if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { - match_add_failed = true; + tv_list_append_tv(s, &pos_di->di_tv); + tv_list_ref(s); + } else { + break; } - tv_list_unref(s); - s = NULL; } - li = li->li_next; } - if (!match_add_failed) { - rettv->vval.v_number = 0; + + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); + if (i == 0) { + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } + } else { + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); + s = NULL; } + }); + if (!match_add_failed) { + rettv->vval.v_number = 0; } } @@ -14860,7 +14906,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_LIST) { list_T *ll = argvars[1].vval.v_list; // If the list is NULL handle like an empty list. - int len = ll == NULL ? 0 : ll->lv_len; + const int len = tv_list_len(ll); // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. @@ -14869,11 +14915,9 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **allocval = lstval + len + 2; char **curallocval = allocval; - for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; - li != NULL; - li = li->li_next) { + TV_LIST_ITER_CONST(ll, li, { char buf[NUMBUFLEN]; - *curval = tv_get_string_buf_chk(&li->li_tv, buf); + *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf); if (*curval == NULL) { goto free_lstval; } @@ -14885,7 +14929,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) curallocval++; } curval++; - } + }); *curval++ = NULL; write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, @@ -15106,12 +15150,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct used in the array that's given to qsort() -typedef struct { - listitem_T *item; - int idx; -} sortItem_T; - /// struct storing information about current sort typedef struct { int item_compare_ic; @@ -15132,11 +15170,11 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *const si1 = (sortItem_T *)s1; - sortItem_T *const si2 = (sortItem_T *)s2; + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; - typval_T *const tv1 = &si1->item->li_tv; - typval_T *const tv2 = &si2->item->li_tv; + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); int res; @@ -15208,6 +15246,8 @@ item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } return res; @@ -15225,7 +15265,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2) static int item_compare2(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; + ListSortItem *si1, *si2; int res; typval_T rettv; typval_T argv[3]; @@ -15238,8 +15278,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) return 0; } - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; if (partial == NULL) { func_name = sortinfo->item_compare_func; @@ -15249,8 +15289,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - tv_copy(&si1->item->li_tv, &argv[0]); - tv_copy(&si2->item->li_tv, &argv[1]); + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -15273,6 +15313,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // When the result would be zero, compare the pointers themselves. Makes // the sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } @@ -15294,9 +15336,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) */ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { - list_T *l; - listitem_T *li; - sortItem_T *ptrs; + ListSortItem *ptrs; long len; long i; @@ -15313,13 +15353,13 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { - l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { - goto theend; + list_T *const l = argvars[0].vval.v_list; + if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + tv_list_ref(l); len = tv_list_len(l); if (len <= 1) { @@ -15385,80 +15425,45 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } } - /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); + // Make an array with each entry pointing to an item in the List. + ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - i = 0; if (sort) { - // sort(): ptrs will be the list to sort. - for (li = l->lv_first; li != NULL; li = li->li_next) { - ptrs[i].item = li; - ptrs[i].idx = i; - i++; - } - info.item_compare_func_err = false; - // Test the compare function. - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) - == ITEM_COMPARE_FAIL) { + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { EMSG(_("E702: Sort compare function failed")); - } else { - // Sort the array with item pointers. - qsort(ptrs, (size_t)len, sizeof (sortItem_T), - (info.item_compare_func == NULL - && info.item_compare_partial == NULL ? - item_compare_not_keeping_zero : - item_compare2_not_keeping_zero)); - - if (!info.item_compare_func_err) { - // Clear the list and append the items in the sorted order. - l->lv_first = NULL; - l->lv_last = NULL; - l->lv_idx_item = NULL; - l->lv_len = 0; - - for (i = 0; i < len; i++) { - tv_list_append(l, ptrs[i].item); - } - } } } else { - int (*item_compare_func_ptr)(const void *, const void *); + ListSorter item_compare_func_ptr; // f_uniq(): ptrs will be a stack of items to remove. info.item_compare_func_err = false; if (info.item_compare_func != NULL || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; + item_compare_func_ptr = item_compare2_keeping_zero; } else { - item_compare_func_ptr = item_compare_keeping_zero; + item_compare_func_ptr = item_compare_keeping_zero; } - for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { - if (item_compare_func_ptr(&li, &li->li_next) == 0) { - ptrs[i++].item = li; - } - if (info.item_compare_func_err) { - EMSG(_("E882: Uniq compare function failed")); - break; - } - } - - if (!info.item_compare_func_err) { - while (--i >= 0) { - assert(ptrs[i].item->li_next); - li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) { - li->li_next->li_prev = ptrs[i].item; - } else { - l->lv_last = ptrs[i].item; + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL;) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { + if (info.item_compare_func_err) { + EMSG(_("E882: Uniq compare function failed")); + break; } - tv_list_watch_fix(l, li); - tv_list_item_free(li); - l->lv_len--; + li = tv_list_item_remove(l, li); + } else { + idx++; + li = TV_LIST_ITEM_NEXT(l, li); } } } @@ -15549,13 +15554,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - tv_list_alloc_ret(rettv); - if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. len = spell_move_to(curwin, FORWARD, true, true, &attr); if (len != 0) { word = (char *)get_cursor_pos_ptr(); + curwin->w_set_curswant = true; } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { const char *str = tv_get_string_chk(&argvars[0]); @@ -15575,6 +15579,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); + tv_list_alloc_ret(rettv, 2); tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" @@ -15591,41 +15596,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool typeerr = false; int maxcount; - garray_T ga; - listitem_T *li; + garray_T ga = GA_EMPTY_INIT_VALUE; bool need_capital = false; - tv_list_alloc_ret(rettv); - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { - return; + goto f_spellsuggest_return; } if (argvars[2].v_type != VAR_UNKNOWN) { need_capital = tv_get_number_chk(&argvars[2], &typeerr); if (typeerr) { - return; + goto f_spellsuggest_return; } } - } else + } else { maxcount = 25; + } spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); + } - for (int i = 0; i < ga.ga_len; i++) { - char *p = ((char **)ga.ga_data)[i]; - - li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)p; - tv_list_append(rettv->vval.v_list, li); - } - ga_clear(&ga); +f_spellsuggest_return: + tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); + for (int i = 0; i < ga.ga_len; i++) { + char *const p = ((char **)ga.ga_data)[i]; + tv_list_append_allocated_string(rettv->vval.v_list, p); } + ga_clear(&ga); } static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -15657,10 +15657,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) pat = "[\\x01- ]\\+"; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); - if (typeerr) + if (typeerr) { return; + } regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { @@ -15677,7 +15678,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { end = str + strlen(str); } - if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 + if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0 && *str != NUL && match && end < (const char *)regmatch.endp[0])) { @@ -16223,7 +16224,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case 'n': { // name - p = get_highlight_name(NULL, id - 1); + p = get_highlight_name_ext(NULL, id - 1, false); break; } case 'r': { // reverse @@ -16287,7 +16288,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, false, NULL, false); @@ -16296,18 +16296,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { cchar = syn_get_sub_char(); - if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) { - cchar = lcs_conceal; + if (cchar == NUL && curwin->w_p_cole == 1) { + cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal; } if (cchar != NUL) { - if (has_mbyte) - (*mb_char2bytes)(cchar, str); - else - str[0] = cchar; + utf_char2bytes(cchar, str); } } } + tv_list_alloc_ret(rettv, 3); tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); @@ -16330,7 +16328,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum))) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; @@ -16346,7 +16344,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) if (!keepempty && str[len - 1] == NL) { len--; } - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(kListLenMayKnow); encode_list_write(list, str, len); return list; } @@ -16392,7 +16390,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 0); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -16405,7 +16403,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); rettv->v_type = VAR_LIST; xfree(res); @@ -16458,7 +16456,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } if (wp != NULL) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (wp != NULL) { tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; @@ -16556,7 +16554,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *fname; tagname_T tn; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); fname = xmalloc(MAXPATHL); bool first = true; @@ -16585,8 +16583,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern, - (char_u *)fname); + (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char_u *)tag_pattern, (char_u *)fname); } /* @@ -16692,6 +16690,18 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } +// "test_write_list_log()" function +static void f_test_write_list_log(typval_T *const argvars, + typval_T *const rettv, + FunPtr fptr) +{ + const char *const fname = tv_get_string_chk(&argvars[0]); + if (fname == NULL) { + return; + } + list_write_log(fname); +} + bool callback_from_typval(Callback *const callback, typval_T *const arg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -16827,7 +16837,9 @@ static void add_timer_info_all(typval_T *rettv) /// "timer_info([timer])" function static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN + ? 1 + : timers->table->n_occupied)); if (argvars[0].v_type != VAR_UNKNOWN) { if (argvars[0].v_type != VAR_NUMBER) { EMSG(_(e_number_exp)); @@ -16852,6 +16864,12 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr) int paused = (bool)tv_get_number(&argvars[1]); timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer != NULL) { + if (!timer->paused && paused) { + time_watcher_stop(&timer->tw); + } else if (timer->paused && !paused) { + time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, + timer->timeout); + } timer->paused = paused; } } @@ -17151,7 +17169,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + internal_error("f_type(UNKNOWN)"); break; } } @@ -17187,7 +17205,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; - list_T *list; tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); @@ -17197,9 +17214,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); - list = tv_list_alloc(); - u_eval_tree(curbuf->b_u_oldhead, list); - tv_dict_add_list(dict, S_LEN("entries"), list); + tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } /* @@ -17258,7 +17273,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); win_findbuf(argvars, rettv->vval.v_list); } @@ -17277,8 +17292,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); - win_id2tabwin(argvars, rettv->vval.v_list); + win_id2tabwin(argvars, rettv); } /// "win_id2win()" function @@ -17449,10 +17463,11 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return true in case of success, false otherwise. static bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) + FUNC_ATTR_NONNULL_ARG(1) { int error = 0; - for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = tv_get_string_chk(&li->li_tv); + TV_LIST_ITER_CONST(list, li, { + const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); if (s == NULL) { return false; } @@ -17479,14 +17494,14 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if (!binary || li->li_next != NULL) { + if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { const ptrdiff_t written = file_write(fp, "\n", 1); if (written < 0) { error = (int)written; goto write_list_error; } } - } + }); if ((error = file_flush(fp)) != 0) { goto write_list_error; } @@ -17496,56 +17511,29 @@ write_list_error: return false; } -/// Initializes a static list with 10 items. -void init_static_list(staticList10_T *sl) -{ - list_T *l = &sl->sl_list; - - memset(sl, 0, sizeof(staticList10_T)); - l->lv_first = &sl->sl_items[0]; - l->lv_last = &sl->sl_items[9]; - l->lv_refcount = DO_NOT_FREE_CNT; - l->lv_lock = VAR_FIXED; - sl->sl_list.lv_len = 10; - - for (int i = 0; i < 10; i++) { - listitem_T *li = &sl->sl_items[i]; - - if (i == 0) { - li->li_prev = NULL; - } else { - li->li_prev = li - 1; - } - if (i == 9) { - li->li_next = NULL; - } else { - li->li_next = li + 1; - } - } -} - /// Saves a typval_T as a string. /// -/// For lists, replaces NLs with NUL and separates items with NLs. +/// For lists or buffers, replaces NLs with NUL and separates items with NLs. /// -/// @param[in] tv A value to store as a string. -/// @param[out] len The length of the resulting string or -1 on error. +/// @param[in] tv Value to store as a string. +/// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { + *len = 0; if (tv->v_type == VAR_UNKNOWN) { - *len = 0; return NULL; } - // For types other than list, let tv_get_string_buf_chk() get the value or + // For other types, let tv_get_string_buf_chk() get the value or // print an error. - if (tv->v_type != VAR_LIST) { + if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { const char *ret = tv_get_string_chk(tv); - if (ret && (*len = strlen(ret))) { + if (ret) { + *len = strlen(ret); return xmemdupz(ret, (size_t)(*len)); } else { *len = -1; @@ -17553,12 +17541,44 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) } } + if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id. + buf_T *buf = buflist_findnr(tv->vval.v_number); + if (buf) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *len += 1; + } + *len += 1; + } + } else { + EMSGN(_(e_nobufnr), tv->vval.v_number); + *len = -1; + return NULL; + } + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc(*len + 1); + char *end = ret; + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *end++ = (*p == '\n') ? NUL : *p; + } + *end++ = '\n'; + } + *end = NUL; + *len = end - ret; + return ret; + } + + assert(tv->v_type == VAR_LIST); // Pre-calculate the resulting length. - *len = 0; list_T *list = tv->vval.v_list; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += strlen(tv_get_string(&li->li_tv)) + 1; - } + TV_LIST_ITER_CONST(list, li, { + *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + }); if (*len == 0) { return NULL; @@ -17566,14 +17586,14 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) char *ret = xmalloc(*len + endnl); char *end = ret; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { + TV_LIST_ITER_CONST(list, li, { + for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } - if (endnl || li->li_next != NULL) { + if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { *end++ = '\n'; } - } + }); *end = NUL; *len = end - ret; return ret; @@ -17613,9 +17633,6 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "writefile()"); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } bool binary = false; bool append = false; @@ -17720,9 +17737,9 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, // We accept "$" for the column number: last column. li = tv_list_find(l, 1L); - if (li != NULL && li->li_tv.v_type == VAR_STRING - && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) { + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING + && TV_LIST_ITEM_TV(li)->vval.v_string != NULL + && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { pos.col = len + 1; } @@ -17799,17 +17816,18 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, */ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) { - list_T *l = arg->vval.v_list; + list_T *l; long i = 0; long n; - /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only - * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */ + // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + // there when "fnump" isn't NULL; "coladd" and "curswant" are optional. if (arg->v_type != VAR_LIST - || l == NULL - || l->lv_len < (fnump == NULL ? 2 : 3) - || l->lv_len > (fnump == NULL ? 4 : 5)) + || (l = arg->vval.v_list) == NULL + || tv_list_len(l) < (fnump == NULL ? 2 : 3) + || tv_list_len(l) > (fnump == NULL ? 4 : 5)) { return FAIL; + } if (fnump != NULL) { n = tv_list_find_nr(l, i++, NULL); // fnum @@ -18234,7 +18252,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { - val->lv_refcount++; + tv_list_ref(val); } } @@ -19030,7 +19048,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, } return; } else if (v->di_tv.v_type != tv->v_type) { - EMSG2(_(e_intern2), "set_var()"); + internal_error("set_var()"); } } @@ -19050,6 +19068,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, return; } + // Make sure dict is valid + assert(dict != NULL); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); if (tv_dict_add(dict, v) == FAIL) { @@ -19267,12 +19288,12 @@ int var_item_copy(const vimconv_T *const conv, case VAR_LIST: to->v_type = VAR_LIST; to->v_lock = 0; - if (from->vval.v_list == NULL) + if (from->vval.v_list == NULL) { to->vval.v_list = NULL; - else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { - /* use the copy made earlier */ - to->vval.v_list = from->vval.v_list->lv_copylist; - ++to->vval.v_list->lv_refcount; + } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { + // Use the copy made earlier. + to->vval.v_list = tv_list_latest_copy(from->vval.v_list); + tv_list_ref(to->vval.v_list); } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } @@ -19297,7 +19318,7 @@ int var_item_copy(const vimconv_T *const conv, } break; case VAR_UNKNOWN: - EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); + internal_error("var_item_copy(UNKNOWN)"); ret = FAIL; } --recurse; @@ -19365,14 +19386,10 @@ void ex_echo(exarg_T *eap) } msg_putchar_attr((uint8_t)(*p), echo_attr); } else { - if (has_mbyte) { - int i = (*mb_ptr2len)((const char_u *)p); + int i = (*mb_ptr2len)((const char_u *)p); - (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); - p += i - 1; - } else { - (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr); - } + (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); + p += i - 1; } } } @@ -20871,7 +20888,9 @@ void ex_delfunction(exarg_T *eap) if (!eap->skip) { if (fp == NULL) { - EMSG2(_(e_nofunc), eap->arg); + if (!eap->forceit) { + EMSG2(_(e_nofunc), eap->arg); + } return; } if (fp->uf_calls > 0) { @@ -20985,11 +21004,11 @@ void func_unref(char_u *name) if (fp == NULL && isdigit(*name)) { #ifdef EXITFREE if (!entered_free_all_mem) { - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); } #else - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); #endif } @@ -21028,7 +21047,7 @@ void func_ref(char_u *name) } else if (isdigit(*name)) { // Only give an error for a numbered function. // Fail silently, when named or lambda function isn't found. - EMSG2(_(e_intern2), "func_ref()"); + internal_error("func_ref()"); } } @@ -21040,6 +21059,22 @@ void func_ptr_ref(ufunc_T *fp) } } +/// Check whether funccall is still referenced outside +/// +/// It is supposed to be referenced if either it is referenced itself or if l:, +/// a: or a:000 are referenced as all these are statically allocated within +/// funccall structure. +static inline bool fc_referenced(const funccall_T *const fc) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL +{ + return ((fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated) + != DO_NOT_FREE_CNT) + || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT + || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT + || fc->fc_refcount > 0); +} + /// Call a user function /// /// @param fp Function to call. @@ -21153,9 +21188,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; - memset(&fc->l_varlist, 0, sizeof(list_T)); - fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; - fc->l_varlist.lv_lock = VAR_FIXED; + tv_list_init_static(&fc->l_varlist); + tv_list_set_lock(&fc->l_varlist, VAR_FIXED); // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. @@ -21204,8 +21238,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (ai >= 0 && ai < MAX_FUNC_ARGS) { tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); - fc->l_listitems[ai].li_tv = argvars[i]; - fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; + *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i]; + TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED; } } @@ -21275,15 +21309,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, } } + const bool do_profiling_yes = do_profiling == PROF_YES; + bool func_not_yet_profiling_but_should = - do_profiling == PROF_YES - && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL); + do_profiling_yes + && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL); if (func_not_yet_profiling_but_should) func_do_profile(fp); bool func_or_func_caller_profiling = - do_profiling == PROF_YES + do_profiling_yes && (fp->uf_profiling || (fc->caller != NULL && fc->caller->func->uf_profiling)); @@ -21293,7 +21329,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, fp->uf_tm_children = profile_zero(); } - if (do_profiling == PROF_YES) { + if (do_profiling_yes) { script_prof_save(&wait_start); } @@ -21369,8 +21405,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; current_SID = save_current_SID; - if (do_profiling == PROF_YES) + if (do_profiling_yes) { script_prof_restore(&wait_start); + } if (p_verbose >= 12 && sourcing_name != NULL) { ++no_wait_return; @@ -21389,10 +21426,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // If the a:000 list and the l: and a: dicts are not referenced and there // is no closure using it, we can free the funccall_T and what's in it. - if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT - && fc->fc_refcount <= 0) { + if (!fc_referenced(fc)) { free_funccal(fc, false); } else { // "fc" is still in use. This can happen when returning "a:000", @@ -21407,10 +21441,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, }); // Make a copy of the a:000 items, since we didn't do that above. - for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); + }); } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -21437,10 +21470,8 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) return; } - if (--fc->fc_refcount <= 0 && (force || ( - fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) { + fc->fc_refcount--; + if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) { for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { if (fc == *pfc) { *pfc = fc->caller; @@ -21475,8 +21506,6 @@ free_funccal ( int free_val /* a: vars were allocated */ ) { - listitem_T *li; - for (int i = 0; i < fc->fc_funcs.ga_len; i++) { ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; @@ -21494,14 +21523,14 @@ free_funccal ( // allocated variables. vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); - /* free all l: variables */ + // Free all l: variables. vars_clear(&fc->l_vars.dv_hashtab); // Free the a:000 variables if they were allocated. if (free_val) { - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - tv_clear(&li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_clear(TV_LIST_ITEM_TV(li)); + }); } func_ptr_unref(fc->func); @@ -22407,7 +22436,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = tv_list_alloc(); + 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); } @@ -22425,7 +22454,6 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) .sourcing_lnum = sourcing_lnum, .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, - .autocmd_fname_full = autocmd_fname_full, .autocmd_bufnr = autocmd_bufnr, .funccalp = save_funccal() }; @@ -22436,8 +22464,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) {.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0}, {.v_type = VAR_UNKNOWN} }; - typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0}; - arguments->lv_refcount++; + typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + tv_list_ref(arguments); int dummy; (void)call_func((const char_u *)func, @@ -22458,7 +22486,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; provider_call_nesting--; - + assert(provider_call_nesting >= 0); + return rettv; } @@ -22498,11 +22527,13 @@ bool eval_has_provider(const char *name) } /// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`. -void eval_format_source_name_line(char *buf, size_t bufsize) +void eval_fmt_source_name_line(char *buf, size_t bufsize) { - snprintf(buf, bufsize, "%s:%" PRIdLINENR, - (sourcing_name ? sourcing_name : (char_u *)"?"), - (sourcing_name ? sourcing_lnum : 0)); + if (sourcing_name) { + snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); + } else { + snprintf(buf, bufsize, "?"); + } } /// ":checkhealth [plugins]" |