aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-02-27 10:10:42 +0800
committerGitHub <noreply@github.com>2023-02-27 10:10:42 +0800
commit2c9fbe34b20266ef5ab54f6ed14fb38eef60430d (patch)
tree904fd34f9cc1e7846192c8f458b21b23ce40996b
parentd52a3f7c715cf4e3f2996dc16b405e1a63b7301d (diff)
downloadrneovim-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.txt11
-rw-r--r--runtime/doc/usr_41.txt2
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/eval/funcs.c70
-rw-r--r--src/nvim/testdir/test_listdict.vim12
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')