]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.2336: Vim9: not possible to extend dictionary with different type v8.2.2336
authorBram Moolenaar <Bram@vim.org>
Tue, 12 Jan 2021 19:23:40 +0000 (20:23 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 12 Jan 2021 19:23:40 +0000 (20:23 +0100)
Problem:    Vim9: it is not possible to extend a dictionary with different
            item types.
Solution:   Add extendnew(). (closes #7666)

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
src/version.c

index 7848534b5da795bfa6f9ec023404c467c8f00292..d31ba2d36231fdcc7223222ded2364d87f68316e 100644 (file)
@@ -2524,6 +2524,9 @@ expand({expr} [, {nosuf} [, {list}]])
 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
@@ -4520,6 +4523,13 @@ extend({expr1}, {expr2} [, {expr3}])                     *extend()*
                        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.
index 7d4e3a2b091e79d2a7c808e867e43647232746e7..624bb934f54ac1116f805df4d1eaf515497f5835 100644 (file)
@@ -640,6 +640,7 @@ List manipulation:                                  *list-functions*
        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
@@ -669,6 +670,7 @@ Dictionary manipulation:                            *dict-functions*
        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
index 1abcd5e11ccecba7673b1b2c166834878306af69..0d17b9ed79d28c697625758427496b60d052c8cf 100644 (file)
@@ -340,7 +340,7 @@ arg_list_or_dict(type_T *type, argcontext_T *context)
 }
 
 /*
- * Check "type" is the same type as the previous argument
+ * Check "type" is the same type as the previous argument.
  * Must not be used for the first argcheck_T entry.
  */
     static int
@@ -351,6 +351,21 @@ arg_same_as_prev(type_T *type, argcontext_T *context)
     return check_arg_type(prev_type, type, context->arg_idx + 1);
 }
 
+/*
+ * 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,6 +409,7 @@ arg_extend3(type_T *type, argcontext_T *context)
 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,6 +893,8 @@ static funcentry_T global_functions[] =
                        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
index 2b44ebacb85d670d8382450a25296ecdff7ac0ab..f7842fa875d7065bef8e4084119fa5effac3057a 100644 (file)
@@ -2454,14 +2454,11 @@ f_count(typval_T *argvars, typval_T *rettv)
 }
 
 /*
- * "extend(list, list [, idx])" function
- * "extend(dict, dict [, action])" function
+ * "extend()" or "extendnew()" function.  "is_new" is TRUE for extendnew().
  */
-    void
-f_extend(typval_T *argvars, typval_T *rettv)
+    static void
+extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new)
 {
-    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;
@@ -2476,8 +2473,16 @@ f_extend(typval_T *argvars, typval_T *rettv)
            return;
        }
        l2 = argvars[1].vval.v_list;
-       if (!value_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL)
+       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,7 +2505,14 @@ f_extend(typval_T *argvars, typval_T *rettv)
                item = NULL;
            list_extend(l1, l2, item);
 
-           copy_tv(&argvars[0], rettv);
+           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,8 +2528,16 @@ f_extend(typval_T *argvars, typval_T *rettv)
            return;
        }
        d2 = argvars[1].vval.v_dict;
-       if (!value_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL)
+       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,11 +2560,42 @@ f_extend(typval_T *argvars, typval_T *rettv)
 
            dict_extend(d1, d2, action);
 
-           copy_tv(&argvars[0], rettv);
+           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), "extend()");
+       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);
 }
 
 /*
index 26990509ff552804cf129beb6d2f52e6069e5143..b77add546bc457cb4dc0f01133449fbfac5aba85 100644 (file)
@@ -52,6 +52,7 @@ void f_mapnew(typval_T *argvars, typval_T *rettv);
 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);
index 762a5170806e2ed3ac16584f877c6f994a56b1d3..051a37c3a533e92d59325218270326c2b31936cf 100644 (file)
@@ -864,6 +864,18 @@ func Test_listdict_extend()
   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')
index 673f0c05627226a506f1c6bf8c2f00eb19a07869..c867266c2deb724e5029b46b1719e5c5b97d6fad 100644 (file)
@@ -243,6 +243,16 @@ def Test_extend_arg_types()
   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
index 1832108d392fb83383808187392676018c17b9f6..d4b5dde22cf51b769c7383fb3dc0edbae98a1068 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2336,
 /**/
     2335,
 /**/