vim-patch:8.2.2533: Vim9: cannot use a range with :unlet

Problem:    Vim9: cannot use a range with :unlet.
Solution:   Implement ISN_UNLETRANGE.

5b5ae29bd3

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq
2023-06-12 15:38:53 +08:00
parent 551cc3a2a3
commit a0cb53eca7
3 changed files with 44 additions and 25 deletions

View File

@@ -1604,13 +1604,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_dict = NULL; lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_list = lp->ll_tv->vval.v_list;
lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); lp->ll_li = tv_list_find_index(lp->ll_list, &lp->ll_n1);
if (lp->ll_li == NULL) {
if (lp->ll_n1 < 0) {
lp->ll_n1 = 0;
lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1);
}
}
if (lp->ll_li == NULL) { if (lp->ll_li == NULL) {
tv_clear(&var2); tv_clear(&var2);
if (!quiet) { if (!quiet) {

View File

@@ -1507,6 +1507,21 @@ const char *tv_list_find_str(list_T *const l, const int n)
return tv_get_string(TV_LIST_ITEM_TV(li)); return tv_get_string(TV_LIST_ITEM_TV(li));
} }
/// Like tv_list_find() but when a negative index is used that is not found use
/// zero and set "idx" to zero. Used for first index of a range.
listitem_T *tv_list_find_index(list_T *const l, long *const idx)
FUNC_ATTR_WARN_UNUSED_RESULT
{
listitem_T *li = tv_list_find(l, (int)(*idx));
if (li == NULL) {
if (*idx < 0) {
*idx = 0;
li = tv_list_find(l, (int)(*idx));
}
}
return li;
}
/// Locate item in a list and return its index /// Locate item in a list and return its index
/// ///
/// @param[in] l List to search. /// @param[in] l List to search.

View File

@@ -1056,25 +1056,10 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
lp->ll_name_len))) { lp->ll_name_len))) {
return FAIL; return FAIL;
} else if (lp->ll_range) { } else if (lp->ll_range) {
assert(lp->ll_list != NULL); if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_name_len,
// Delete a range of List items. lp->ll_n1, !lp->ll_empty2, lp->ll_n2) == FAIL) {
listitem_T *const first_li = lp->ll_li; return FAIL;
listitem_T *last_li = first_li;
while (true) {
listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
lp->ll_name,
lp->ll_name_len)) {
return false;
}
lp->ll_li = li;
lp->ll_n1++;
if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
break;
}
last_li = lp->ll_li;
} }
tv_list_remove_items(lp->ll_list, first_li, last_li);
} else { } else {
if (lp->ll_list != NULL) { if (lp->ll_list != NULL) {
// unlet a List item. // unlet a List item.
@@ -1107,6 +1092,31 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
return ret; return ret;
} }
/// Unlet one item or a range of items from a list.
/// Return OK or FAIL.
static int list_unlet_range(list_T *const l, listitem_T *const li_first, const char *const name,
const size_t name_len, const long n1_arg, const bool has_n2,
const long n2)
{
assert(l != NULL);
// Delete a range of List items.
listitem_T *li_last = li_first;
long n1 = n1_arg;
while (true) {
if (value_check_lock(TV_LIST_ITEM_TV(li_last)->v_lock, name, name_len)) {
return FAIL;
}
listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last);
n1++;
if (li == NULL || (has_n2 && n2 < n1)) {
break;
}
li_last = li;
}
tv_list_remove_items(l, li_first, li_last);
return OK;
}
/// unlet a variable /// unlet a variable
/// ///
/// @param[in] name Variable name to unlet. /// @param[in] name Variable name to unlet.