diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/clint.py | 18 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 4 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 4 | ||||
-rw-r--r-- | src/nvim/channel.c | 2 | ||||
-rw-r--r-- | src/nvim/edit.c | 16 | ||||
-rw-r--r-- | src/nvim/eval.c | 1202 | ||||
-rw-r--r-- | src/nvim/eval/decode.c | 46 | ||||
-rw-r--r-- | src/nvim/eval/encode.c | 87 | ||||
-rw-r--r-- | src/nvim/eval/encode.h | 13 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 231 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 264 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.c.h | 137 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 14 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 44 | ||||
-rw-r--r-- | src/nvim/lua/converter.c | 12 | ||||
-rw-r--r-- | src/nvim/ops.c | 31 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 36 | ||||
-rw-r--r-- | src/nvim/regexp.c | 59 | ||||
-rw-r--r-- | src/nvim/shada.c | 29 | ||||
-rw-r--r-- | src/nvim/spell.c | 11 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 34 | ||||
-rw-r--r-- | src/nvim/window.c | 69 |
22 files changed, 1367 insertions, 996 deletions
diff --git a/src/clint.py b/src/clint.py index e63175a69b..79ab91ebe1 100755 --- a/src/clint.py +++ b/src/clint.py @@ -201,6 +201,7 @@ _ERROR_CATEGORIES = [ 'runtime/printf', 'runtime/printf_format', 'runtime/threadsafe_fn', + 'runtime/deprecated', 'syntax/parenthesis', 'whitespace/alignment', 'whitespace/blank_line', @@ -2123,8 +2124,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): + (level_starts[depth][2] == '{')): if depth not in ignore_error_levels: error(filename, linenum, 'whitespace/alignment', 2, - 'Inner expression should be aligned ' - 'as opening brace + 1 (+ 2 in case of {)') + ('Inner expression should be aligned ' + 'as opening brace + 1 (+ 2 in case of {{). ' + 'Relevant opening is on line {0!r}').format( + level_starts[depth][3])) prev_line_start = pos elif brace == 'e': pass @@ -2141,7 +2144,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): ignore_error_levels.add(depth) line_ended_with_opening = ( pos == len(line) - 2 * (line.endswith(' \\')) - 1) - level_starts[depth] = (pos, line_ended_with_opening, brace) + level_starts[depth] = (pos, line_ended_with_opening, brace, + linenum) if line_ended_with_opening: depth_line_starts[depth] = (prev_line_start, brace) else: @@ -3200,6 +3204,14 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcat or snprintf instead of %s' % match.group(1)) + if not Search(r'eval/typval\.[ch]$', filename): + match = Search(r'(?:\.|->)' + r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?' + r'|copylist|lock)' + r'|li_(?:next|prev|tv))\b', line) + if match: + error(filename, linenum, 'runtime/deprecated', 4, + 'Accessing list_T internals directly is prohibited') # Check for suspicious usage of "if" like # } if (a == b) { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 629873998e..4492f8bb93 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -789,7 +789,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) Object item = obj.data.array.items[i]; listitem_T *li = tv_list_item_alloc(); - if (!object_to_vim(item, &li->li_tv, err)) { + if (!object_to_vim(item, TV_LIST_ITEM_TV(li), err)) { // cleanup tv_list_item_free(li); tv_list_free(list); @@ -798,7 +798,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) tv_list_append(list, li); } - list->lv_refcount++; + tv_list_ref(list); tv->v_type = VAR_LIST; tv->vval.v_list = list; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f0db391abe..c0daac8085 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -900,9 +900,9 @@ typedef struct { Object *ret_node_p; } ExprASTConvStackItem; -///@cond DOXYGEN_NOT_A_FUNCTION +/// @cond DOXYGEN_NOT_A_FUNCTION typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; -///@endcond +/// @endcond /// Parse a VimL expression /// diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 3807f2b3cd..efef95de01 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -688,7 +688,7 @@ static void on_channel_event(void **args) argv[1].v_type = VAR_LIST; argv[1].v_lock = VAR_UNLOCKED; argv[1].vval.v_list = ev->received; - argv[1].vval.v_list->lv_refcount++; + tv_list_ref(argv[1].vval.v_list); } else { argv[1].v_type = VAR_NUMBER; argv[1].v_lock = VAR_UNLOCKED; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 4f0a3eaf34..649357c7ea 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3536,19 +3536,19 @@ theend: /* * Add completions from a list. */ -static void ins_compl_add_list(list_T *list) +static void ins_compl_add_list(list_T *const list) { - listitem_T *li; int dir = compl_direction; - /* Go through the List with matches and add each of them. */ - for (li = list->lv_first; li != NULL; li = li->li_next) { - if (ins_compl_add_tv(&li->li_tv, dir) == OK) - /* if dir was BACKWARD then honor it just once */ + // Go through the List with matches and add each of them. + TV_LIST_ITER(list, li, { + if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; - else if (did_emsg) + } else if (did_emsg) { break; - } + } + }); } /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2355c8c813..e6cb8cdec0 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. */ @@ -564,8 +561,8 @@ 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; + 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) { @@ -1014,7 +1011,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); } } @@ -1144,27 +1141,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); } @@ -1505,9 +1506,7 @@ ex_let_vars ( ) { char_u *arg = arg_start; - list_T *l; int i; - listitem_T *item; typval_T ltv; if (*arg != '[') { @@ -1519,13 +1518,12 @@ 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) { @@ -1536,29 +1534,34 @@ ex_let_vars ( EMSG(_("E688: More targets than List items")); return FAIL; } + // 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); 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); + item = TV_LIST_ITEM_NEXT(l, item); + if (arg == NULL) { return FAIL; + } 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(); 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.vval.v_list = rest_list; + tv_list_ref(rest_list); arg = ex_let_one(skipwhite(arg + 1), <v, false, (char_u *)"]", nextchars); @@ -2311,7 +2314,7 @@ 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); } } @@ -2376,47 +2379,52 @@ 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) { + } + 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); + assert(TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li)); } - lp->ll_li = lp->ll_li->li_next; - ++lp->ll_n1; + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + 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; @@ -2515,7 +2523,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); } } } @@ -2533,21 +2541,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 @@ -2871,7 +2876,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, @@ -2884,8 +2892,9 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) 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, + li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); + if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + (const char *)lp->ll_name, lp->ll_name_len)) { return false; } @@ -2893,12 +2902,12 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) ll_n1++; } - /* Delete a range of List items. */ + // 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; + li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; - ++lp->ll_n1; + lp->ll_n1++; } } else { if (lp->ll_list != NULL) { @@ -3044,13 +3053,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); @@ -4512,15 +4521,15 @@ eval_index ( l = tv_list_alloc(); 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(l, 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; } @@ -4878,22 +4887,23 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (evaluate) { item = tv_list_item_alloc(); - item->li_tv = tv; - item->li_tv.v_lock = 0; + *TV_LIST_ITEM_TV(item) = tv; + TV_LIST_ITEM_TV(item)->v_lock = VAR_UNLOCKED; tv_list_append(l, item); } - 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); @@ -4905,7 +4915,7 @@ failret: if (evaluate) { rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - ++l->lv_refcount; + tv_list_ref(l); } return OK; @@ -5242,8 +5252,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. @@ -5263,7 +5273,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. @@ -5328,15 +5338,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; @@ -6533,12 +6544,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); } @@ -6589,9 +6598,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) { @@ -6599,7 +6609,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. @@ -6611,7 +6621,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); @@ -7250,30 +7260,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]); @@ -7490,7 +7496,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; } @@ -7598,7 +7604,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; @@ -7616,9 +7622,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; @@ -7936,34 +7944,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: - internal_error("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; @@ -8027,17 +8052,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)); } @@ -8079,11 +8109,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; @@ -8265,14 +8298,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); @@ -8280,7 +8306,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); @@ -8289,8 +8315,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); @@ -8462,7 +8489,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) 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) { @@ -8525,16 +8553,18 @@ 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 (li = tv_list_first(l); li != NULL; li = nli) { 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; + nli = TV_LIST_ITEM_NEXT(l, li); 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); } @@ -8938,7 +8968,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; } } @@ -8949,7 +8979,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); @@ -8958,11 +8988,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++]); + }); } } @@ -9048,7 +9076,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) { @@ -10070,7 +10098,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->vval.v_list == NULL) { rettv->vval.v_list = tv_list_alloc(); } - 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); @@ -11027,39 +11055,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; } + } } } @@ -11223,11 +11254,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; } @@ -11238,15 +11268,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; } @@ -11302,9 +11333,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); @@ -11315,7 +11345,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); @@ -11379,7 +11409,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); @@ -11416,32 +11446,32 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, 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_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->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_LIST_ITEM_TV(li)); 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++; + TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_list = sub_l; + tv_list_ref(sub_l); 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); + TV_LIST_ITEM_TV(sub_li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(sub_li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(sub_li)->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); + tv_copy(&di->di_tv, TV_LIST_ITEM_TV(sub_li)); break; } } @@ -11539,15 +11569,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; @@ -11562,15 +11590,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; } @@ -11698,7 +11726,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); @@ -11707,10 +11735,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; @@ -11722,7 +11750,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; @@ -11731,7 +11760,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; @@ -11761,7 +11790,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *rv = tv_list_alloc(); // 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; @@ -11777,7 +11806,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; } @@ -11791,9 +11820,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])); @@ -12203,7 +12229,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (argvars[0].v_type == VAR_LIST) { 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); @@ -12223,11 +12249,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; @@ -12263,7 +12289,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; } @@ -12278,8 +12305,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); @@ -12292,18 +12319,22 @@ 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); + 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) { - li2->li_tv.vval.v_number = (varnumber_T)idx; + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; } } else if (type == 3) { int i; @@ -12321,7 +12352,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } else if (type == 2) { // Return matched string. if (l != NULL) { - tv_copy(&li->li_tv, rettv); + tv_copy(TV_LIST_ITEM_TV(li), rettv); } else { rettv->vval.v_string = (char_u *)xmemdupz( (const char *)regmatch.startp[0], @@ -12345,8 +12376,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && 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: @@ -12412,56 +12443,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); @@ -12542,41 +12573,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; } /* @@ -12662,22 +12693,19 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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 *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); } @@ -12691,10 +12719,10 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } 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) { + 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; } @@ -12735,9 +12763,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (result == MSGPACK_UNPACK_SUCCESS) { listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; tv_list_append(ret_list, li); - if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { + if (msgpack_to_vim(unpacked.data, TV_LIST_ITEM_TV(li)) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; } @@ -13030,9 +13058,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } 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_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = s; tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ @@ -13109,7 +13137,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + tv_list_item_remove(rettv->vval.v_list, + tv_list_first(rettv->vval.v_list)); cnt--; } @@ -13126,9 +13155,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; } @@ -13252,8 +13279,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); @@ -13265,7 +13292,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. tv_list_remove_items(l, item, item); - *rettv = item->li_tv; + *rettv = *TV_LIST_ITEM_TV(item); xfree(item); } else { // Remove range of items, return list with values. @@ -13277,21 +13304,16 @@ 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); } } } @@ -13527,21 +13549,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); } } @@ -13839,14 +13852,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) { @@ -13864,9 +13880,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 @@ -14295,9 +14311,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l = tv_list_alloc_ret(rettv); 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_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)addrs[i]; tv_list_append(l, li); } xfree(addrs); @@ -14506,25 +14522,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. */ @@ -14626,8 +14643,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; } } @@ -14652,8 +14669,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; @@ -14662,92 +14677,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(); + } - 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; } } @@ -14864,7 +14876,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. @@ -14873,11 +14885,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; } @@ -14889,7 +14899,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, @@ -15139,8 +15149,8 @@ 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; - 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; @@ -15212,6 +15222,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; @@ -15253,8 +15265,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, @@ -15277,6 +15289,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; } @@ -15298,8 +15312,6 @@ 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; long len; long i; @@ -15317,13 +15329,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) { @@ -15395,11 +15407,11 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) i = 0; if (sort) { // sort(): ptrs will be the list to sort. - for (li = l->lv_first; li != NULL; li = li->li_next) { + TV_LIST_ITER(l, li, { ptrs[i].item = li; ptrs[i].idx = i; i++; - } + }); info.item_compare_func_err = false; // Test the compare function. @@ -15418,11 +15430,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) 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; - + tv_list_clear(l); for (i = 0; i < len; i++) { tv_list_append(l, ptrs[i].item); } @@ -15435,34 +15443,25 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) 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; - } - - 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; - } + item_compare_func_ptr = item_compare_keeping_zero; } - 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 + ; li = TV_LIST_ITEM_NEXT(l, li)) { + 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--; + tv_list_item_remove(l, li); + li = tv_list_find(l, idx); + } else { + idx++; } } } @@ -15596,7 +15595,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool typeerr = false; int maxcount; garray_T ga; - listitem_T *li; bool need_capital = false; tv_list_alloc_ret(rettv); @@ -15622,10 +15620,10 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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; + listitem_T *const li = tv_list_item_alloc(); + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_LOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)p; tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); @@ -15681,7 +15679,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])) { @@ -16409,7 +16407,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); @@ -17453,10 +17451,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; } @@ -17483,14 +17482,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; } @@ -17500,34 +17499,6 @@ 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. @@ -17560,9 +17531,9 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) // 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; @@ -17570,14 +17541,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; @@ -17617,9 +17588,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; @@ -17724,9 +17692,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; } @@ -17803,17 +17771,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 @@ -18238,7 +18207,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); } } @@ -19271,12 +19240,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); } @@ -21044,6 +21013,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. @@ -21157,9 +21142,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. @@ -21208,8 +21192,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; } } @@ -21393,10 +21377,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", @@ -21411,10 +21392,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) { @@ -21441,10 +21421,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; @@ -21479,8 +21457,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]; @@ -21498,14 +21474,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); @@ -22440,8 +22416,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, diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 0933b1bf9c..d5c65ebe81 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -60,8 +60,8 @@ static inline void create_special_dict(typval_T *const rettv, dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; - type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; - type_di->di_tv.vval.v_list->lv_refcount++; + type_di->di_tv.vval.v_list = (list_T *)eval_msgpack_type_lists[type]; + tv_list_ref(type_di->di_tv.vval.v_list); tv_dict_add(dict, type_di); dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; @@ -120,7 +120,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, last_container = kv_last(*container_stack); } if (last_container.container.v_type == VAR_LIST) { - if (last_container.container.vval.v_list->lv_len != 0 + if (tv_list_len(last_container.container.vval.v_list) != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); tv_clear(&obj.val); @@ -128,7 +128,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, } assert(last_container.special_val == NULL); listitem_T *obj_li = tv_list_item_alloc(); - obj_li->li_tv = obj.val; + *TV_LIST_ITEM_TV(obj_li) = obj.val; tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { @@ -155,10 +155,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(last_container.special_val, kv_pair); listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv = key.val; + *TV_LIST_ITEM_TV(key_li) = key.val; tv_list_append(kv_pair, key_li); listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv = obj.val; + *TV_LIST_ITEM_TV(val_li) = obj.val; tv_list_append(kv_pair, val_li); } } else { @@ -234,7 +234,7 @@ list_T *decode_create_map_special_dict(typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(ret_tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -270,7 +270,7 @@ typval_T decode_string(const char *const s, const size_t len, : (bool)hasnul); if (really_hasnul) { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); typval_T tv; create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -738,8 +738,9 @@ json_decode_string_cycle_start: } else if (last_container.special_val == NULL ? (last_container.container.v_type == VAR_DICT ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (last_container.container.vval.v_list->lv_len == 0)) - : (last_container.special_val->lv_len == 0)) { + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } @@ -849,7 +850,7 @@ json_decode_string_cycle_start: } case '[': { list_T *list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); typval_T tv = { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -970,7 +971,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; } else { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -993,7 +994,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; } else { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -1039,7 +1040,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } case MSGPACK_OBJECT_ARRAY: { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); *rettv = (typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -1047,9 +1048,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; for (size_t i = 0; i < mobj.via.array.size; i++) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; tv_list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.array.ptr[i], TV_LIST_ITEM_TV(li)) + == FAIL) { return FAIL; } } @@ -1094,15 +1096,17 @@ msgpack_to_vim_generic_map: {} list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(list, kv_pair); listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(key_li)->v_type = VAR_UNKNOWN; tv_list_append(kv_pair, key_li); listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv.v_type = VAR_UNKNOWN; + TV_LIST_ITEM_TV(val_li)->v_type = VAR_UNKNOWN; tv_list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.map.ptr[i].key, TV_LIST_ITEM_TV(key_li)) + == FAIL) { return FAIL; } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + if (msgpack_to_vim(mobj.via.map.ptr[i].val, TV_LIST_ITEM_TV(val_li)) + == FAIL) { return FAIL; } } @@ -1110,7 +1114,7 @@ msgpack_to_vim_generic_map: {} } case MSGPACK_OBJECT_EXT: { list_T *const list = tv_list_alloc(); - list->lv_refcount++; + tv_list_ref(list); tv_list_append_number(list, mobj.via.ext.type); list_T *const ext_val_list = tv_list_alloc(); tv_list_append_list(list, ext_val_list); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1607d2139a..31779a544f 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -53,17 +53,18 @@ int encode_list_write(void *const data, const char *const buf, const size_t len) list_T *const list = (list_T *) data; const char *const end = buf + len; const char *line_end = buf; - listitem_T *li = list->lv_last; + listitem_T *li = tv_list_last(list); // Continue the last list element if (li != NULL) { line_end = xmemscan(buf, NL, len); if (line_end != buf) { const size_t line_length = (size_t)(line_end - buf); - char *str = (char *)li->li_tv.vval.v_string; + char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string; const size_t li_len = (str == NULL ? 0 : strlen(str)); - li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); - str = (char *)li->li_tv.vval.v_string + li_len; + TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc( + str, li_len + line_length + 1); + str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len; memcpy(str, buf, line_length); str[line_length] = 0; memchrsub(str, NUL, NL, line_length); @@ -135,21 +136,27 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, } case kMPConvPairs: case kMPConvList: { - int idx = 0; - const listitem_T *li; - for (li = v.data.l.list->lv_first; - li != NULL && li->li_next != v.data.l.li; - li = li->li_next) { - idx++; - } + const int idx = (v.data.l.li == tv_list_first(v.data.l.list) + ? 0 + : (v.data.l.li == NULL + ? tv_list_len(v.data.l.list) - 1 + : (int)tv_list_idx_of_item( + v.data.l.list, + TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)))); + const listitem_T *const li = (v.data.l.li == NULL + ? tv_list_last(v.data.l.list) + : TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)); if (v.type == kMPConvList || li == NULL - || (li->li_tv.v_type != VAR_LIST - && li->li_tv.vval.v_list->lv_len <= 0)) { - vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); + || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) { + vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx); ga_concat(&msg_ga, IObuff); } else { - typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; + typval_T key_tv = *TV_LIST_ITEM_TV( + tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list)); char *const key = encode_tv2echo(&key_tv, NULL); vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); xfree(key); @@ -202,21 +209,17 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT { size_t len = 0; - if (list != NULL) { - for (const listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { - return false; - } - len++; - if (li->li_tv.vval.v_string != 0) { - len += STRLEN(li->li_tv.vval.v_string); - } + TV_LIST_ITER_CONST(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { + return false; } - if (len) { - len--; + len++; + if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) { + len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string); } + }); + if (len) { + len--; } *ret_len = len; if (len == 0) { @@ -253,31 +256,34 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, char *const buf_end = buf + nbuf; char *p = buf; while (p < buf_end) { - assert(state->li_length == 0 || state->li->li_tv.vval.v_string != NULL); + assert(state->li_length == 0 + || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { - assert(state->li->li_tv.vval.v_string != NULL); - const char ch = (char)state->li->li_tv.vval.v_string[state->offset++]; + assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); + const char ch = (char)( + TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]); *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch); } if (p < buf_end) { - state->li = state->li->li_next; + state->li = TV_LIST_ITEM_NEXT(state->list, state->li); if (state->li == NULL) { *read_bytes = (size_t) (p - buf); return OK; } *p++ = NL; - if (state->li->li_tv.v_type != VAR_STRING) { - *read_bytes = (size_t) (p - buf); + if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) { + *read_bytes = (size_t)(p - buf); return FAIL; } state->offset = 0; - state->li_length = (state->li->li_tv.vval.v_string == NULL + state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL ? 0 - : STRLEN(state->li->li_tv.vval.v_string)); + : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string)); } } *read_bytes = nbuf; - return (state->offset < state->li_length || state->li->li_next != NULL + return ((state->offset < state->li_length + || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL) ? NOTDONE : OK); } @@ -727,12 +733,11 @@ bool encode_check_json_key(const typval_T *const tv) if (val_di->di_tv.vval.v_list == NULL) { return true; } - for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; - li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { + TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { return false; } - } + }); return true; } diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 9bc665253b..ccea245ab3 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer, /// Structure defining state for read_from_list() typedef struct { + const list_T *const list; ///< List being currently read. const listitem_T *li; ///< Item currently read. - size_t offset; ///< Byte offset inside the read item. - size_t li_length; ///< Length of the string inside the read item. + size_t offset; ///< Byte offset inside the read item. + size_t li_length; ///< Length of the string inside the read item. } ListReaderState; /// Initialize ListReaderState structure @@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list) FUNC_ATTR_NONNULL_ALL { return (ListReaderState) { - .li = list->lv_first, + .list = list, + .li = tv_list_first(list), .offset = 0, - .li_length = (list->lv_first->li_tv.vval.v_string == NULL + .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL ? 0 - : STRLEN(list->lv_first->li_tv.vval.v_string)), + : STRLEN(TV_LIST_ITEM_TV( + tv_list_first(list))->vval.v_string)), }; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b5382d1ccb..53c56d0ffd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -66,7 +66,7 @@ listitem_T *tv_list_item_alloc(void) void tv_list_item_free(listitem_T *const item) FUNC_ATTR_NONNULL_ALL { - tv_clear(&item->li_tv); + tv_clear(TV_LIST_ITEM_TV(item)); xfree(item); } @@ -153,6 +153,45 @@ list_T *tv_list_alloc(void) return list; } +/// Initialize a static list with 10 items +/// +/// @param[out] sl Static list to initialize. +void tv_list_init_static10(staticList10_T *const sl) + FUNC_ATTR_NONNULL_ALL +{ +#define SL_SIZE ARRAY_SIZE(sl->sl_items) + list_T *const l = &sl->sl_list; + + memset(sl, 0, sizeof(staticList10_T)); + l->lv_first = &sl->sl_items[0]; + l->lv_last = &sl->sl_items[SL_SIZE - 1]; + l->lv_refcount = DO_NOT_FREE_CNT; + tv_list_set_lock(l, VAR_FIXED); + sl->sl_list.lv_len = 10; + + sl->sl_items[0].li_prev = NULL; + sl->sl_items[0].li_next = &sl->sl_items[1]; + sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2]; + sl->sl_items[SL_SIZE - 1].li_next = NULL; + + for (size_t i = 1; i < SL_SIZE - 1; i++) { + listitem_T *const li = &sl->sl_items[i]; + li->li_prev = li - 1; + li->li_next = li + 1; + } +#undef SL_SIZE +} + +/// Initialize static list with undefined number of elements +/// +/// @param[out] l List to initialize. +void tv_list_init_static(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + memset(l, 0, sizeof(*l)); + l->lv_refcount = DO_NOT_FREE_CNT; +} + /// Free items contained in a list /// /// @param[in,out] l List to clear. @@ -221,7 +260,7 @@ void tv_list_unref(list_T *const l) //{{{2 Add/remove -/// Remove items "item" to "item2" from list "l". +/// Remove items "item" to "item2" from list "l" /// /// @warning Does not free the listitem or the value! /// @@ -251,6 +290,30 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, l->lv_idx_item = NULL; } +/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" +/// +/// @param[out] l List to move from. +/// @param[in] item First item to move. +/// @param[in] item2 Last item to move. +/// @param[out] tgt_l List to move to. +/// @param[in] cnt Number of items moved. +void tv_list_move_items(list_T *const l, listitem_T *const item, + listitem_T *const item2, list_T *const tgt_l, + const int cnt) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item2); + item->li_prev = tgt_l->lv_last; + item2->li_next = NULL; + if (tgt_l->lv_last == NULL) { + tgt_l->lv_first = item; + } else { + tgt_l->lv_last->li_next = item; + } + tgt_l->lv_last = item2; + tgt_l->lv_len += cnt; +} + /// Insert list item /// /// @param[out] l List to insert to. @@ -326,7 +389,7 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - tv_copy(tv, &li->li_tv); + tv_copy(tv, TV_LIST_ITEM_TV(li)); tv_list_append(l, li); } @@ -339,13 +402,11 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = itemlist; + TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_list = itemlist; tv_list_append(list, li); - if (itemlist != NULL) { - itemlist->lv_refcount++; - } + tv_list_ref(itemlist); } /// Append a dictionary to a list @@ -357,9 +418,9 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_dict = dict; + TV_LIST_ITEM_TV(li)->v_type = VAR_DICT; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_dict = dict; tv_list_append(list, li); if (dict != NULL) { dict->dv_refcount++; @@ -399,9 +460,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) listitem_T *const li = tv_list_item_alloc(); tv_list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = (char_u *)str; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str; } /// Append number to the list @@ -412,9 +473,9 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) void tv_list_append_number(list_T *const l, const varnumber_T n) { listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_number = n; + TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER; + TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; + TV_LIST_ITEM_TV(li)->vval.v_number = n; tv_list_append(l, li); } @@ -439,33 +500,35 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, } list_T *copy = tv_list_alloc(); + tv_list_ref(copy); if (copyID != 0) { // Do this before adding the items, because one of the items may // refer back to this list. orig->lv_copyID = copyID; orig->lv_copylist = copy; } - listitem_T *item; - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { + TV_LIST_ITER(orig, item, { + if (got_int) { + break; + } listitem_T *const ni = tv_list_item_alloc(); if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni), + deep, copyID) == FAIL) { xfree(ni); - break; + goto tv_list_copy_error; } } else { - tv_copy(&item->li_tv, &ni->li_tv); + tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni)); } tv_list_append(copy, ni); - } - copy->lv_refcount++; - if (item != NULL) { - tv_list_unref(copy); - copy = NULL; - } + }); return copy; + +tv_list_copy_error: + tv_list_unref(copy); + return NULL; } /// Extend first list with the second @@ -475,17 +538,17 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, /// @param[in] bef If not NULL, extends before this item. void tv_list_extend(list_T *const l1, list_T *const l2, listitem_T *const bef) - FUNC_ATTR_NONNULL_ARG(1, 2) + FUNC_ATTR_NONNULL_ARG(1) { - int todo = l2->lv_len; + int todo = tv_list_len(l2); listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first - ; item != NULL && --todo >= 0 + for (listitem_T *item = tv_list_first(l2) + ; item != NULL && todo-- ; item = (item == befbef ? saved_next : item->li_next)) { - tv_list_insert_tv(l1, &item->li_tv, bef); + tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef); } } @@ -540,13 +603,15 @@ static int list_join_inner(garray_T *const gap, list_T *const l, { size_t sumlen = 0; bool first = true; - listitem_T *item; // Stringify each item in the list. - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + TV_LIST_ITER(l, item, { + if (got_int) { + break; + } char *s; size_t len; - s = encode_tv2echo(&item->li_tv, &len); + s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); if (s == NULL) { return FAIL; } @@ -557,7 +622,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *)s; line_breakcheck(); - } + }); // Allocate result buffer with its total size, avoid re-allocation and // multiple copy operations. Add 2 for a tailing ']' and NUL. @@ -591,16 +656,16 @@ static int list_join_inner(garray_T *const gap, list_T *const l, /// /// @return OK in case of success, FAIL otherwise. int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1) { - if (l->lv_len < 1) { + if (!tv_list_len(l)) { return OK; } garray_T join_ga; int retval; - ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l)); retval = list_join_inner(gap, l, sep, &join_ga); #define FREE_JOIN_TOFREE(join) xfree((join)->tofree) @@ -632,11 +697,13 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return false; } - listitem_T *item1 = l1->lv_first; - listitem_T *item2 = l2->lv_first; + listitem_T *item1 = tv_list_first(l1); + listitem_T *item2 = tv_list_first(l2); for (; item1 != NULL && item2 != NULL - ; item1 = item1->li_next, item2 = item2->li_next) { - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + ; (item1 = TV_LIST_ITEM_NEXT(l1, item1), + item2 = TV_LIST_ITEM_NEXT(l2, item2))) { + if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, + recursive)) { return false; } } @@ -644,6 +711,31 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } +/// Reverse list in-place +/// +/// @param[in,out] l List to reverse. +void tv_list_reverse(list_T *const l) +{ + if (tv_list_len(l) <= 1) { + return; + } +#define SWAP(a, b) \ + do { \ + tmp = a; \ + a = b; \ + b = tmp; \ + } while (0) + listitem_T *tmp; + + SWAP(l->lv_first, l->lv_last); + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + SWAP(li->li_next, li->li_prev); + } +#undef SWAP + + l->lv_idx = l->lv_len - l->lv_idx - 1; +} + //{{{2 Indexing/searching /// Locate item with a given index in a list and return it @@ -662,13 +754,8 @@ listitem_T *tv_list_find(list_T *const l, int n) return NULL; } - // Negative index is relative to the end. - if (n < 0) { - n = l->lv_len + n; - } - - // Check for index out of range. - if (n < 0 || n >= l->lv_len) { + n = tv_list_uidx(l, n); + if (n == -1) { return NULL; } @@ -740,7 +827,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) } return -1; } - return tv_get_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error); } /// Get list item l[n] as a string @@ -757,7 +844,7 @@ const char *tv_list_find_str(list_T *const l, const int n) emsgf(_(e_listidx), (int64_t)n); return NULL; } - return tv_get_string(&li->li_tv); + return tv_get_string(TV_LIST_ITEM_TV(li)); } /// Locate item in a list and return its index @@ -772,15 +859,14 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) if (l == NULL) { return -1; } - long idx = 0; - const listitem_T *li; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (li == item) { + return idx; + } idx++; - } - if (li == NULL) { - return -1; - } - return idx; + }); + return -1; } //{{{1 Dictionaries @@ -1339,7 +1425,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - list->lv_refcount++; + tv_list_ref(list); if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; @@ -1677,7 +1763,7 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv) ret_tv->vval.v_list = l; ret_tv->v_type = VAR_LIST; ret_tv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; + tv_list_ref(l); return l; } @@ -2032,9 +2118,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_LIST: { - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } + tv_list_ref(to->vval.v_list); break; } case VAR_DICT: { @@ -2090,9 +2174,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, l->lv_lock); if (deep < 0 || deep > 1) { // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - tv_item_lock(&li->li_tv, deep - 1, lock); - } + TV_LIST_ITER(l, li, { + tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock); + }); } } break; @@ -2128,6 +2212,8 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) /// Check whether VimL value is locked itself or refers to a locked container /// +/// @warning Fixed container is not the same as locked. +/// /// @param[in] tv Value to check. /// /// @return True if value is locked, false otherwise. @@ -2136,8 +2222,7 @@ bool tv_islocked(const typval_T *const tv) { return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock == VAR_LOCKED)) + && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index c44b85644d..2bce7bd6b2 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -6,6 +6,8 @@ #include <stdint.h> #include <string.h> #include <stdbool.h> +#include <assert.h> +#include <limits.h> #include "nvim/types.h" #include "nvim/hashtab.h" @@ -26,6 +28,9 @@ typedef uint64_t uvarnumber_T; /// Type used for VimL VAR_FLOAT values typedef double float_T; +/// Refcount for dict or list that should not be freed +enum { DO_NOT_FREE_CNT = (INT_MAX / 2) }; + /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX @@ -150,12 +155,26 @@ struct listvar_S { list_T *lv_used_prev; ///< Previous list in used lists list. }; -// Static list with 10 items. Use init_static_list() to initialize. +// Static list with 10 items. Use tv_list_init_static10() to initialize. typedef struct { list_T sl_list; // must be first listitem_T sl_items[10]; } staticList10_T; +#define TV_LIST_STATIC10_INIT { \ + .sl_list = { \ + .lv_first = NULL, \ + .lv_last = NULL, \ + .lv_refcount = 0, \ + .lv_len = 0, \ + .lv_watch = NULL, \ + .lv_idx_item = NULL, \ + .lv_lock = VAR_FIXED, \ + .lv_used_next = NULL, \ + .lv_used_prev = NULL, \ + }, \ + } + // Structure to hold an item of a Dictionary. // Also used for a variable. // The key is copied into "di_key" to avoid an extra alloc/free for it. @@ -284,13 +303,71 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline long tv_list_len(const list_T *const l) +/// Increase reference count for a given list +/// +/// Does nothing for NULL lists. +/// +/// @param[in] l List to modify. +static inline void tv_list_ref(list_T *const l) +{ + if (l == NULL) { + return; + } + l->lv_refcount++; +} + +static inline VarLockStatus tv_list_locked(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get list lock status +/// +/// Returns VAR_FIXED for NULL lists. +/// +/// @param[in] l List to check. +static inline VarLockStatus tv_list_locked(const list_T *const l) +{ + if (l == NULL) { + return VAR_FIXED; + } + return l->lv_lock; +} + +/// Set list lock status +/// +/// May only “set” VAR_FIXED for NULL lists. +/// +/// @param[out] l List to modify. +/// @param[in] lock New lock status. +static inline void tv_list_set_lock(list_T *const l, + const VarLockStatus lock) +{ + if (l == NULL) { + assert(lock == VAR_FIXED); + return; + } + l->lv_lock = lock; +} + +/// Set list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[out] l List to modify. +/// @param[in] copyid New copyID. +static inline void tv_list_set_copyid(list_T *const l, + const int copyid) + FUNC_ATTR_NONNULL_ALL +{ + l->lv_copyID = copyid; +} + +static inline int tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *const l) { if (l == NULL) { return 0; @@ -298,6 +375,118 @@ static inline long tv_list_len(const list_T *const l) return l->lv_len; } +static inline int tv_list_copyid(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline int tv_list_copyid(const list_T *const l) +{ + return l->lv_copyID; +} + +static inline list_T *tv_list_latest_copy(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get latest list copy +/// +/// Gets lv_copylist field assigned by tv_list_copy() earlier. +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline list_T *tv_list_latest_copy(const list_T *const l) +{ + return l->lv_copylist; +} + +/// Clear the list without freeing anything at all +/// +/// For use in sort() which saves items to a separate array and readds them back +/// after sorting via a number of tv_list_append() calls. +/// +/// @param[out] l List to clear. +static inline void tv_list_clear(list_T *const l) +{ + l->lv_first = NULL; + l->lv_last = NULL; + l->lv_idx_item = NULL; + l->lv_len = 0; +} + +static inline int tv_list_uidx(const list_T *const l, int n) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Normalize index: that is, return either -1 or non-negative index +/// +/// @param[in] l List to index. Used to get length. +/// @param[in] n List index, possibly negative. +/// +/// @return -1 or list index in range [0, tv_list_len(l)). +static inline int tv_list_uidx(const list_T *const l, int n) +{ + // Negative index is relative to the end. + if (n < 0) { + n += tv_list_len(l); + } + + // Check for index out of range. + if (n < 0 || n >= tv_list_len(l)) { + return -1; + } + return n; +} + +static inline bool tv_list_has_watchers(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check whether list has watchers +/// +/// E.g. is referenced by a :for loop. +/// +/// @param[in] l List to check. +/// +/// @return true if there are watchers, false otherwise. +static inline bool tv_list_has_watchers(const list_T *const l) +{ + return l && l->lv_watch; +} + +static inline listitem_T *tv_list_first(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get first list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_first(const list_T *const l) +{ + if (l == NULL) { + return NULL; + } + return l->lv_first; +} + +static inline listitem_T *tv_list_last(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get last list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_last(const list_T *const l) +{ + if (l == NULL) { + return NULL; + } + return l->lv_last; +} + static inline long tv_dict_len(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -352,6 +541,75 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a list +/// +/// @param modifier Modifier: expected to be const or nothing, volatile should +/// also work if you have any uses for the volatile list. +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define _TV_LIST_ITER_MOD(modifier, l, li, code) \ + do { \ + modifier list_T *const l_ = (l); \ + if (l_ != NULL) { \ + for (modifier listitem_T *li = l_->lv_first; \ + li != NULL; li = li->li_next) { \ + code \ + } \ + } \ + } while (0) + +/// Iterate over a list +/// +/// To be used when you need to modify list or values you iterate over, use +/// #TV_LIST_ITER_CONST if you don’t. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER(l, li, code) \ + _TV_LIST_ITER_MOD(, l, li, code) + +/// Iterate over a list +/// +/// To be used when you don’t need to modify list or values you iterate over, +/// use #TV_LIST_ITER if you do. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER_CONST(l, li, code) \ + _TV_LIST_ITER_MOD(const, l, li, code) + +// Below macros are macros to avoid duplicating code for functionally identical +// const and non-const function variants. + +/// Get typval_T out of list item +/// +/// @param[in] li List item to get typval_T from, must not be NULL. +/// +/// @return Pointer to typval_T. +#define TV_LIST_ITEM_TV(li) (&(li)->li_tv) + +/// Get next list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the next item or NULL. +#define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next) + +/// Get previous list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the previous item or NULL. +#define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev) +// List argument is not used currently, but it is a must for lists implemented +// as a pair (size(in list), array) without terminator - basically for lists on +// top of kvec. + /// Iterate over a dictionary /// /// @param[in] d Dictionary to iterate over. diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index df820f664c..f2d0d7265f 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( break; } case VAR_LIST: { - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { + if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_LIST(tv); break; } - const int saved_copyID = tv->vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(tv->vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .type = kMPConvList, @@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = tv->vval.v_list, - .li = tv->vval.v_list->lv_first, + .li = tv_list_first(tv->vval.v_list), }, }, })); @@ -440,23 +440,43 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( // bits is not checked), other unsigned and have at most 31 // non-zero bits (number of bits is not checked). if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 4 - || val_list->lv_first->li_tv.v_type != VAR_NUMBER - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER - || (highest_bits = - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER - || (high_bits = - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_tv.v_type != VAR_NUMBER - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { + || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) { goto _convert_one_value_regular_dict; } - uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) - | (uint64_t)(((uint64_t)high_bits) << 31) - | (uint64_t)low_bits); + + const listitem_T *const sign_li = tv_list_first(val_list); + if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER + || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const highest_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, sign_li)); + if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER + || ((highest_bits + = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const high_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, highest_bits_li)); + if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER + || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const low_bits_li = tv_list_last(val_list); + if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER + || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) + | (uint64_t)(((uint64_t)high_bits) << 31) + | (uint64_t)low_bits); if (sign > 0) { TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number); } else { @@ -495,12 +515,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( if (val_di->di_tv.v_type != VAR_LIST) { goto _convert_one_value_regular_dict; } - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, - val_di->di_tv.vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START( + tv, tv_list_len(val_di->di_tv.vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_di->di_tv.vval.v_list, - .li = val_di->di_tv.vval.v_list->lv_first, + .li = tv_list_first(val_di->di_tv.vval.v_list), }, }, })); @@ -520,22 +540,21 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( goto _convert_one_value_regular_dict; } list_T *const val_list = val_di->di_tv.vval.v_list; - if (val_list == NULL || val_list->lv_len == 0) { + if (val_list == NULL || tv_list_len(val_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR); break; } - for (const listitem_T *li = val_list->lv_first; li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_LIST - || li->li_tv.vval.v_list->lv_len != 2) { + TV_LIST_ITER_CONST(val_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) { goto _convert_one_value_regular_dict; } - } - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + }); + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, kMPConvPairs); TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, - val_list->lv_len); + tv_list_len(val_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_list, - .li = val_list->lv_first, + .li = tv_list_first(val_list), }, }, })); @@ -554,18 +573,23 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const list_T *val_list; varnumber_T type; if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 2 - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX + || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2 + || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type + != VAR_NUMBER) + || ((type + = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) + > INT8_MAX) || type < INT8_MIN - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { + || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type + != VAR_LIST)) { goto _convert_one_value_regular_dict; } size_t len; char *buf; - if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, - &len, &buf)) { + if (!( + encode_vim_list_to_buf( + TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, + &buf))) { goto _convert_one_value_regular_dict; } TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); @@ -671,40 +695,45 @@ typval_encode_stop_converting_one_item: case kMPConvList: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv); } - tv = &cur_mpsv->data.l.li->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPairs: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS( cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); } - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; + const list_T *const kv_pair = ( + TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list); TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK( - encode_vim_to__error_ret, kv_pair->lv_first->li_tv); - if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, - &mpstack, cur_mpsv, - &kv_pair->lv_first->li_tv, - copyID, - objname) == FAIL) { + encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair))); + if ( + _TYPVAL_ENCODE_CONVERT_ONE_VALUE( + TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv, + TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname) + == FAIL) { goto encode_vim_to__error_ret; } TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); - tv = &kv_pair->lv_last->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPartial: { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 8616508d88..4f54d4c88b 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6334,7 +6334,6 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) void ex_oldfiles(exarg_T *eap) { list_T *l = get_vim_var_list(VV_OLDFILES); - listitem_T *li; long nr = 0; if (l == NULL) { @@ -6342,19 +6341,22 @@ void ex_oldfiles(exarg_T *eap) } else { msg_start(); msg_scroll = true; - for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { + TV_LIST_ITER(l, li, { + if (got_int) { + break; + } nr++; - const char *fname = tv_get_string(&li->li_tv); + const char *fname = tv_get_string(TV_LIST_ITEM_TV(li)); if (!message_filtered((char_u *)fname)) { msg_outnum(nr); MSG_PUTS(": "); - msg_outtrans((char_u *)tv_get_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li))); msg_clr_eos(); msg_putchar('\n'); ui_flush(); // output one line at a time os_breakcheck(); } - } + }); // Assume "got_int" was set to truncate the listing. got_int = false; @@ -6364,7 +6366,7 @@ void ex_oldfiles(exarg_T *eap) quit_more = false; nr = prompt_for_number(false); msg_starthere(); - if (nr > 0 && nr <= l->lv_len) { + if (nr > 0 && nr <= tv_list_len(l)) { const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1adc8325f8..a5a8804e1c 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2618,20 +2618,20 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) } varnumber_T prev_end = 0; int i = 0; - for (const listitem_T *li = tv.vval.v_list->lv_first; - li != NULL; li = li->li_next, i++) { - if (li->li_tv.v_type != VAR_LIST) { + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) { PRINT_ERRMSG(_("E5401: List item %i is not a List"), i); goto color_cmdline_error; } - const list_T *const l = li->li_tv.vval.v_list; + const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list; if (tv_list_len(l) != 3) { PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"), i, tv_list_len(l)); goto color_cmdline_error; } bool error = false; - const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error); + const varnumber_T start = ( + tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error)); if (error) { goto color_cmdline_error; } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) { @@ -2651,8 +2651,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) .attr = 0, })); } - const varnumber_T end = tv_get_number_chk(&l->lv_first->li_next->li_tv, - &error); + const varnumber_T end = tv_get_number_chk( + TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error); if (error) { goto color_cmdline_error; } else if (!(start < end && end <= colored_ccline->cmdlen)) { @@ -2668,7 +2668,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) goto color_cmdline_error; } prev_end = end; - const char *const group = tv_get_string_chk(&l->lv_last->li_tv); + const char *const group = tv_get_string_chk( + TV_LIST_ITEM_TV(tv_list_last(l))); if (group == NULL) { goto color_cmdline_error; } @@ -2679,7 +2680,8 @@ static bool color_cmdline(CmdlineInfo *colored_ccline) .end = end, .attr = attr, })); - } + i++; + }); if (prev_end < colored_ccline->cmdlen) { kv_push(ccline_colors->colors, ((CmdlineColorChunk) { .start = prev_end, @@ -5021,24 +5023,24 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, */ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) { - list_T *retlist; - listitem_T *li; - garray_T ga; - - retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, - num_file, file); + list_T *const retlist = call_user_expand_func( + (user_expand_func_T)call_func_retlist, xp, num_file, file); if (retlist == NULL) { return FAIL; } + garray_T ga; ga_init(&ga, (int)sizeof(char *), 3); - /* Loop over the items in the list. */ - for (li = retlist->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL) - continue; /* Skip non-string items and empty strings */ + // Loop over the items in the list. + TV_LIST_ITER_CONST(retlist, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING + || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) { + continue; // Skip non-string items and empty strings. + } - GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); - } + GA_APPEND(char *, &ga, xstrdup( + (const char *)TV_LIST_ITEM_TV(li)->vval.v_string)); + }); tv_list_unref(retlist); *file = ga.ga_data; diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index cacba3ce87..8303ecdd37 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -214,9 +214,9 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) list_T *const kv_pair = tv_list_alloc(); tv_list_append_list(cur.tv->vval.v_list, kv_pair); listitem_T *const key = tv_list_item_alloc(); - key->li_tv = decode_string(s, len, kTrue, false, false); + *TV_LIST_ITEM_TV(key) = decode_string(s, len, kTrue, false, false); tv_list_append(kv_pair, key); - if (key->li_tv.v_type == VAR_UNKNOWN) { + if (TV_LIST_ITEM_TV(key)->v_type == VAR_UNKNOWN) { ret = false; tv_list_unref(kv_pair); continue; @@ -224,7 +224,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) listitem_T *const val = tv_list_item_alloc(); tv_list_append(kv_pair, val); kv_push(stack, cur); - cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; + cur = (TVPopStackItem) { TV_LIST_ITEM_TV(val), false, false, 0 }; } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { @@ -239,7 +239,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } } else { assert(cur.tv->v_type == VAR_LIST); - lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); + lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); if (lua_isnil(lstate, -1)) { lua_pop(lstate, 2); continue; @@ -247,7 +247,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) listitem_T *const li = tv_list_item_alloc(); tv_list_append(cur.tv->vval.v_list, li); kv_push(stack, cur); - cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; + cur = (TVPopStackItem) { TV_LIST_ITEM_TV(li), false, false, 0 }; } } assert(!cur.container); @@ -306,7 +306,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) case kObjectTypeArray: { cur.tv->v_type = VAR_LIST; cur.tv->vval.v_list = tv_list_alloc(); - cur.tv->vval.v_list->lv_refcount++; + tv_list_ref(cur.tv->vval.v_list); if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 432c9a8b47..a5e131190d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2568,7 +2568,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(list, (const char *)reg->y_array[i], -1); } - list->lv_lock = VAR_FIXED; + tv_list_set_lock(list, VAR_FIXED); tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type @@ -4854,9 +4854,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = tv_list_alloc(); - tv_list_append_string(list, NULL, 0); - list->lv_first->li_tv.vval.v_string = s; + list_T *const list = tv_list_alloc(); + tv_list_append_allocated_string(list, (char *)s); return list; } @@ -5610,13 +5609,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) list_T *res = result.vval.v_list; list_T *lines = NULL; - if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { - lines = res->lv_first->li_tv.vval.v_list; - if (res->lv_last->li_tv.v_type != VAR_STRING) { + if (tv_list_len(res) == 2 + && TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) { + lines = TV_LIST_ITEM_TV(tv_list_first(res))->vval.v_list; + if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) { goto err; } - char_u *regtype = res->lv_last->li_tv.vval.v_string; - if (regtype == NULL || strlen((char*)regtype) > 1) { + char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string; + if (regtype == NULL || strlen((char *)regtype) > 1) { goto err; } switch (regtype[0]) { @@ -5641,20 +5641,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) reg->y_type = kMTUnknown; } - reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *)); - reg->y_size = (size_t)lines->lv_len; + reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *)); + reg->y_size = (size_t)tv_list_len(lines); reg->additional_data = NULL; reg->timestamp = 0; // Timestamp is not saved for clipboard registers because clipboard registers // are not saved in the ShaDa file. int i = 0; - for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { + TV_LIST_ITER_CONST(lines, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); - } + reg->y_array[i++] = (char_u *)xstrdupnul( + (const char *)TV_LIST_ITEM_TV(li)->vval.v_string); + }); if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { // a known-to-be charwise yank might have a final linebreak diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index c5d03e73f1..f85009dca8 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -157,6 +157,7 @@ typedef struct { FILE *fd; typval_T *tv; char_u *p_str; + list_T *p_list; listitem_T *p_li; buf_T *buf; linenr_T buflnum; @@ -518,17 +519,17 @@ static int qf_get_next_list_line(qfstate_T *state) // Get the next line from the supplied list while (p_li != NULL - && (p_li->li_tv.v_type != VAR_STRING - || p_li->li_tv.vval.v_string == NULL)) { - p_li = p_li->li_next; // Skip non-string items + && (TV_LIST_ITEM_TV(p_li)->v_type != VAR_STRING + || TV_LIST_ITEM_TV(p_li)->vval.v_string == NULL)) { + p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); // Skip non-string items. } - if (p_li == NULL) { // End of the list + if (p_li == NULL) { // End of the list. state->p_li = NULL; return QF_END_OF_INPUT; } - len = STRLEN(p_li->li_tv.vval.v_string); + len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -536,9 +537,10 @@ static int qf_get_next_list_line(qfstate_T *state) state->linelen = len; } - STRLCPY(state->linebuf, p_li->li_tv.vval.v_string, state->linelen + 1); + STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string, + state->linelen + 1); - state->p_li = p_li->li_next; // next item + state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); return QF_OK; } @@ -1089,7 +1091,8 @@ qf_init_ext( if (tv->v_type == VAR_STRING) { state.p_str = tv->vval.v_string; } else if (tv->v_type == VAR_LIST) { - state.p_li = tv->vval.v_list->lv_first; + state.p_list = tv->vval.v_list; + state.p_li = tv_list_first(tv->vval.v_list); } state.tv = tv; } @@ -4164,7 +4167,6 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, int action) { - listitem_T *li; dict_T *d; qfline_T *old_last = NULL; int retval = OK; @@ -4181,13 +4183,15 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, qf_store_title(qi, title); } - for (li = list->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_DICT) - continue; /* Skip non-dict items */ + TV_LIST_ITER_CONST(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { + continue; // Skip non-dict items. + } - d = li->li_tv.vval.v_dict; - if (d == NULL) + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + if (d == NULL) { continue; + } char *const filename = tv_dict_get_string(d, "filename", true); int bufnum = (int)tv_dict_get_number(d, "bufnr"); @@ -4244,7 +4248,7 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, retval = FAIL; break; } - } + }); if (qi->qf_lists[qi->qf_curlist].qf_index == 0) { // no valid entry @@ -4576,7 +4580,7 @@ void ex_cexpr(exarg_T *eap) typval_T tv; if (eval0(eap->arg, &tv, NULL, true) != FAIL) { if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) - || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + || tv.v_type == VAR_LIST) { if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index ec9916f4a2..ddc3681867 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -232,17 +232,17 @@ #define LAST_NL NUPPER + ADD_NL #define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL) -#define MOPEN 80 /* -89 Mark this point in input as start of - * \( subexpr. MOPEN + 0 marks start of - * match. */ -#define MCLOSE 90 /* -99 Analogous to MOPEN. MCLOSE + 0 marks - * end of match. */ -#define BACKREF 100 /* -109 node Match same string again \1-\9 */ - -# define ZOPEN 110 /* -119 Mark this point in input as start of - * \z( subexpr. */ -# define ZCLOSE 120 /* -129 Analogous to ZOPEN. */ -# define ZREF 130 /* -139 node Match external submatch \z1-\z9 */ +#define MOPEN 80 // -89 Mark this point in input as start of + // \( … \) subexpr. MOPEN + 0 marks start of + // match. +#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks + // end of match. +#define BACKREF 100 // -109 node Match same string again \1-\9. + +# define ZOPEN 110 // -119 Mark this point in input as start of + // \z( … \) subexpr. +# define ZCLOSE 120 // -129 Analogous to ZOPEN. +# define ZREF 130 // -139 node Match external submatch \z1-\z9 #define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */ @@ -462,11 +462,11 @@ static int toggle_Magic(int x) #define IEMSG_RET_NULL(m) return (IEMSG(m), rc_did_emsg = true, (void *)NULL) #define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL) #define EMSG2_RET_NULL(m, c) \ - return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL) + return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL) #define EMSG2_RET_FAIL(m, c) \ - return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL) + return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL) #define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \ - "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL) + "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL) #define MAX_LIMIT (32767L << 16L) @@ -6474,41 +6474,35 @@ static regsubmatch_T rsm; // can only be used when can_f_submatch is true /// vim_regsub_both(). static int fill_submatch_list(int argc, typval_T *argv, int argcount) { - listitem_T *li; - int i; - char_u *s; - if (argcount == 0) { // called function doesn't take an argument return 0; } // Relies on sl_list to be the first item in staticList10_T. - init_static_list((staticList10_T *)(argv->vval.v_list)); + tv_list_init_static10((staticList10_T *)argv->vval.v_list); // There are always 10 list items in staticList10_T. - li = argv->vval.v_list->lv_first; - for (i = 0; i < 10; i++) { - s = rsm.sm_match->startp[i]; + listitem_T *li = tv_list_first(argv->vval.v_list); + for (int i = 0; i < 10; i++) { + char_u *s = rsm.sm_match->startp[i]; if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s)); } - li->li_tv.v_type = VAR_STRING; - li->li_tv.vval.v_string = s; - li = li->li_next; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->vval.v_string = s; + li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li); } return 1; } static void clear_submatch_list(staticList10_T *sl) { - int i; - - for (i = 0; i < 10; i++) { - xfree(sl->sl_items[i].li_tv.vval.v_string); - } + TV_LIST_ITER(&sl->sl_list, li, { + xfree(TV_LIST_ITEM_TV(li)->vval.v_string); + }); } /// vim_regsub() - perform substitutions after a vim_regexec() or @@ -6642,13 +6636,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, typval_T argv[2]; int dummy; typval_T rettv; - staticList10_T matchList; + staticList10_T matchList = TV_LIST_STATIC10_INIT; rettv.v_type = VAR_STRING; rettv.vval.v_string = NULL; argv[0].v_type = VAR_LIST; argv[0].vval.v_list = &matchList.sl_list; - matchList.sl_list.lv_len = 0; if (expr->v_type == VAR_FUNC) { s = expr->vval.v_string; call_func(s, (int)STRLEN(s), &rettv, 1, argv, @@ -6662,7 +6655,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, fill_submatch_list, 0L, 0L, &dummy, true, partial, NULL); } - if (matchList.sl_list.lv_len > 0) { + if (tv_list_len(&matchList.sl_list) > 0) { // fill_submatch_list() was called. clear_submatch_list(&matchList); } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 736d6bf162..6bf816bb74 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1180,8 +1180,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES); const bool force = flags & kShaDaForceit; const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) - && (force || oldfiles_list == NULL - || oldfiles_list->lv_len == 0)); + && (force || tv_list_len(oldfiles_list) == 0)); const bool want_marks = flags & kShaDaWantMarks; const unsigned srni_flags = (unsigned) ( (flags & kShaDaWantInfo @@ -1599,13 +1598,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, #define DUMP_ADDITIONAL_ELEMENTS(src, what) \ do { \ if ((src) != NULL) { \ - for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ - if (encode_vim_to_msgpack(spacker, &li->li_tv, \ + TV_LIST_ITER((src), li, { \ + if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \ _("additional elements of ShaDa " what)) \ == FAIL) { \ goto shada_pack_entry_error; \ } \ - } \ + }); \ } \ } while (0) #define DUMP_ADDITIONAL_DATA(src, what) \ @@ -1647,25 +1646,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, case kSDItemHistoryEntry: { const bool is_hist_search = entry.data.history_item.histtype == HIST_SEARCH; - const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) ( - entry.data.history_item.additional_elements == NULL - ? 0 - : entry.data.history_item.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( + tv_list_len(entry.data.history_item.additional_elements)); msgpack_pack_array(spacker, arr_size); msgpack_pack_uint8(spacker, entry.data.history_item.histtype); PACK_BIN(cstr_as_string(entry.data.history_item.string)); if (is_hist_search) { - msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); } DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, "history entry item"); break; } case kSDItemVariable: { - const size_t arr_size = 2 + (size_t) ( - entry.data.global_var.additional_elements == NULL - ? 0 - : entry.data.global_var.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)( + tv_list_len(entry.data.global_var.additional_elements)); msgpack_pack_array(spacker, arr_size); const String varname = cstr_as_string(entry.data.global_var.name); PACK_BIN(varname); @@ -1684,10 +1679,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, break; } case kSDItemSubString: { - const size_t arr_size = 1 + (size_t) ( - entry.data.sub_string.additional_elements == NULL - ? 0 - : entry.data.sub_string.additional_elements->lv_len); + const size_t arr_size = 1 + (size_t)( + tv_list_len(entry.data.sub_string.additional_elements)); msgpack_pack_array(spacker, arr_size); PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c837038659..d2b2575f6a 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3213,26 +3213,25 @@ spell_find_suggest ( // Find suggestions by evaluating expression "expr". static void spell_suggest_expr(suginfo_T *su, char_u *expr) { - list_T *list; - listitem_T *li; int score; const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. // First evaluate the expression and get the resulting list. - list = eval_spell_expr(su->su_badword, expr); + list_T *const list = eval_spell_expr(su->su_badword, expr); if (list != NULL) { // Loop over the items in the list. - for (li = list->lv_first; li != NULL; li = li->li_next) - if (li->li_tv.v_type == VAR_LIST) { + TV_LIST_ITER(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { // Get the word and the score from the items. - score = get_spellword(li->li_tv.vval.v_list, &p); + score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p); if (score >= 0 && score <= su->su_maxscore) { add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, score, 0, true, su->su_sallang, false); } } + }); tv_list_unref(list); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 3c181aca65..dbe1222dc0 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1532,22 +1532,24 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, // DECSCUSR (cursor shape) sequence is widely supported by several terminal // types. https://github.com/gnachman/iTerm2/pull/92 // xterm extension: vertical bar - if (!konsole && ((xterm && !vte_version) // anything claiming xterm compat - // per MinTTY 0.4.3-1 release notes from 2009 - || putty - // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 - || (vte_version >= 3900) - || tmux // per tmux manual page - // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html - || screen - || st // #7641 - || rxvt // per command.C - // per analysis of VT100Terminal.m - || iterm || iterm_pretending_xterm - || teraterm // per TeraTerm "Supported Control Functions" doco - // Some linux-type terminals (such as console-terminal-emulator - // from the nosh toolset) implement the xterm extension. - || (linuxvt && (xterm_version || (vte_version > 0) || colorterm)))) { + if (!konsole + && ((xterm && !vte_version) // anything claiming xterm compat + // per MinTTY 0.4.3-1 release notes from 2009 + || putty + // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 + || (vte_version >= 3900) + || tmux // per tmux manual page + // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html + || screen + || st // #7641 + || rxvt // per command.C + // per analysis of VT100Terminal.m + || iterm || iterm_pretending_xterm + || teraterm // per TeraTerm "Supported Control Functions" doco + // Some linux-type terminals implement the xterm extension. + // Example: console-terminal-emulator from the nosh toolset. + || (linuxvt + && (xterm_version || (vte_version > 0) || colorterm)))) { data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q"); if (-1 == data->unibi_ext.reset_cursor_style) { diff --git a/src/nvim/window.c b/src/nvim/window.c index 63c9361663..5e85a9bede 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5606,49 +5606,48 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, } // Set up position matches - if (pos_list != NULL) - { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - listitem_T *li; - int i; - - for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; - i++, li = li->li_next) { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - list_T *subl; - listitem_T *subli; + if (pos_list != NULL) { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + + int i = 0; + TV_LIST_ITER(pos_list, li, { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; bool error = false; - if (li->li_tv.v_type == VAR_LIST) { - subl = li->li_tv.vval.v_list; - if (subl == NULL) { - goto fail; - } - subli = subl->lv_first; + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { + const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; + const listitem_T *subli = tv_list_first(subl); if (subli == NULL) { + emsgf(_("E5030: Empty list at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } - lnum = tv_get_number_chk(&subli->li_tv, &error); + lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); if (error) { goto fail; } - if (lnum == 0) { - --i; + if (lnum <= 0) { continue; } m->pos.pos[i].lnum = lnum; - subli = subli->li_next; + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - col = tv_get_number_chk(&subli->li_tv, &error); + col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); if (error) { goto fail; } - subli = subli->li_next; + if (col < 0) { + continue; + } + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - len = tv_get_number_chk(&subli->li_tv, &error); + len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (len < 0) { + continue; + } if (error) { goto fail; } @@ -5656,16 +5655,16 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, } m->pos.pos[i].col = col; m->pos.pos[i].len = len; - } else if (li->li_tv.v_type == VAR_NUMBER) { - if (li->li_tv.vval.v_number == 0) { - --i; + } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { + if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { continue; } - m->pos.pos[i].lnum = li->li_tv.vval.v_number; + m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; m->pos.pos[i].col = 0; m->pos.pos[i].len = 0; } else { - EMSG(_("List or number required")); + emsgf(_("E5031: List or number required at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } if (toplnum == 0 || lnum < toplnum) { @@ -5674,7 +5673,11 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, if (botlnum == 0 || lnum >= botlnum) { botlnum = lnum + 1; } - } + i++; + if (i >= MAXPOSMATCH) { + break; + } + }); // Calculate top and bottom lines for redrawing area if (toplnum != 0){ |