Patch 8.2.3867
Problem:    Implementation of some list functions too complicated.
Solution:   Refactor do_sort_uniq(), f_count() and extend() (Yegappan
            Lakshmanan, closes #9378)
Files:      src/list.c


*** ../vim-8.2.3866/src/list.c  2021-12-19 10:35:10.700109727 +0000
--- src/list.c  2021-12-21 13:15:24.697233945 +0000
***************
*** 1987,2209 ****
  }
  
  /*
!  * "sort()" or "uniq()" function
   */
      static void
! do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
  {
-     list_T    *l;
-     listitem_T        *li;
-     sortItem_T        *ptrs;
-     sortinfo_T        *old_sortinfo;
-     sortinfo_T        info;
      long      len;
!     long      i;
  
!     if (in_vim9script()
!           && (check_for_list_arg(argvars, 0) == FAIL
!               || (argvars[1].v_type != VAR_UNKNOWN
!                   && check_for_opt_dict_arg(argvars, 2) == FAIL)))
!       return;
  
!     // Pointer to current info struct used in compare function. Save and
!     // restore the current one for nested calls.
!     old_sortinfo = sortinfo;
!     sortinfo = &info;
  
!     if (argvars[0].v_type != VAR_LIST)
!       semsg(_(e_listarg), sort ? "sort()" : "uniq()");
      else
      {
!       l = argvars[0].vval.v_list;
!       if (l != NULL && value_check_lock(l->lv_lock,
!            (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
!                                                                       TRUE))
!           goto theend;
!       rettv_list_set(rettv, l);
!       if (l == NULL)
!           goto theend;
!       CHECK_LIST_MATERIALIZE(l);
  
!       len = list_len(l);
!       if (len <= 1)
!           goto theend;        // short list sorts pretty quickly
! 
!       info.item_compare_ic = FALSE;
!       info.item_compare_lc = FALSE;
!       info.item_compare_numeric = FALSE;
!       info.item_compare_numbers = FALSE;
! #ifdef FEAT_FLOAT
!       info.item_compare_float = FALSE;
! #endif
!       info.item_compare_func = NULL;
!       info.item_compare_partial = NULL;
!       info.item_compare_selfdict = NULL;
!       if (argvars[1].v_type != VAR_UNKNOWN)
!       {
!           // optional second argument: {func}
!           if (argvars[1].v_type == VAR_FUNC)
!               info.item_compare_func = argvars[1].vval.v_string;
!           else if (argvars[1].v_type == VAR_PARTIAL)
!               info.item_compare_partial = argvars[1].vval.v_partial;
            else
!           {
!               int         error = FALSE;
!               int         nr = 0;
  
!               if (argvars[1].v_type == VAR_NUMBER)
!               {
!                   nr = tv_get_number_chk(&argvars[1], &error);
!                   if (error)
!                       goto theend;    // type error; errmsg already given
!                   if (nr == 1)
!                       info.item_compare_ic = TRUE;
!               }
!               if (nr != 1)
!               {
!                   if (argvars[1].v_type != VAR_NUMBER)
!                       info.item_compare_func = tv_get_string(&argvars[1]);
!                   else if (nr != 0)
!                   {
!                       emsg(_(e_invarg));
!                       goto theend;
!                   }
!               }
!               if (info.item_compare_func != NULL)
!               {
!                   if (*info.item_compare_func == NUL)
!                   {
!                       // empty string means default sort
!                       info.item_compare_func = NULL;
!                   }
!                   else if (STRCMP(info.item_compare_func, "n") == 0)
!                   {
!                       info.item_compare_func = NULL;
!                       info.item_compare_numeric = TRUE;
!                   }
!                   else if (STRCMP(info.item_compare_func, "N") == 0)
!                   {
!                       info.item_compare_func = NULL;
!                       info.item_compare_numbers = TRUE;
!                   }
  #ifdef FEAT_FLOAT
!                   else if (STRCMP(info.item_compare_func, "f") == 0)
!                   {
!                       info.item_compare_func = NULL;
!                       info.item_compare_float = TRUE;
!                   }
  #endif
!                   else if (STRCMP(info.item_compare_func, "i") == 0)
!                   {
!                       info.item_compare_func = NULL;
!                       info.item_compare_ic = TRUE;
!                   }
!                   else if (STRCMP(info.item_compare_func, "l") == 0)
!                   {
!                       info.item_compare_func = NULL;
!                       info.item_compare_lc = TRUE;
!                   }
!               }
!           }
  
!           if (argvars[2].v_type != VAR_UNKNOWN)
            {
!               // optional third argument: {dict}
!               if (argvars[2].v_type != VAR_DICT)
!               {
!                   emsg(_(e_dictreq));
!                   goto theend;
!               }
!               info.item_compare_selfdict = argvars[2].vval.v_dict;
            }
        }
! 
!       // Make an array with each entry pointing to an item in the List.
!       ptrs = ALLOC_MULT(sortItem_T, len);
!       if (ptrs == NULL)
!           goto theend;
! 
!       i = 0;
!       if (sort)
        {
!           // sort(): ptrs will be the list to sort
!           FOR_ALL_LIST_ITEMS(l, li)
            {
!               ptrs[i].item = li;
!               ptrs[i].idx = i;
!               ++i;
!           }
! 
!           info.item_compare_func_err = FALSE;
!           info.item_compare_keep_zero = FALSE;
!           // test the compare function
!           if ((info.item_compare_func != NULL
!                                        || info.item_compare_partial != NULL)
!                   && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
!                                                        == ITEM_COMPARE_FAIL)
!               emsg(_("E702: Sort compare function failed"));
!           else
            {
!               // Sort the array with item pointers.
!               qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
!                   info.item_compare_func == NULL
!                                         && info.item_compare_partial == NULL
!                                              ? item_compare : item_compare2);
! 
!               if (!info.item_compare_func_err)
!               {
!                   // Clear the List and append the items in sorted order.
!                   l->lv_first = l->lv_u.mat.lv_last
!                                             = l->lv_u.mat.lv_idx_item = NULL;
!                   l->lv_len = 0;
!                   for (i = 0; i < len; ++i)
!                       list_append(l, ptrs[i].item);
!               }
            }
!       }
!       else
!       {
!           int (*item_compare_func_ptr)(const void *, const void *);
! 
!           // f_uniq(): ptrs will be a stack of items to remove
!           info.item_compare_func_err = FALSE;
!           info.item_compare_keep_zero = TRUE;
!           item_compare_func_ptr = info.item_compare_func != NULL
!                                         || info.item_compare_partial != NULL
!                                              ? item_compare2 : item_compare;
! 
!           for (li = l->lv_first; li != NULL && li->li_next != NULL;
!                                                            li = li->li_next)
!           {
!               if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
!                                                                        == 0)
!                   ptrs[i++].item = li;
!               if (info.item_compare_func_err)
!               {
!                   emsg(_("E882: Uniq compare function failed"));
!                   break;
!               }
            }
! 
!           if (!info.item_compare_func_err)
            {
!               while (--i >= 0)
!               {
!                   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_u.mat.lv_last = ptrs[i].item;
!                   list_fix_watch(l, li);
!                   listitem_free(l, li);
!                   l->lv_len--;
!               }
            }
        }
  
!       vim_free(ptrs);
      }
  theend:
      sortinfo = old_sortinfo;
  }
