Skip to content

Commit 4f0884d

Browse files
committed
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.
1 parent ed7cb2d commit 4f0884d

File tree

9 files changed

+261
-106
lines changed

9 files changed

+261
-106
lines changed

src/eval.c

Lines changed: 9 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ typedef struct
4545
int fi_byte_idx; // byte index in fi_string
4646
} forinfo_T;
4747

48-
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
4948
static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
5049
static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
5150
static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
@@ -827,7 +826,6 @@ get_lval(
827826
typval_T var1;
828827
typval_T var2;
829828
int empty1 = FALSE;
830-
listitem_T *ni;
831829
char_u *key = NULL;
832830
int len;
833831
hashtab_T *ht = NULL;
@@ -1210,23 +1208,11 @@ get_lval(
12101208

12111209
lp->ll_dict = NULL;
12121210
lp->ll_list = lp->ll_tv->vval.v_list;
1213-
lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
1211+
lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1, quiet);
12141212
if (lp->ll_li == NULL)
12151213
{
1216-
// Vim9: Allow for adding an item at the end.
1217-
if (in_vim9script() && lp->ll_n1 == lp->ll_list->lv_len
1218-
&& lp->ll_list->lv_lock == 0)
1219-
{
1220-
list_append_number(lp->ll_list, 0);
1221-
lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
1222-
}
1223-
if (lp->ll_li == NULL)
1224-
{
1225-
clear_tv(&var2);
1226-
if (!quiet)
1227-
semsg(_(e_listidx), lp->ll_n1);
1228-
return NULL;
1229-
}
1214+
clear_tv(&var2);
1215+
return NULL;
12301216
}
12311217

12321218
if (lp->ll_valtype != NULL)
@@ -1244,27 +1230,10 @@ get_lval(
12441230
lp->ll_n2 = (long)tv_get_number(&var2);
12451231
// is number or string
12461232
clear_tv(&var2);
1247-
if (lp->ll_n2 < 0)
1248-
{
1249-
ni = list_find(lp->ll_list, lp->ll_n2);
1250-
if (ni == NULL)
1251-
{
1252-
if (!quiet)
1253-
semsg(_(e_listidx), lp->ll_n2);
1254-
return NULL;
1255-
}
1256-
lp->ll_n2 = list_idx_of_item(lp->ll_list, ni);
1257-
}
1258-
1259-
// Check that lp->ll_n2 isn't before lp->ll_n1.
1260-
if (lp->ll_n1 < 0)
1261-
lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li);
1262-
if (lp->ll_n2 < lp->ll_n1)
1263-
{
1264-
if (!quiet)
1265-
semsg(_(e_listidx), lp->ll_n2);
1233+
if (check_range_index_two(lp->ll_list,
1234+
&lp->ll_n1, lp->ll_li,
1235+
&lp->ll_n2, quiet) == FAIL)
12661236
return NULL;
1267-
}
12681237
}
12691238

