# HG changeset patch # User Bram Moolenaar # Date 1612206904 -3600 # Node ID 525c9e218c6934261ae1258097a4e9e4879c74b9 # Parent 40a619efcc9015598a755505a7cbb2d31429d45b patch 8.2.2449: Vim9: flatten() always changes the list type Commit: https://github.com/vim/vim/commit/3b690069730805a147d45d92eaca4dc838272d1d Author: Bram Moolenaar Date: Mon Feb 1 20:14:51 2021 +0100 patch 8.2.2449: Vim9: flatten() always changes the list type Problem: Vim9: flatten() always changes the list type. Solution: Disallow using flatten() and add flattennew(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2549,6 +2549,8 @@ finddir({name} [, {path} [, {count}]]) findfile({name} [, {path} [, {count}]]) String find file {name} in {path} flatten({list} [, {maxdepth}]) List flatten {list} up to {maxdepth} levels +flattennew({list} [, {maxdepth}]) + List flatten a copy of {list} float2nr({expr}) Number convert Float {expr} to a Number floor({expr}) Float round {expr} down fmod({expr1}, {expr2}) Float remainder of {expr1} / {expr2} @@ -4712,8 +4714,10 @@ flatten({list} [, {maxdepth}]) *flat Flatten {list} up to {maxdepth} levels. Without {maxdepth} the result is a |List| without nesting, as if {maxdepth} is a very large number. - The {list} is changed in place, make a copy first if you do + The {list} is changed in place, use |flattennew()| if you do not want that. + In Vim9 script flatten() cannot be used, you must always use + |flattennew()|. *E900* {maxdepth} means how deep in nested lists changes are made. {list} is not modified when {maxdepth} is 0. @@ -4727,6 +4731,10 @@ flatten({list} [, {maxdepth}]) *flat :echo flatten([1, [2, [3, 4]], 5], 1) < [1, 2, [3, 4], 5] +flattennew({list} [, {maxdepth}]) *flattennew()* + Like |flatten()| but first make a copy of {list}. + + float2nr({expr}) *float2nr()* Convert {expr} to a Number by omitting the part after the decimal point. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -665,6 +665,7 @@ List manipulation: *list-functions* count() count number of times a value appears in a List repeat() repeat a List multiple times flatten() flatten a List + flattennew() flatten a copy of a List Dictionary manipulation: *dict-functions* get() get an entry without an error for a wrong key diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -351,3 +351,5 @@ EXTERN char e_cannot_change_arglist_recu INIT(= N_("E1156: Cannot change the argument list recursively")); EXTERN char e_missing_return_type[] INIT(= N_("E1157: Missing return type")); +EXTERN char e_cannot_use_flatten_in_vim9_script[] + INIT(= N_("E1158: Cannot use flatten() in Vim9 script")); diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -954,6 +954,8 @@ static funcentry_T global_functions[] = ret_string, f_findfile}, {"flatten", 1, 2, FEARG_1, NULL, ret_list_any, f_flatten}, + {"flattennew", 1, 2, FEARG_1, NULL, + ret_list_any, f_flattennew}, {"float2nr", 1, 1, FEARG_1, NULL, ret_number, FLOAT_FUNC(f_float2nr)}, {"floor", 1, 1, FEARG_1, NULL, diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -740,7 +740,7 @@ list_insert(list_T *l, listitem_T *ni, l * It does nothing if "maxdepth" is 0. * Returns FAIL when out of memory. */ - static int + static void list_flatten(list_T *list, long maxdepth) { listitem_T *item; @@ -748,7 +748,7 @@ list_flatten(list_T *list, long maxdepth int n; if (maxdepth == 0) - return OK; + return; CHECK_LIST_MATERIALIZE(list); n = 0; @@ -757,7 +757,7 @@ list_flatten(list_T *list, long maxdepth { fast_breakcheck(); if (got_int) - return FAIL; + return; if (item->li_tv.v_type == VAR_LIST) { @@ -765,7 +765,7 @@ list_flatten(list_T *list, long maxdepth vimlist_remove(list, item, item); if (list_extend(list, item->li_tv.vval.v_list, next) == FAIL) - return FAIL; + return; clear_tv(&item->li_tv); tofree = item; @@ -787,15 +787,13 @@ list_flatten(list_T *list, long maxdepth item = item->li_next; } } - - return OK; } /* - * "flatten(list[, {maxdepth}])" function + * "flatten()" and "flattennew()" functions */ - void -f_flatten(typval_T *argvars, typval_T *rettv) + static void +flatten_common(typval_T *argvars, typval_T *rettv, int make_copy) { list_T *l; long maxdepth; @@ -822,10 +820,48 @@ f_flatten(typval_T *argvars, typval_T *r } l = argvars[0].vval.v_list; - if (l != NULL && !value_check_lock(l->lv_lock, - (char_u *)N_("flatten() argument"), TRUE) - && list_flatten(l, maxdepth) == OK) - copy_tv(&argvars[0], rettv); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + if (l == NULL) + return; + + if (make_copy) + { + l = list_copy(l, TRUE, get_copyID()); + rettv->vval.v_list = l; + if (l == NULL) + return; + } + else + { + if (value_check_lock(l->lv_lock, + (char_u *)N_("flatten() argument"), TRUE)) + return; + ++l->lv_refcount; + } + + list_flatten(l, maxdepth); +} + +/* + * "flatten(list[, {maxdepth}])" function + */ + void +f_flatten(typval_T *argvars, typval_T *rettv) +{ + if (in_vim9script()) + emsg(_(e_cannot_use_flatten_in_vim9_script)); + else + flatten_common(argvars, rettv, FALSE); +} + +/* + * "flattennew(list[, {maxdepth}])" function + */ + void +f_flattennew(typval_T *argvars, typval_T *rettv) +{ + flatten_common(argvars, rettv, TRUE); } /* diff --git a/src/proto/list.pro b/src/proto/list.pro --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -31,6 +31,7 @@ int list_append_number(list_T *l, varnum int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item); void list_insert(list_T *l, listitem_T *ni, listitem_T *item); void f_flatten(typval_T *argvars, typval_T *rettv); +void f_flattennew(typval_T *argvars, typval_T *rettv); int list_extend(list_T *l1, list_T *l2, listitem_T *bef); int list_concat(list_T *l1, list_T *l2, typval_T *tv); list_T *list_slice(list_T *ol, long n1, long n2); diff --git a/src/testdir/test_flatten.vim b/src/testdir/test_flatten.vim --- a/src/testdir/test_flatten.vim +++ b/src/testdir/test_flatten.vim @@ -81,4 +81,13 @@ func Test_flatten() call assert_equal([2, l:x], l:y) endfunc +func Test_flattennew() + let l = [1, [2, [3, 4]], 5] + call assert_equal([1, 2, 3, 4, 5], flattennew(l)) + call assert_equal([1, [2, [3, 4]], 5], l) + + call assert_equal([1, 2, [3, 4], 5], flattennew(l, 1)) + call assert_equal([1, [2, [3, 4]], 5], l) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -382,6 +382,23 @@ def Test_findfile() CheckDefExecFailure(['echo findfile("")'], 'E1142:') enddef +def Test_flattennew() + var lines =<< trim END + var l = [1, [2, [3, 4]], 5] + call assert_equal([1, 2, 3, 4, 5], flattennew(l)) + call assert_equal([1, [2, [3, 4]], 5], l) + + call assert_equal([1, 2, [3, 4], 5], flattennew(l, 1)) + call assert_equal([1, [2, [3, 4]], 5], l) + END + CheckDefAndScriptSuccess(lines) + + lines =<< trim END + echo flatten([1, 2, 3]) + END + CheckDefAndScriptFailure(lines, 'E1158:') +enddef + def Test_fnamemodify() CheckDefSuccess(['echo fnamemodify(test_null_string(), ":p")']) CheckDefSuccess(['echo fnamemodify("", ":p")']) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2449, +/**/ 2448, /**/ 2447, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2900,6 +2900,12 @@ compile_call( idx = find_internal_func(name); if (idx >= 0) { + if (STRCMP(name, "flatten") == 0) + { + emsg(_(e_cannot_use_flatten_in_vim9_script)); + goto theend; + } + if (STRCMP(name, "add") == 0 && argcount == 2) { garray_T *stack = &cctx->ctx_type_stack;