--- 1987,2255 ----
  }
  
  /*
!  * sort() List "l"
   */
      static void
! do_sort(list_T *l, sortinfo_T *info)
  {
      long      len;
!     sortItem_T        *ptrs;
!     long      i = 0;
!     listitem_T        *li;
  
!     len = list_len(l);
  
!     // Make an array with each entry pointing to an item in the List.
!     ptrs = ALLOC_MULT(sortItem_T, len);
!     if (ptrs == NULL)
!       return;
  
!     // sort(): ptrs will be the list to sort
!     FOR_ALL_LIST_ITEMS(l, li)
!     {
!       ptrs[i].item = li;
!       ptrs[i].idx = i;
!       ++i;
!     }
! 
!     info->item_compare_func_err = FALSE;
!     info->item_compare_keep_zero = FALSE;
!     // test the compare function
!     if ((info->item_compare_func != NULL
!               || info->item_compare_partial != NULL)
!           && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
!           == ITEM_COMPARE_FAIL)
!       emsg(_("E702: Sort compare function failed"));
      else
      {
!       // Sort the array with item pointers.
!       qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
!               info->item_compare_func == NULL
!               && info->item_compare_partial == NULL
!               ? item_compare : item_compare2);
! 
!       if (!info->item_compare_func_err)
!       {
!           // Clear the List and append the items in sorted order.
!           l->lv_first = l->lv_u.mat.lv_last
!               = l->lv_u.mat.lv_idx_item = NULL;
!           l->lv_len = 0;
!           for (i = 0; i < len; ++i)
!               list_append(l, ptrs[i].item);
!       }
!     }
  
