diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-06-12 20:35:01 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2023-06-12 21:05:22 +0800 |
commit | aa92a04beeed1fb10a82edd6a245e5783cd771c1 (patch) | |
tree | cf114cfd7c4c0c9953509191ecce81b322752642 | |
parent | 0eb02ea90a5a7c2e35bfcf99b701a28ff2901b4b (diff) | |
download | rneovim-aa92a04beeed1fb10a82edd6a245e5783cd771c1.tar.gz rneovim-aa92a04beeed1fb10a82edd6a245e5783cd771c1.tar.bz2 rneovim-aa92a04beeed1fb10a82edd6a245e5783cd771c1.zip |
vim-patch:8.2.3332: Vim9: cannot assign to range in list
Problem: Vim9: cannot assign to range in list.
Solution: Implement overwriting a list range.
https://github.com/vim/vim/commit/4f0884d6e24d1d45ec83fd86b372b403177d3298
Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r-- | src/nvim/eval.c | 79 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 113 | ||||
-rw-r--r-- | test/old/testdir/test_listdict.vim | 13 |
3 files changed, 127 insertions, 78 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b2729b0e67..771824af71 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1609,12 +1609,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_find_index(lp->ll_list, &lp->ll_n1); + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); if (lp->ll_li == NULL) { tv_clear(&var2); - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n1); - } return NULL; } @@ -1625,25 +1622,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. tv_clear(&var2); - if (lp->ll_n2 < 0) { - listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); - if (ni == NULL) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } - return NULL; - } - lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); - } - - // Check that lp->ll_n2 isn't before lp->ll_n1. - if (lp->ll_n1 < 0) { - lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); - } - if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { return NULL; } } @@ -1672,7 +1653,6 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool const char *op) { int cc; - listitem_T *ri; dictitem_T *di; if (lp->ll_tv == NULL) { @@ -1733,60 +1713,13 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_name, TV_CSTRING)) { // Skip } else if (lp->ll_range) { - listitem_T *ll_li = lp->ll_li; - int ll_n1 = (int)lp->ll_n1; - if (is_const) { emsg(_("E996: Cannot lock a range")); return; } - // Check whether any of the list items is locked - for (ri = tv_list_first(rettv->vval.v_list); - ri != NULL && ll_li != NULL;) { - if (value_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, - TV_CSTRING)) { - return; - } - ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { - break; - } - ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); - ll_n1++; - } - - // Assign the List values to the list items. - for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { - if (op != NULL && *op != '=') { - eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); - } else { - tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); - tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); - } - ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { - break; - } - assert(lp->ll_li != NULL); - if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { - // Need to add an empty item. - tv_list_append_number(lp->ll_list, 0); - // ll_li may have become invalid after append, don’t use it. - lp->ll_li = tv_list_last(lp->ll_list); // Valid again. - } else { - lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - } - lp->ll_n1++; - } - if (ri != NULL) { - emsg(_("E710: List value has more items than target")); - } else if (lp->ll_empty2 - ? (lp->ll_li != NULL - && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) - : lp->ll_n1 != lp->ll_n2) { - emsg(_("E711: List value has not enough items")); - } + (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list, + lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name); } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ec227a6b1a..a891ba1570 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -594,6 +594,119 @@ tv_list_copy_error: return NULL; } +/// Get the list item in "l" with index "n1". "n1" is adjusted if needed. +/// Return NULL if there is no such item. +listitem_T *tv_list_check_range_index_one(list_T *const l, long *const n1, const bool quiet) +{ + listitem_T *li = tv_list_find_index(l, n1); + if (li == NULL) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)n1); + } + return NULL; + } + return li; +} + +/// Check that "n2" can be used as the second index in a range of list "l". +/// If "n1" or "n2" is negative it is changed to the positive index. +/// "li1" is the item for item "n1". +/// Return OK or FAIL. +int tv_list_check_range_index_two(list_T *const l, long *const n1, const listitem_T *const li1, + long *const n2, const bool quiet) +{ + if (*n2 < 0) { + listitem_T *ni = tv_list_find(l, (int)(*n2)); + if (ni == NULL) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2)); + } + return FAIL; + } + *n2 = tv_list_idx_of_item(l, ni); + } + + // Check that n2 isn't before n1. + if (*n1 < 0) { + *n1 = tv_list_idx_of_item(l, li1); + } + if (*n2 < *n1) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2)); + } + return FAIL; + } + return OK; +} + +/// Assign values from list "src" into a range of "dest". +/// "idx1_arg" is the index of the first item in "dest" to be replaced. +/// "idx2" is the index of last item to be replaced, but when "empty_idx2" is +/// true then replace all items after "idx1". +/// "op" is the operator, normally "=" but can be "+=" and the like. +/// "varname" is used for error messages. +/// Returns OK or FAIL. +int tv_list_assign_range(list_T *const dest, list_T *const src, const long idx1_arg, + const long idx2, const bool empty_idx2, const char *const op, + const char *const varname) +{ + long idx1 = idx1_arg; + listitem_T *const first_li = tv_list_find_index(dest, &idx1); + listitem_T *src_li; + + // Check whether any of the list items is locked before making any changes. + long idx = idx1; + listitem_T *dest_li = first_li; + for (src_li = tv_list_first(src); src_li != NULL && dest_li != NULL;) { + if (value_check_lock(TV_LIST_ITEM_TV(dest_li)->v_lock, varname, TV_CSTRING)) { + return FAIL; + } + src_li = TV_LIST_ITEM_NEXT(src, src_li); + if (src_li == NULL || (!empty_idx2 && idx2 == idx)) { + break; + } + dest_li = TV_LIST_ITEM_NEXT(dest, dest_li); + idx++; + } + + // Assign the List values to the list items. + idx = idx1; + dest_li = first_li; + for (src_li = tv_list_first(src); src_li != NULL;) { + if (op != NULL && *op != '=') { + eexe_mod_op(TV_LIST_ITEM_TV(dest_li), TV_LIST_ITEM_TV(src_li), op); + } else { + tv_clear(TV_LIST_ITEM_TV(dest_li)); + tv_copy(TV_LIST_ITEM_TV(src_li), TV_LIST_ITEM_TV(dest_li)); + } + src_li = TV_LIST_ITEM_NEXT(src, src_li); + if (src_li == NULL || (!empty_idx2 && idx2 == idx)) { + break; + } + assert(dest_li != NULL); + if (TV_LIST_ITEM_NEXT(dest, dest_li) == NULL) { + // Need to add an empty item. + tv_list_append_number(dest, 0); + // "dest_li" may have become invalid after append, don’t use it. + dest_li = tv_list_last(dest); // Valid again. + } else { + dest_li = TV_LIST_ITEM_NEXT(dest, dest_li); + } + idx++; + } + if (src_li != NULL) { + emsg(_("E710: List value has more items than target")); + return FAIL; + } + if (empty_idx2 + ? (dest_li != NULL && TV_LIST_ITEM_NEXT(dest, dest_li) != NULL) + : idx != idx2) { + emsg(_("E711: List value has not enough items")); + return FAIL; + } + return OK; +} + /// Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth". /// When "first" is NULL use the first item. /// Does nothing if "maxdepth" is 0. diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 07a365d337..d31d17f81a 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -178,11 +178,14 @@ endfunc " test for range assign func Test_list_range_assign() - let l = [0] - let l[:] = [1, 2] - call assert_equal([1, 2], l) - let l[-4:-1] = [5, 6] - call assert_equal([5, 6], l) + let lines =<< trim END + VAR l = [0] + LET l[:] = [1, 2] + call assert_equal([1, 2], l) + LET l[-4 : -1] = [5, 6] + call assert_equal([5, 6], l) + END + call CheckLegacyAndVim9Success(lines) endfunc " Test removing items in list |