patch 8.2.2336: Vim9: not possible to extend dictionary with different type
Problem: Vim9: it is not possible to extend a dictionary with different
item types.
Solution: Add extendnew(). (closes #7666)
This commit is contained in:
@ -2524,6 +2524,9 @@ expand({expr} [, {nosuf} [, {list}]])
|
|||||||
expandcmd({expr}) String expand {expr} like with `:edit`
|
expandcmd({expr}) String expand {expr} like with `:edit`
|
||||||
extend({expr1}, {expr2} [, {expr3}])
|
extend({expr1}, {expr2} [, {expr3}])
|
||||||
List/Dict insert items of {expr2} into {expr1}
|
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
|
feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer
|
||||||
filereadable({file}) Number |TRUE| if {file} is a readable file
|
filereadable({file}) Number |TRUE| if {file} is a readable file
|
||||||
filewritable({file}) Number |TRUE| if {file} is a writable file
|
filewritable({file}) Number |TRUE| if {file} is a writable file
|
||||||
@ -4520,6 +4523,13 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()*
|
|||||||
mylist->extend(otherlist)
|
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()*
|
feedkeys({string} [, {mode}]) *feedkeys()*
|
||||||
Characters in {string} are queued for processing as if they
|
Characters in {string} are queued for processing as if they
|
||||||
come from a mapping or were typed by the user.
|
come from a mapping or were typed by the user.
|
||||||
|
|||||||
@ -640,6 +640,7 @@ List manipulation: *list-functions*
|
|||||||
insert() insert an item somewhere in a List
|
insert() insert an item somewhere in a List
|
||||||
add() append an item to a List
|
add() append an item to a List
|
||||||
extend() append a List 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
|
remove() remove one or more items from a List
|
||||||
copy() make a shallow copy of a List
|
copy() make a shallow copy of a List
|
||||||
deepcopy() make a full 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
|
empty() check if Dictionary is empty
|
||||||
remove() remove an entry from a Dictionary
|
remove() remove an entry from a Dictionary
|
||||||
extend() add entries from one Dictionary to another
|
extend() add entries from one Dictionary to another
|
||||||
|
extendnew() make a new Dictionary and append items
|
||||||
filter() remove selected entries from a Dictionary
|
filter() remove selected entries from a Dictionary
|
||||||
map() change each Dictionary entry
|
map() change each Dictionary entry
|
||||||
mapnew() make a new Dictionary with changed items
|
mapnew() make a new Dictionary with changed items
|
||||||
|
|||||||
@ -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.
|
* Must not be used for the first argcheck_T entry.
|
||||||
*/
|
*/
|
||||||
static int
|
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);
|
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.
|
* Check "type" is an item of the list or blob of the previous arg.
|
||||||
* Must not be used for the first argcheck_T entry.
|
* 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 arg1_float_or_nr[] = {arg_float_or_nr};
|
||||||
argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev};
|
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_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};
|
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},
|
ret_string, f_expandcmd},
|
||||||
{"extend", 2, 3, FEARG_1, arg23_extend,
|
{"extend", 2, 3, FEARG_1, arg23_extend,
|
||||||
ret_first_arg, f_extend},
|
ret_first_arg, f_extend},
|
||||||
|
{"extendnew", 2, 3, FEARG_1, arg23_extendnew,
|
||||||
|
ret_first_cont, f_extendnew},
|
||||||
{"feedkeys", 1, 2, FEARG_1, NULL,
|
{"feedkeys", 1, 2, FEARG_1, NULL,
|
||||||
ret_void, f_feedkeys},
|
ret_void, f_feedkeys},
|
||||||
{"file_readable", 1, 1, FEARG_1, NULL, // obsolete
|
{"file_readable", 1, 1, FEARG_1, NULL, // obsolete
|
||||||
|
|||||||
73
src/list.c
73
src/list.c
@ -2454,14 +2454,11 @@ f_count(typval_T *argvars, typval_T *rettv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "extend(list, list [, idx])" function
|
* "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew().
|
||||||
* "extend(dict, dict [, action])" function
|
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
f_extend(typval_T *argvars, typval_T *rettv)
|
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)
|
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST)
|
||||||
{
|
{
|
||||||
list_T *l1, *l2;
|
list_T *l1, *l2;
|
||||||
@ -2476,8 +2473,16 @@ f_extend(typval_T *argvars, typval_T *rettv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
l2 = argvars[1].vval.v_list;
|
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)
|
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||||
{
|
{
|
||||||
before = (long)tv_get_number_chk(&argvars[2], &error);
|
before = (long)tv_get_number_chk(&argvars[2], &error);
|
||||||
@ -2500,7 +2505,14 @@ f_extend(typval_T *argvars, typval_T *rettv)
|
|||||||
item = NULL;
|
item = NULL;
|
||||||
list_extend(l1, l2, item);
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
d2 = argvars[1].vval.v_dict;
|
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.
|
// Check the third argument.
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||||
{
|
{
|
||||||
@ -2540,11 +2560,42 @@ f_extend(typval_T *argvars, typval_T *rettv)
|
|||||||
|
|
||||||
dict_extend(d1, d2, action);
|
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
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -52,6 +52,7 @@ void f_mapnew(typval_T *argvars, typval_T *rettv);
|
|||||||
void f_add(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_count(typval_T *argvars, typval_T *rettv);
|
||||||
void f_extend(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_insert(typval_T *argvars, typval_T *rettv);
|
||||||
void f_remove(typval_T *argvars, typval_T *rettv);
|
void f_remove(typval_T *argvars, typval_T *rettv);
|
||||||
void f_reverse(typval_T *argvars, typval_T *rettv);
|
void f_reverse(typval_T *argvars, typval_T *rettv);
|
||||||
|
|||||||
@ -864,6 +864,18 @@ func Test_listdict_extend()
|
|||||||
call assert_fails("call extend(g:, {'-!' : 10})", 'E461:')
|
call assert_fails("call extend(g:, {'-!' : 10})", 'E461:')
|
||||||
endfunc
|
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:check_scope_dict(x, fixed)
|
||||||
func s:gen_cmd(cmd, x)
|
func s:gen_cmd(cmd, x)
|
||||||
return substitute(a:cmd, '\<x\ze:', a:x, 'g')
|
return substitute(a:cmd, '\<x\ze:', a:x, 'g')
|
||||||
|
|||||||
@ -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')
|
CheckDefFailure(['extend({a: 1}, {b: 2}, 1)'], 'E1013: Argument 3: type mismatch, expected string but got number')
|
||||||
enddef
|
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()
|
def Test_extend_return_type()
|
||||||
var l = extend([1, 2], [3])
|
var l = extend([1, 2], [3])
|
||||||
var res = 0
|
var res = 0
|
||||||
|
|||||||
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2336,
|
||||||
/**/
|
/**/
|
||||||
2335,
|
2335,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user