12701239
lp->ll_tv = &lp->ll_li->li_tv;
@@ -1303,7 +1272,6 @@ set_var_lval(
13031272
int var_idx) // index for "let [a, b] = list"
13041273
{
13051274
int cc;
1306-
listitem_T *ri;
13071275
dictitem_T *di;
13081276

13091277
if (lp->ll_tv == NULL)
@@ -1383,63 +1351,15 @@ set_var_lval(
13831351
;
13841352
else if (lp->ll_range)
13851353
{
1386-
listitem_T *ll_li = lp->ll_li;
1387-
int ll_n1 = lp->ll_n1;
1388-
13891354
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
13901355
&& (flags & ASSIGN_FOR_LOOP) == 0)
13911356
{
13921357
emsg(_("E996: Cannot lock a range"));
13931358
return;
13941359
}
13951360

1396-
/*
1397-
* Check whether any of the list items is locked
1398-
*/
1399-
for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; )
1400-
{
1401-
if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
1402-
return;
1403-
ri = ri->li_next;
1404-
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1))
1405-
break;
1406-
ll_li = ll_li->li_next;
1407-
++ll_n1;
1408-
}
1409-
1410-
/*
1411-
* Assign the List values to the list items.
1412-
*/
1413-
for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
1414-
{
1415-
if (op != NULL && *op != '=')
1416-
tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
1417-
else
1418-
{
1419-
clear_tv(&lp->ll_li->li_tv);
1420-
copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
1421-
}
1422-
ri = ri->li_next;
1423-
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
1424-
break;
1425-
if (lp->ll_li->li_next == NULL)
1426-
{
1427-
// Need to add an empty item.
1428-
if (list_append_number(lp->ll_list, 0) == FAIL)
1429-
{
1430-
ri = NULL;
1431-
break;
1432-
}
1433-
}
1434-
lp->ll_li = lp->ll_li->li_next;
1435-
++lp->ll_n1;
1436-
}
1437-
if (ri != NULL)
1438-
emsg(_(e_list_value_has_more_items_than_targets));
1439-
else if (lp->ll_empty2
1440-
? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
1441-
: lp->ll_n1 != lp->ll_n2)
1442-
emsg(_(e_list_value_does_not_have_enough_items));
1361+
(void)list_assign_range(lp->ll_list, rettv->vval.v_list,
1362+
lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name);
14431363
}
14441364
else
14451365
{
@@ -1507,7 +1427,7 @@ set_var_lval(
15071427
* and "tv1 .= tv2"
15081428
* Returns OK or FAIL.
15091429
*/
1510-
static int
1430+
int
15111431
tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
15121432
{
15131433
varnumber_T n;

src/list.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,158 @@ list_insert(list_T *l, listitem_T *ni, listitem_T *item)
761761
}
762762
}
763763

764+
/*
765+
* Get the list item in "l" with index "n1". "n1" is adjusted if needed.
766+
* In Vim9, it is at the end of the list, add an item.
767+
* Return NULL if there is no such item.
768+
*/
769+
listitem_T *
770+
check_range_index_one(list_T *l, long *n1, int quiet)
771+
{
772+
listitem_T *li = list_find_index(l, n1);
773+
774+
if (li == NULL)
775+
{
776+
// Vim9: Allow for adding an item at the end.
777+
if (in_vim9script() && *n1 == l->lv_len && l->lv_lock == 0)
778+
{
779+
list_append_number(l, 0);
780+
li = list_find_index(l, n1);
781+
}
782+
if (li == NULL)
783+
{
784+
if (!quiet)
785+
semsg(_(e_listidx), *n1);
786+
return NULL;
787+
}
788+
}
789+
return li;
790+
}
791+
792+
/*
793+
* Check that "n2" can be used as the second index in a range of list "l".
794+
* If "n1" or "n2" is negative it is changed to the positive index.
795+
* "li1" is the item for item "n1".
796+
* Return OK or FAIL.
797+
*/
798+
int
799+
check_range_index_two(
800+
list_T *l,
801+
long *n1,
802+
listitem_T *li1,
803+
long *n2,
804+
int quiet)
805+
{
806+
if (*n2 < 0)
807+
{
808+
listitem_T *ni = list_find(l, *n2);
809+
810+
if (ni == NULL)
811+
{
812+
if (!quiet)
813+
semsg(_(e_listidx), *n2);
814+
return FAIL;
815+
}
816+
*n2 = list_idx_of_item(l, ni);
817+
}
818+
819+
// Check that n2 isn't before n1.
820+
if (*n1 < 0)
821+
*n1 = list_idx_of_item(l, li1);
822+
if (*n2 < *n1)
823+
{
824+
if (!quiet)
825+
semsg(_(e_listidx), *n2);
826+
return FAIL;
827+
}
828+
return OK;
829+
}
830+
831+
/*
832+
* Assign values from list "src" into a range of "dest".
833+
* "idx1_arg" is the index of the first item in "dest" to be replaced.
834+
* "idx2" is the index of last item to be replaced, but when "empty_idx2" is
835+
* TRUE then replace all items after "idx1".
836+
* "op" is the operator, normally "=" but can be "+=" and the like.
837+
* "varname" is used for error messages.
838+
* Returns OK or FAIL.
839+
*/
840+
int
841+
list_assign_range(
842+
list_T *dest,
843+
list_T *src,
844+
long idx1_arg,
845+
long idx2,
846+
int empty_idx2,
847+
char_u *op,
848+
char_u *varname)
849+
{
850+
listitem_T *src_li;
851+
listitem_T *dest_li;
852+
long idx1 = idx1_arg;
853+
listitem_T *first_li = list_find_index(dest, &idx1);
854+
long idx;
855+
856+
/*
857+
* Check whether any of the list items is locked before making any changes.
858+
*/
859+
idx = idx1;
860+
dest_li = first_li;
861+
for (src_li = src->lv_first; src_li != NULL && dest_li != NULL; )
862+
{
863+
if (value_check_lock(dest_li->li_tv.v_lock, varname, FALSE))
864+
return FAIL;
865+
src_li = src_li->li_next;
866+
if (src_li == NULL || (!empty_idx2 && idx2 == idx))
867+
break;
868+
dest_li = dest_li->li_next;
869+
++idx;
870+
}
871+
872+
/*
873+
* Assign the List values to the list items.
874+
*/
875+
idx = idx1;
876+
dest_li = first_li;
877+
for (src_li = src->lv_first; src_li != NULL; )
878+
{
879+
if (op != NULL && *op != '=')
880+
tv_op(&dest_li->li_tv, &src_li->li_tv, op);
881+
else
882+
{
883+
clear_tv(&dest_li->li_tv);
884+
copy_tv(&src_li->li_tv, &dest_li->li_tv);
885+
}
886+
src_li = src_li->li_next;
887+
if (src_li == NULL || (!empty_idx2 && idx2 == idx))
888+
break;
889+
if (dest_li->li_next == NULL)
890+
{
891+
// Need to add an empty item.
892+
if (list_append_number(dest, 0) == FAIL)
893+
{
894+
src_li = NULL;
895+
break;
896+
}
897+
}
898+
dest_li = dest_li->li_next;
899+
++idx;
900+
}
901+
if (src_li != NULL)
902+
{
903+
emsg(_(e_list_value_has_more_items_than_targets));
904+
return FAIL;
905+
}
906+
if (empty_idx2
907+
? (dest_li != NULL && dest_li->li_next != NULL)
908+
: idx != idx2)
909+
{
910+
emsg(_(e_list_value_does_not_have_enough_items));
911+
return FAIL;
912+
}
913+
return OK;
914+
}
915+
764916
/*
765917
* Flatten "list" to depth "maxdepth".
766918
* It does nothing if "maxdepth" is 0.

src/proto/eval.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ int eval_foldexpr(char_u *arg, int *cp);
2626
char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags);
2727
void clear_lval(lval_T *lp);
2828
void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op, int var_idx);
29+
int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
2930
void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, evalarg_T *evalarg);
3031
void skip_for_lines(void *fi_void, evalarg_T *evalarg);
3132
int next_for_item(void *fi_void, char_u *arg);

src/proto/list.pro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ int list_append_string(list_T *l, char_u *str, int len);
3030
int list_append_number(list_T *l, varnumber_T n);
3131
int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item);
3232
void list_insert(list_T *l, listitem_T *ni, listitem_T *item);
33+
listitem_T *check_range_index_one(list_T *l, long *n1, int quiet);
34+
int check_range_index_two(list_T *l, long *n1, listitem_T *li1, long *n2, int quiet);
35+
int list_assign_range(list_T *dest, list_T *src, long idx1_arg, long idx2, int empty_idx2, char_u *op, char_u *varname);
3336
void f_flatten(typval_T *argvars, typval_T *rettv);
3437
void f_flattennew(typval_T *argvars, typval_T *rettv);
3538
int list_extend(list_T *l1, list_T *l2, listitem_T *bef);

src/testdir/test_listdict.vim

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,14 @@ endfunc
164164

165165
" test for range assign
166166
func Test_list_range_assign()
167-
let l = [0]
168-
let l[:] = [1, 2]
169-
call assert_equal([1, 2], l)
170-
let l[-4:-1] = [5, 6]
171-
call assert_equal([5, 6], l)
167+
let lines =<< trim END
168+
VAR l = [0]
169+
LET l[:] = [1, 2]
170+
call assert_equal([1, 2], l)
171+
LET l[-4 : -1] = [5, 6]
172+
call assert_equal([5, 6], l)
173+
END
174+
call CheckLegacyAndVim9Success(lines)
172175
endfunc
173176

174177
" Test removing items in list

src/testdir/test_vim9_assign.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1821,7 +1821,7 @@ def Test_unlet()
18211821
CheckDefFailure([
18221822
'var ll = [1, 2]',
18231823
'll[1 : 2] = 7',
1824-
], 'E1165:', 2)
1824+
], 'E1012: Type mismatch; expected list<number> but got number', 2)
18251825
CheckDefFailure([
18261826
'var dd = {a: 1}',
18271827
'unlet dd["a" : "a"]',

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ static char *(features[]) =
755755

756756
static int included_patches[] =
757757
{ /* Add new patch number below this line */
758+
/**/
759+
3332,
758760
/**/
759761
3331,
760762
/**/

0 commit comments

Comments
 (0)