To: vim_dev@googlegroups.com Subject: Patch 8.2.2336 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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, '\ but got number') + CheckDefFailure(['extendnew({a: 1}, [42])'], 'E1013: Argument 2: type mismatch, expected dict but got list') + CheckDefFailure(['extendnew([1, 2], "x")'], 'E1013: Argument 2: type mismatch, expected list but got string') + CheckDefFailure(['extendnew([1, 2], {x: 1})'], 'E1013: Argument 2: type mismatch, expected list but got dict') + 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 -- Bram@Moolenaar.net -- 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 ///