Patch 8.2.2336
Problem:    Vim9: it is not possible to extend a dictionary with different
            item types.
Solution:   Add extendnew(). (closes #7666)
Files:      runtime/doc/eval.txt, runtime/doc/usr_41.txt, src/evalfunc.c,
            src/list.c, src/proto/list.pro, src/testdir/test_listdict.vim,
            src/testdir/test_vim9_builtin.vim


*** ../vim-8.2.2335/runtime/doc/eval.txt        2021-01-10 20:22:20.763926913 
+0100
--- runtime/doc/eval.txt        2021-01-12 19:33:17.117646626 +0100
***************
*** 2488,2493 ****
--- 2524,2532 ----
  expandcmd({expr})             String  expand {expr} like with `:edit`
  extend({expr1}, {expr2} [, {expr3}])
                                List/Dict insert items of {expr2} into {expr1}
+ extendnew({expr1}, {expr2} [, {expr3}])
+                               List/Dict like |extend()| but creates a new
+                                       List or Dictionary
  feedkeys({string} [, {mode}]) Number  add key sequence to typeahead buffer
  filereadable({file})          Number  |TRUE| if {file} is a readable file
  filewritable({file})          Number  |TRUE| if {file} is a writable file
***************
*** 4477,4482 ****
--- 4523,4535 ----
                        mylist->extend(otherlist)
  
  
+ extendnew({expr1}, {expr2} [, {expr3}])                       *extendnew()*
+               Like |extend()| but instead of adding items to {expr1} a new
+               List or Dictionary is created and returned.  {expr1} remains
+               unchanged.  Items can still be changed by {expr2}, if you
+               don't want that use |deepcopy()| first.
+ 
+ 
  feedkeys({string} [, {mode}])                         *feedkeys()*
                Characters in {string} are queued for processing as if they
                come from a mapping or were typed by the user.
*** ../vim-8.2.2335/runtime/doc/usr_41.txt      2021-01-10 20:22:20.763926913 
+0100
--- runtime/doc/usr_41.txt      2021-01-12 19:37:45.661026590 +0100
***************
*** 632,637 ****
--- 640,646 ----
        insert()                insert an item somewhere in a List
        add()                   append an item to a List
        extend()                append a List to a List
+       extendnew()             make a new List and append items
        remove()                remove one or more items from a List
        copy()                  make a shallow copy of a List
        deepcopy()              make a full copy of a List
***************
*** 661,666 ****
--- 670,676 ----
        empty()                 check if Dictionary is empty
        remove()                remove an entry from a Dictionary
        extend()                add entries from one Dictionary to another
+       extendnew()             make a new Dictionary and append items
        filter()                remove selected entries from a Dictionary
        map()                   change each Dictionary entry
        mapnew()                make a new Dictionary with changed items
*** ../vim-8.2.2335/src/evalfunc.c      2021-01-10 22:42:46.916847071 +0100
--- src/evalfunc.c      2021-01-12 20:12:19.412281720 +0100
***************
*** 340,346 ****
  }
  
  /*
!  * Check "type" is the same type as the previous argument
   * Must not be used for the first argcheck_T entry.
   */
      static int
--- 340,346 ----
  }
  
  /*
!  * Check "type" is the same type as the previous argument.
   * Must not be used for the first argcheck_T entry.
   */
      static int
***************
*** 352,357 ****
--- 352,372 ----
  }
  
  /*
+  * Check "type" is the same basic type as the previous argument, checks list 
or
+  * dict vs other type, but not member type.
+  * Must not be used for the first argcheck_T entry.
+  */
+     static int
+ arg_same_struct_as_prev(type_T *type, argcontext_T *context)
+ {
+     type_T *prev_type = context->arg_types[context->arg_idx - 1];
+ 
+     if (prev_type->tt_type != context->arg_types[context->arg_idx]->tt_type)
+       return check_arg_type(prev_type, type, context->arg_idx + 1);
+     return OK;
+ }
+ 
+ /*
   * Check "type" is an item of the list or blob of the previous arg.
   * Must not be used for the first argcheck_T entry.
   */
***************
*** 394,399 ****
--- 409,415 ----
  argcheck_T arg1_float_or_nr[] = {arg_float_or_nr};
  argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev};
  argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3};
+ argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, 
arg_extend3};
  argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number};
  
  /*
***************
*** 877,882 ****
--- 893,900 ----
                        ret_string,         f_expandcmd},
      {"extend",                2, 3, FEARG_1,      arg23_extend,
                        ret_first_arg,      f_extend},
+     {"extendnew",     2, 3, FEARG_1,      arg23_extendnew,
+                       ret_first_cont,     f_extendnew},
      {"feedkeys",      1, 2, FEARG_1,      NULL,
                        ret_void,           f_feedkeys},
      {"file_readable", 1, 1, FEARG_1,      NULL,       // obsolete
*** ../vim-8.2.2335/src/list.c  2021-01-10 22:42:46.920847063 +0100
--- src/list.c  2021-01-12 19:44:56.271925504 +0100
***************
*** 2454,2467 ****
  }
  
  /*
!  * "extend(list, list [, idx])" function
!  * "extend(dict, dict [, action])" function
   */