!     vim_free(ptrs);
! }
! 
! /*
!  * uniq() List "l"
!  */
!     static void
! do_uniq(list_T *l, sortinfo_T *info)
! {
!     long      len;
!     sortItem_T        *ptrs;
!     long      i = 0;
!     listitem_T        *li;
!     int       (*item_compare_func_ptr)(const void *, const void *);
! 
!     len = list_len(l);
! 
!     // Make an array with each entry pointing to an item in the List.
!     ptrs = ALLOC_MULT(sortItem_T, len);
!     if (ptrs == NULL)
!       return;
! 
!     // f_uniq(): ptrs will be a stack of items to remove
!     info->item_compare_func_err = FALSE;
!     info->item_compare_keep_zero = TRUE;
!     item_compare_func_ptr = info->item_compare_func != NULL
!       || info->item_compare_partial != NULL
!       ? item_compare2 : item_compare;
! 
!     for (li = l->lv_first; li != NULL && li->li_next != NULL;
!           li = li->li_next)
!     {
!       if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
!               == 0)
!           ptrs[i++].item = li;
!       if (info->item_compare_func_err)
!       {
!           emsg(_("E882: Uniq compare function failed"));
!           break;
!       }
!     }
! 
!     if (!info->item_compare_func_err)
!     {
!       while (--i >= 0)
!       {
!           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_u.mat.lv_last = ptrs[i].item;
!           list_fix_watch(l, li);
!           listitem_free(l, li);
!           l->lv_len--;
!       }
!     }
  
