diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-02-27 10:10:42 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-27 10:10:42 +0800 |
commit | 2c9fbe34b20266ef5ab54f6ed14fb38eef60430d (patch) | |
tree | 904fd34f9cc1e7846192c8f458b21b23ce40996b | |
parent | d52a3f7c715cf4e3f2996dc16b405e1a63b7301d (diff) | |
download | rneovim-2c9fbe34b20266ef5ab54f6ed14fb38eef60430d.tar.gz rneovim-2c9fbe34b20266ef5ab54f6ed14fb38eef60430d.tar.bz2 rneovim-2c9fbe34b20266ef5ab54f6ed14fb38eef60430d.zip |
vim-patch:8.2.2336: Vim9: not possible to extend dictionary with different type (#22425)
Problem: Vim9: it is not possible to extend a dictionary with different
item types.
Solution: Add extendnew(). (closes vim/vim#7666)
https://github.com/vim/vim/commit/b0e6b513648db7035046613431a4aa9d71ef4653
Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r-- | runtime/doc/builtin.txt | 11 | ||||
-rw-r--r-- | runtime/doc/usr_41.txt | 2 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 70 | ||||
-rw-r--r-- | src/nvim/testdir/test_listdict.vim | 12 |
5 files changed, 82 insertions, 14 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 35f1b7cd6f..3ff4e47a45 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -138,6 +138,9 @@ expandcmd({string} [, {options}]) String expand {string} 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 @@ -2119,6 +2122,14 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()* Can also be used as a |method|: > 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. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 77b1795a57..0f2cfdd2ac 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -650,6 +650,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 @@ -681,6 +682,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 keys() get List of Dictionary keys diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0c6912a702..a476e20339 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -120,6 +120,7 @@ return { expand={args={1, 3}, base=1}, expandcmd={args={1, 2}, base=1}, extend={args={2, 3}, base=1}, + extendnew={args={2, 3}, base=1}, feedkeys={args={1, 2}, base=1}, file_readable={args=1, base=1, func='f_filereadable'}, -- obsolete filereadable={args=1, base=1, fast=true}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 13d8f52768..1baf96e281 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1929,18 +1929,22 @@ static void f_flattennew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) flatten_common(argvars, rettv, true); } -/// "extend(list, list [, idx])" function -/// "extend(dict, dict [, action])" function -static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +/// "extend()" or "extendnew()" function. "is_new" is true for extendnew(). +static void extend(typval_T *argvars, typval_T *rettv, char *arg_errmsg, bool is_new) { - const char *const arg_errmsg = N_("extend() argument"); - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { bool error = false; - list_T *const l1 = argvars[0].vval.v_list; + list_T *l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (!value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + if (is_new) { + l1 = tv_list_copy(NULL, l1, false, get_copyID()); + if (l1 == NULL) { + return; + } + } + listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { long before = (long)tv_get_number_chk(&argvars[2], &error); @@ -1962,11 +1966,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } tv_list_extend(l1, l2, item); - tv_copy(&argvars[0], rettv); + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = l1, + }; + } else { + tv_copy(&argvars[0], rettv); + } } - } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == - VAR_DICT) { - dict_T *const d1 = argvars[0].vval.v_dict; + } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { + dict_T *d1 = argvars[0].vval.v_dict; dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); @@ -1975,7 +1986,14 @@ static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } else if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + if (is_new) { + d1 = tv_dict_copy(NULL, d1, false, get_copyID()); + if (d1 == NULL) { + return; + } + } + const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -1999,13 +2017,37 @@ static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_dict_extend(d1, d2, action); - tv_copy(&argvars[0], rettv); + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = d1, + }; + } else { + tv_copy(&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 +static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char *errmsg = N_("extend() argument"); + extend(argvars, rettv, errmsg, false); +} + +/// "extendnew(list, list [, idx])" function +/// "extendnew(dict, dict [, action])" function +static void f_extendnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char *errmsg = N_("extendnew() argument"); + extend(argvars, rettv, errmsg, true); +} + /// "feedkeys()" function static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 37c1ee8307..fa22bad0cb 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -881,6 +881,18 @@ func Test_listdict_extend() call assert_equal([1, 5, 7, 1, 5, 7], l) 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') |