!     void
! f_extend(typval_T *argvars, typval_T *rettv)
  {
-     char_u      *arg_errmsg = (char_u *)N_("extend() argument");
- 
      if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
      {
        list_T          *l1, *l2;
--- 2454,2464 ----
  }
  
  /*
!  * "extend()" or "extendnew()" function.  "is_new" is TRUE for extendnew().
   */
!     static void
! extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
  {
      if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
      {
        list_T          *l1, *l2;
***************
*** 2476,2483 ****
            return;
        }
        l2 = argvars[1].vval.v_list;
!       if (!value_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL)
        {
            if (argvars[2].v_type != VAR_UNKNOWN)
            {
                before = (long)tv_get_number_chk(&argvars[2], &error);
--- 2473,2488 ----
            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);
***************
*** 2500,2506 ****
                item = NULL;
            list_extend(l1, l2, item);
  
!           copy_tv(&argvars[0], rettv);
        }
      }
      else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT)
--- 2505,2518 ----
                item = NULL;
            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)
***************
*** 2516,2523 ****
            return;
        }
        d2 = argvars[1].vval.v_dict;
!       if (!value_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL)
        {
            // Check the third argument.
            if (argvars[2].v_type != VAR_UNKNOWN)
            {
--- 2528,2543 ----
            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)
            {
***************
*** 2540,2550 ****
  
            dict_extend(d1, d2, action);
  
!           copy_tv(&argvars[0], rettv);
        }
      }
      else
!       semsg(_(e_listdictarg), "extend()");
  }
  
  /*
--- 2560,2601 ----
  
            dict_extend(d1, d2, action);
  
!           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), is_new ? "extendnew()" : "extend()");
! }
! 
! /*
!  * "extend(list, list [, idx])" function
!  * "extend(dict, dict [, action])" function
!  */
!     void
! f_extend(typval_T *argvars, typval_T *rettv)
! {
!     char_u      *errmsg = (char_u *)N_("extend() argument");
! 
!     extend(argvars, rettv, errmsg, FALSE);
! }
! 
! /*
!  * "extendnew(list, list [, idx])" function
!  * "extendnew(dict, dict [, action])" function
!  */
!     void
! f_extendnew(typval_T *argvars, typval_T *rettv)
! {
!     char_u      *errmsg = (char_u *)N_("extendnew() argument");
! 
!     extend(argvars, rettv, errmsg, TRUE);
  }
  
  /*
*** ../vim-8.2.2335/src/proto/list.pro  2020-11-09 18:31:30.548791857 +0100
--- src/proto/list.pro  2021-01-12 19:43:28.936155853 +0100
***************
*** 52,57 ****
--- 52,58 ----
  void f_add(typval_T *argvars, typval_T *rettv);
  void f_count(typval_T *argvars, typval_T *rettv);
  void f_extend(typval_T *argvars, typval_T *rettv);
+ void f_extendnew(typval_T *argvars, typval_T *rettv);
  void f_insert(typval_T *argvars, typval_T *rettv);
  void f_remove(typval_T *argvars, typval_T *rettv);
  void f_reverse(typval_T *argvars, typval_T *rettv);
*** ../vim-8.2.2335/src/testdir/test_listdict.vim       2020-11-04 
12:23:01.328933876 +0100
--- src/testdir/test_listdict.vim       2021-01-12 19:42:50.240256970 +0100
***************
*** 864,869 ****
--- 864,881 ----
    call assert_fails("call extend(g:, {'-!' : 10})", 'E461:')
  endfunc
  
+ func Test_listdict_extendnew()
+   " Test extendnew() with lists
+   let l = [1, 2, 3]
+   call assert_equal([1, 2, 3, 4, 5], extendnew(l, [4, 5]))
+   call assert_equal([1, 2, 3], l)
+ 
+   " Test extend() with dictionaries.
+   let d = {'a': {'b': 'B'}}
+   call assert_equal({'a': {'b': 'B'}, 'c': 'cc'}, extendnew(d, {'c': 'cc'}))
+   call assert_equal({'a': {'b': 'B'}}, d)
+ endfunc
+ 
  func s:check_scope_dict(x, fixed)
    func s:gen_cmd(cmd, x)
      return substitute(a:cmd, '\<x\ze:', a:x, 'g')
*** ../vim-8.2.2335/src/testdir/test_vim9_builtin.vim   2021-01-10 
22:42:46.920847063 +0100
--- src/testdir/test_vim9_builtin.vim   2021-01-12 20:16:55.355667190 +0100
***************
*** 243,248 ****
--- 243,258 ----
    CheckDefFailure(['extend({a: 1}, {b: 2}, 1)'], 'E1013: Argument 3: type 
mismatch, expected string but got number')
  enddef
  
+ def Test_extendnew()
+   assert_equal([1, 2, 'a'], extendnew([1, 2], ['a']))
+   assert_equal({one: 1, two: 'a'}, extendnew({one: 1}, {two: 'a'}))
+ 
+   CheckDefFailure(['extendnew({a: 1}, 42)'], 'E1013: Argument 2: type 
mismatch, expected dict<number> but got number')
+   CheckDefFailure(['extendnew({a: 1}, [42])'], 'E1013: Argument 2: type 
mismatch, expected dict<number> but got list<number>')
+   CheckDefFailure(['extendnew([1, 2], "x")'], 'E1013: Argument 2: type 
mismatch, expected list<number> but got string')
+   CheckDefFailure(['extendnew([1, 2], {x: 1})'], 'E1013: Argument 2: type 
mismatch, expected list<number> but got dict<number>')
+ enddef
+ 
  def Test_extend_return_type()
    var l = extend([1, 2], [3])
    var res = 0
*** ../vim-8.2.2335/src/version.c       2021-01-12 18:58:36.443811813 +0100
--- src/version.c       2021-01-12 20:03:45.553201775 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2336,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
132. You come back and check this list every half-hour.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202101121924.10CJO8jJ1043658%40masaka.moolenaar.net.

Raspunde prin e-mail lui