!     vim_free(ptrs);
! }
! 
! /*
!  * Parse the optional arguments to sort() and uniq() and return the values in
!  * 'info'.
!  */
!     static int
! parse_sort_uniq_args(typval_T *argvars, sortinfo_T *info)
! {
!     info->item_compare_ic = FALSE;
!     info->item_compare_lc = FALSE;
!     info->item_compare_numeric = FALSE;
!     info->item_compare_numbers = FALSE;
  #ifdef FEAT_FLOAT
!     info->item_compare_float = FALSE;
  #endif
!     info->item_compare_func = NULL;
!     info->item_compare_partial = NULL;
!     info->item_compare_selfdict = NULL;
! 
!     if (argvars[1].v_type == VAR_UNKNOWN)
!       return OK;
! 
!     // optional second argument: {func}
!     if (argvars[1].v_type == VAR_FUNC)
!       info->item_compare_func = argvars[1].vval.v_string;
!     else if (argvars[1].v_type == VAR_PARTIAL)
!       info->item_compare_partial = argvars[1].vval.v_partial;
!     else
!     {
!       int         error = FALSE;
!       int         nr = 0;
  
!       if (argvars[1].v_type == VAR_NUMBER)
!       {
!           nr = tv_get_number_chk(&argvars[1], &error);
!           if (error)
!               return FAIL;
!           if (nr == 1)
!               info->item_compare_ic = TRUE;
!       }
!       if (nr != 1)
!       {
!           if (argvars[1].v_type != VAR_NUMBER)
!               info->item_compare_func = tv_get_string(&argvars[1]);
!           else if (nr != 0)
            {
!               emsg(_(e_invarg));
!               return FAIL;
            }
        }
!       if (info->item_compare_func != NULL)
        {
!           if (*info->item_compare_func == NUL)
            {
!               // empty string means default sort
!               info->item_compare_func = NULL;
!           }
!           else if (STRCMP(info->item_compare_func, "n") == 0)
            {
!               info->item_compare_func = NULL;
!               info->item_compare_numeric = TRUE;
            }
!           else if (STRCMP(info->item_compare_func, "N") == 0)
!           {
!               info->item_compare_func = NULL;
!               info->item_compare_numbers = TRUE;
            }
! #ifdef FEAT_FLOAT
!           else if (STRCMP(info->item_compare_func, "f") == 0)
            {
!               info->item_compare_func = NULL;
!               info->item_compare_float = TRUE;
!           }
! #endif
!           else if (STRCMP(info->item_compare_func, "i") == 0)
!           {
!               info->item_compare_func = NULL;
!               info->item_compare_ic = TRUE;
!           }
!           else if (STRCMP(info->item_compare_func, "l") == 0)
!           {
!               info->item_compare_func = NULL;
!               info->item_compare_lc = TRUE;
            }
        }
+     }
+ 
+     if (argvars[2].v_type != VAR_UNKNOWN)
+     {
+       // optional third argument: {dict}
+       if (argvars[2].v_type != VAR_DICT)
+       {
+           emsg(_(e_dictreq));
+           return FAIL;
+       }
+       info->item_compare_selfdict = argvars[2].vval.v_dict;
+     }
+ 
+     return OK;
+ }
+ 
+ /*
+  * "sort()" or "uniq()" function
+  */
+     static void
+ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
+ {
+     list_T    *l;
+     sortinfo_T        *old_sortinfo;
+     sortinfo_T        info;
+     long      len;
+ 
+     if (in_vim9script()
+           && (check_for_list_arg(argvars, 0) == FAIL
+               || (argvars[1].v_type != VAR_UNKNOWN
+                   && check_for_opt_dict_arg(argvars, 2) == FAIL)))
+       return;
  
!     if (argvars[0].v_type != VAR_LIST)
!     {
!       semsg(_(e_listarg), sort ? "sort()" : "uniq()");
!       return;
      }
+ 
+     // Pointer to current info struct used in compare function. Save and
+     // restore the current one for nested calls.
+     old_sortinfo = sortinfo;
+     sortinfo = &info;
+ 
+     l = argvars[0].vval.v_list;
+     if (l != NULL && value_check_lock(l->lv_lock,
+               (char_u *)(sort ? N_("sort() argument") : N_("uniq() 
argument")),
+               TRUE))
+       goto theend;
+     rettv_list_set(rettv, l);
+     if (l == NULL)
+       goto theend;
+     CHECK_LIST_MATERIALIZE(l);
+ 
+     len = list_len(l);
+     if (len <= 1)
+       goto theend;    // short list sorts pretty quickly
+ 
+     if (parse_sort_uniq_args(argvars, &info) == FAIL)
+       goto theend;
+ 
+     if (sort)
+       do_sort(l, &info);
+     else
+       do_uniq(l, &info);
+ 
  theend:
      sortinfo = old_sortinfo;
  }
***************
*** 2835,2840 ****
--- 2881,2985 ----
  }
  
  /*
+  * Count the number of times "needle" occurs in string "haystack". Case is
+  * ignored if "ic" is TRUE.
+  */
+     static long
+ count_string(char_u *haystack, char_u *needle, int ic)
+ {
+     long      n = 0;
+     char_u    *p = haystack;
+     char_u    *next;
+ 
+     if (p == NULL || needle == NULL || *needle == NUL)
+       return 0;
+ 
+     if (ic)
+     {
+       size_t len = STRLEN(needle);
+ 
+       while (*p != NUL)
+       {
+           if (MB_STRNICMP(p, needle, len) == 0)
+           {
+               ++n;
+               p += len;
+           }
+           else
+               MB_PTR_ADV(p);
+       }
+     }
+     else
+       while ((next = (char_u *)strstr((char *)p, (char *)needle)) != NULL)
+       {
+           ++n;
+           p = next + STRLEN(needle);
+       }
+ 
+     return n;
+ }
+ 
+ /*
+  * Count the number of times item "needle" occurs in List "l" starting at 
index
+  * "idx". Case is ignored if "ic" is TRUE.
+  */
+     static long
+ count_list(list_T *l, typval_T *needle, long idx, int ic)
+ {
+     long      n = 0;
+     listitem_T        *li;
+ 
+     if (l == NULL)
+       return 0;
+ 
+     CHECK_LIST_MATERIALIZE(l);
+ 
+     if (list_len(l) == 0)
+       return 0;
+ 
+     li = list_find(l, idx);
+     if (li == NULL)
+     {
+       semsg(_(e_listidx), idx);
+       return 0;
+     }
+ 
+     for ( ; li != NULL; li = li->li_next)
+       if (tv_equal(&li->li_tv, needle, ic, FALSE))
+           ++n;
+ 
+     return n;
+ }
+ 
+ /*
+  * Count the number of times item "needle" occurs in Dict "d". Case is ignored
+  * if "ic" is TRUE.
+  */
+     static long
+ count_dict(dict_T *d, typval_T *needle, int ic)
+ {
+     int               todo;
+     hashitem_T        *hi;
+     long      n = 0;
+ 
+     if (d == NULL)
+       return 0;
+ 
+     todo = (int)d->dv_hashtab.ht_used;
+     for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
+     {
+       if (!HASHITEM_EMPTY(hi))
+       {
+           --todo;
+           if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
+               ++n;
+       }
+     }
+ 
+     return n;
+ }
+ 
+ /*
   * "count()" function
   */
      void
***************
*** 2854,2952 ****
      if (argvars[2].v_type != VAR_UNKNOWN)
        ic = (int)tv_get_bool_chk(&argvars[2], &error);
  
!     if (argvars[0].v_type == VAR_STRING)
!     {
!       char_u *expr = tv_get_string_chk(&argvars[1]);
!       char_u *p = argvars[0].vval.v_string;
!       char_u *next;
! 
!       if (!error && expr != NULL && *expr != NUL && p != NULL)
!       {
!           if (ic)
!           {
!               size_t len = STRLEN(expr);
  
!               while (*p != NUL)
!               {
!                   if (MB_STRNICMP(p, expr, len) == 0)
!                   {
!                       ++n;
!                       p += len;
!                   }
!                   else
!                       MB_PTR_ADV(p);
!               }
!           }
!           else
!               while ((next = (char_u *)strstr((char *)p, (char *)expr))
!                                                                      != NULL)
!               {
!                   ++n;
!                   p = next + STRLEN(expr);
!               }
!       }
  
      }
!     else if (argvars[0].v_type == VAR_LIST)
      {
!       listitem_T      *li;
!       list_T          *l;
!       long            idx;
  
!       if ((l = argvars[0].vval.v_list) != NULL)
        {
!           CHECK_LIST_MATERIALIZE(l);
!           li = l->lv_first;
!           if (argvars[2].v_type != VAR_UNKNOWN)
            {
!               if (argvars[3].v_type != VAR_UNKNOWN)
                {
!                   idx = (long)tv_get_number_chk(&argvars[3], &error);
!                   if (!error)
!                   {
!                       li = list_find(l, idx);
!                       if (li == NULL)
!                           semsg(_(e_listidx), idx);
!                   }
                }
-               if (error)
-                   li = NULL;
            }
  
!           for ( ; li != NULL; li = li->li_next)
!               if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
!                   ++n;
        }
      }
!     else if (argvars[0].v_type == VAR_DICT)
      {
!       int             todo;
!       dict_T          *d;
!       hashitem_T      *hi;
  
!       if ((d = argvars[0].vval.v_dict) != NULL)
        {
!           if (argvars[2].v_type != VAR_UNKNOWN)
!           {
!               if (argvars[3].v_type != VAR_UNKNOWN)
!                   emsg(_(e_invarg));
!           }
  
!           todo = error ? 0 : (int)d->dv_hashtab.ht_used;
!           for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
            {
!               if (!HASHITEM_EMPTY(hi))
!               {
!                   --todo;
!                   if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
!                       ++n;
!               }
            }
        }
      }
-     else
-       semsg(_(e_listdictarg), "count()");
-     rettv->vval.v_number = n;
  }
  
  /*
--- 2999,3170 ----
      if (argvars[2].v_type != VAR_UNKNOWN)
        ic = (int)tv_get_bool_chk(&argvars[2], &error);
  
!     if (!error && argvars[0].v_type == VAR_STRING)
!       n = count_string(argvars[0].vval.v_string,
!                                       tv_get_string_chk(&argvars[1]), ic);
!     else if (!error && argvars[0].v_type == VAR_LIST)
!     {
!       long idx = 0;
! 
!       if (argvars[2].v_type != VAR_UNKNOWN
!               && argvars[3].v_type != VAR_UNKNOWN)
!           idx = (long)tv_get_number_chk(&argvars[3], &error);
!       if (!error)
!           n = count_list(argvars[0].vval.v_list, &argvars[1], idx, ic);
!     }
!     else if (!error && argvars[0].v_type == VAR_DICT)
!     {
!       if (argvars[2].v_type != VAR_UNKNOWN
!               && argvars[3].v_type != VAR_UNKNOWN)
!           emsg(_(e_invarg));
!       else
!           n = count_dict(argvars[0].vval.v_dict, &argvars[1], ic);
!     }
!     else
!       semsg(_(e_listdictarg), "count()");
!     rettv->vval.v_number = n;
! }
  
! /*
!  * extend() a List. Append List argvars[1] to List argvars[0] before index
!  * argvars[3] and return the resulting list in "rettv".  "is_new" is TRUE for
!  * extendnew().
!  */
!     static void
! extend_list(
!       typval_T        *argvars,
!       type_T          *type,
!       char            *func_name,
!       char_u          *arg_errmsg,
!       int             is_new,
!       typval_T        *rettv)
! {
!     list_T    *l1, *l2;
!     listitem_T        *item;
!     long      before;
!     int               error = FALSE;
  
+     l1 = argvars[0].vval.v_list;
+     if (l1 == NULL)
+     {
+       emsg(_(e_cannot_extend_null_list));
+       return;
      }
!     l2 = argvars[1].vval.v_list;
!     if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
!           && l2 != NULL)
      {
!       if (is_new)
!       {
!           l1 = list_copy(l1, FALSE, get_copyID());
!           if (l1 == NULL)
!               return;
!       }
  
!       if (argvars[2].v_type != VAR_UNKNOWN)
        {
!           before = (long)tv_get_number_chk(&argvars[2], &error);
!           if (error)
!               return;         // type error; errmsg already given
! 
!           if (before == l1->lv_len)
!               item = NULL;
!           else
            {
!               item = list_find(l1, before);
!               if (item == NULL)
                {
!                   semsg(_(e_listidx), before);
!                   return;
                }
            }
+       }
+       else
+           item = NULL;
+       if (type != NULL && check_typval_arg_type(
+                   type, &argvars[1], func_name, 2) == FAIL)
+           return;
+       list_extend(l1, l2, item);
  
!       if (is_new)
!       {
!           rettv->v_type = VAR_LIST;
!           rettv->vval.v_list = l1;
!           rettv->v_lock = FALSE;
        }
+       else
+           copy_tv(&argvars[0], rettv);
      }
