diff options
| author | Justin M. Keyes <justinkz@gmail.com> | 2017-12-23 18:17:01 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-23 18:17:01 +0100 | 
| commit | dee78a4095a27369e428572f74f7b64bcc5f670e (patch) | |
| tree | 4f06de0fd7a5a80d746c2ffaf18cb0719e66cccd /src | |
| parent | ec86f4215fc58246998c6017df84206153d0df1a (diff) | |
| parent | 5cb7a709e7f60b0e7bcde70a1aa9fea5f060fe0f (diff) | |
| download | rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.tar.gz rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.tar.bz2 rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.zip | |
Merge #7708 from ZyX-I/hide-container-impl
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){ | 