! }
! 
! /*
!  * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
!  * resulting Dict in "rettv".  "is_new" is TRUE for extendnew().
!  */
!     static void
! extend_dict(
!       typval_T        *argvars,
!       type_T          *type,
!       char            *func_name,
!       char_u          *arg_errmsg,
!       int             is_new,
!       typval_T        *rettv)
! {
!     dict_T    *d1, *d2;
!     char_u    *action;
!     int       i;
! 
!     d1 = argvars[0].vval.v_dict;
!     if (d1 == NULL)
!     {
!       emsg(_(e_cannot_extend_null_dict));
!       return;
!     }
!     d2 = argvars[1].vval.v_dict;
!     if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
!           && d2 != NULL)
      {
!       if (is_new)
!       {
!           d1 = dict_copy(d1, FALSE, get_copyID());
!           if (d1 == NULL)
!               return;
!       }
  
!       // Check the third argument.
!       if (argvars[2].v_type != VAR_UNKNOWN)
        {
!           static char *(av[]) = {"keep", "force", "error"};
  
!           action = tv_get_string_chk(&argvars[2]);
!           if (action == NULL)
!               return;
!           for (i = 0; i < 3; ++i)
!               if (STRCMP(action, av[i]) == 0)
!                   break;
!           if (i == 3)
            {
!               semsg(_(e_invarg2), action);
!               return;
            }
        }
+       else
+           action = (char_u *)"force";
+ 
+       if (type != NULL && check_typval_arg_type(type, &argvars[1],
+                   func_name, 2) == FAIL)
+           return;
+       dict_extend(d1, d2, action, func_name);
+ 
+       if (is_new)
+       {
+           rettv->v_type = VAR_DICT;
+           rettv->vval.v_dict = d1;
+           rettv->v_lock = FALSE;
+       }
+       else
+           copy_tv(&argvars[0], rettv);
      }
  }
  
  /*
***************
*** 2967,3092 ****
      }
  
      if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
!     {
!       list_T          *l1, *l2;
!       listitem_T      *item;
!       long            before;
!       int             error = FALSE;
! 
!       l1 = argvars[0].vval.v_list;
!       if (l1 == NULL)
!       {
!           emsg(_(e_cannot_extend_null_list));
!           goto theend;
!       }
!       l2 = argvars[1].vval.v_list;
!       if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE))
!                                                                && l2 != NULL)
!       {
!           if (is_new)
!           {
!               l1 = list_copy(l1, FALSE, get_copyID());
!               if (l1 == NULL)
!                   goto theend;
!           }
! 
!           if (argvars[2].v_type != VAR_UNKNOWN)
!           {
!               before = (long)tv_get_number_chk(&argvars[2], &error);
!               if (error)
!                   goto theend;        // type error; errmsg already given
! 
!               if (before == l1->lv_len)
!                   item = NULL;
!               else
!               {
!                   item = list_find(l1, before);
!                   if (item == NULL)
!                   {
!                       semsg(_(e_listidx), before);
!                       goto theend;
!                   }
!               }
!           }
!           else
!               item = NULL;
!           if (type != NULL && check_typval_arg_type(
!                                     type, &argvars[1], func_name, 2) == FAIL)
!               goto theend;
!           list_extend(l1, l2, item);
! 
!           if (is_new)
!           {
!               rettv->v_type = VAR_LIST;
!               rettv->vval.v_list = l1;
!               rettv->v_lock = FALSE;
!           }
!           else
!               copy_tv(&argvars[0], rettv);
!       }
!     }
      else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
!     {
!       dict_T  *d1, *d2;
!       char_u  *action;
!       int     i;
! 
!       d1 = argvars[0].vval.v_dict;
!       if (d1 == NULL)
!       {
!           emsg(_(e_cannot_extend_null_dict));
!           goto theend;
!       }
!       d2 = argvars[1].vval.v_dict;
!       if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
!                                                                && d2 != NULL)
!       {
!           if (is_new)
!           {
!               d1 = dict_copy(d1, FALSE, get_copyID());
!               if (d1 == NULL)
!                   goto theend;
!           }
! 
!           // Check the third argument.
!           if (argvars[2].v_type != VAR_UNKNOWN)
!           {
!               static char *(av[]) = {"keep", "force", "error"};
! 
!               action = tv_get_string_chk(&argvars[2]);
!               if (action == NULL)
!                   goto theend;        // type error; errmsg already given
!               for (i = 0; i < 3; ++i)
!                   if (STRCMP(action, av[i]) == 0)
!                       break;
!               if (i == 3)
!               {
!                   semsg(_(e_invarg2), action);
!                   goto theend;
!               }
!           }
!           else
!               action = (char_u *)"force";
! 
!           if (type != NULL && check_typval_arg_type(type, &argvars[1],
!                                                        func_name, 2) == FAIL)
!               goto theend;
!           dict_extend(d1, d2, action, func_name);
! 
!           if (is_new)
!           {
!               rettv->v_type = VAR_DICT;
!               rettv->vval.v_dict = d1;
!               rettv->v_lock = FALSE;
!           }
!           else
!               copy_tv(&argvars[0], rettv);
!       }
!     }
      else
        semsg(_(e_listdictarg), func_name);
  
- theend:
      if (type != NULL)
        clear_type_list(&type_list);
  }
--- 3185,3196 ----
      }
  
      if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
!       extend_list(argvars, type, func_name, arg_errmsg, is_new, rettv);
      else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
!       extend_dict(argvars, type, func_name, arg_errmsg, is_new, rettv);
      else
        semsg(_(e_listdictarg), func_name);
  
      if (type != NULL)
        clear_type_list(&type_list);
  }
***************
*** 3308,3314 ****
  }
  
  /*
!  * reduce() on a List
   */
      static void
  reduce_list(
--- 3412,3420 ----
  }
  
  /*
!  * reduce() List argvars[0] using the function 'funcname' with arguments in
!  * 'funcexe' starting with the initial value argvars[2] and return the result
!  * in 'rettv'.
   */
      static void
  reduce_list(
***************
*** 3365,3371 ****
  }
  
  /*
!  * reduce() on a String
   */
      static void
  reduce_string(
--- 3471,3479 ----
  }
  
  /*
!  * reduce() String argvars[0] using the function 'funcname' with arguments in
!  * 'funcexe' starting with the initial value argvars[2] and return the result
!  * in 'rettv'.
   */
      static void
  reduce_string(
***************
*** 3414,3420 ****
  }
  
  /*
!  * reduce() on a Blob
   */
      static void
  reduce_blob(
--- 3522,3530 ----
  }
  
  /*
!  * reduce() Blob argvars[0] using the function 'funcname' with arguments in
!  * 'funcexe' starting with the initial value argvars[2] and return the result
!  * in 'rettv'.
   */
      static void
  reduce_blob(
***************
*** 3470,3475 ****
--- 3580,3587 ----
  
  /*
   * "reduce(list, { accumulator, element -> value } [, initial])" function
+  * "reduce(blob, { accumulator, element -> value } [, initial])"
+  * "reduce(string, { accumulator, element -> value } [, initial])"
   */
      void
  f_reduce(typval_T *argvars, typval_T *rettv)
*** ../vim-8.2.3866/src/version.c       2021-12-21 12:32:13.300529985 +0000
--- src/version.c       2021-12-21 13:10:18.677101619 +0000
***************
*** 751,752 ****
--- 751,754 ----
  {   /* Add new patch number below this line */
+ /**/
+     3867,
  /**/

-- 
How many light bulbs does it take to change a person?

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20211221132020.EB1211C0DCA%40moolenaar.net.

Raspunde prin e-mail lui