Skip to content

Commit e7525c5

Browse files
committed
patch 8.2.2318: Vim9: string and list index work differently
Problem: Vim9: string and list index work differently. Solution: Make string index work like list index. (closes #7643)
1 parent 9e0f883 commit e7525c5

7 files changed

Lines changed: 121 additions & 101 deletions

File tree

src/eval.c

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -5548,97 +5548,6 @@ eval_isdictc(int c)
55485548
return ASCII_ISALNUM(c) || c == '_';
55495549
}
55505550

5551-
/*
5552-
* Return the character "str[index]" where "index" is the character index. If
5553-
* "index" is out of range NULL is returned.
5554-
*/
5555-
char_u *
5556-
char_from_string(char_u *str, varnumber_T index)
5557-
{
5558-
size_t nbyte = 0;
5559-
varnumber_T nchar = index;
5560-
size_t slen;
5561-
5562-
if (str == NULL || index < 0)
5563-
return NULL;
5564-
slen = STRLEN(str);
5565-
while (nchar > 0 && nbyte < slen)
5566-
{
5567-
nbyte += MB_CPTR2LEN(str + nbyte);
5568-
--nchar;
5569-
}
5570-
if (nbyte >= slen)
5571-
return NULL;
5572-
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
5573-
}
5574-
5575-
/*
5576-
* Get the byte index for character index "idx" in string "str" with length
5577-
* "str_len".
5578-
* If going over the end return "str_len".
5579-
* If "idx" is negative count from the end, -1 is the last character.
5580-
* When going over the start return -1.
5581-
*/
5582-
static long
5583-
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
5584-
{
5585-
varnumber_T nchar = idx;
5586-
size_t nbyte = 0;
5587-
5588-
if (nchar >= 0)
5589-
{
5590-
while (nchar > 0 && nbyte < str_len)
5591-
{
5592-
nbyte += MB_CPTR2LEN(str + nbyte);
5593-
--nchar;
5594-
}
5595-
}
5596-
else
5597-
{
5598-
nbyte = str_len;
5599-
while (nchar < 0 && nbyte > 0)
5600-
{
5601-
--nbyte;
5602-
nbyte -= mb_head_off(str, str + nbyte);
5603-
++nchar;
5604-
}
5605-
if (nchar < 0)
5606-
return -1;
5607-
}
5608-
return (long)nbyte;
5609-
}
5610-
5611-
/*
5612-
* Return the slice "str[first:last]" using character indexes.
5613-
* Return NULL when the result is empty.
5614-
*/
5615-
char_u *
5616-
string_slice(char_u *str, varnumber_T first, varnumber_T last)
5617-
{
5618-
long start_byte, end_byte;
5619-
size_t slen;
5620-
5621-
if (str == NULL)
5622-
return NULL;
5623-
slen = STRLEN(str);
5624-
start_byte = char_idx2byte(str, slen, first);
5625-
if (start_byte < 0)
5626-
start_byte = 0; // first index very negative: use zero
5627-
if (last == -1)
5628-
end_byte = (long)slen;
5629-
else
5630-
{
5631-
end_byte = char_idx2byte(str, slen, last);
5632-
if (end_byte >= 0 && end_byte < (long)slen)
5633-
// end index is inclusive
5634-
end_byte += MB_CPTR2LEN(str + end_byte);
5635-
}
5636-
5637-
if (start_byte >= (long)slen || end_byte <= start_byte)
5638-
return NULL;
5639-
return vim_strnsave(str + start_byte, end_byte - start_byte);
5640-
}
5641-
56425551
/*
56435552
* Handle:
56445553
* - expr[expr], expr[expr:expr] subscript

src/list.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ list_slice_or_index(
924924
if (!range)
925925
{
926926
if (verbose)
927-
semsg(_(e_listidx), n1);
927+
semsg(_(e_listidx), n1_arg);
928928
return FAIL;
929929
}
930930
n1 = n1 < 0 ? 0 : len;

src/proto/eval.pro

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int f
6464
int eval_isnamec(int c);
6565
int eval_isnamec1(int c);
6666
int eval_isdictc(int c);
67-
char_u *char_from_string(char_u *str, varnumber_T index);
68-
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
6967
int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
7068
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
7169
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);

src/proto/vim9execute.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* vim9execute.c */
22
void to_string_error(vartype_T vartype);
33
void funcstack_check_refcount(funcstack_T *funcstack);
4+
char_u *char_from_string(char_u *str, varnumber_T index);
5+
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
46
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
57
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
68
void ex_disassemble(exarg_T *eap);

src/testdir/test_vim9_expr.vim

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,7 +2304,7 @@ def Test_expr7_any_index_slice()
23042304
# string is permissive, index out of range accepted
23052305
g:teststring = 'abcdef'
23062306
assert_equal('b', g:teststring[1])
2307-
assert_equal('', g:teststring[-1])
2307+
assert_equal('f', g:teststring[-1])
23082308
assert_equal('', g:teststring[99])
23092309

23102310
assert_equal('b', g:teststring[1 : 1])
@@ -2368,10 +2368,10 @@ def Test_expr7_any_index_slice()
23682368
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
23692369
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
23702370

2371-
CheckDefExecFailure(['echo g:testlist[4]'], 'E684:', 1)
2371+
CheckDefExecFailure(['echo g:testlist[4]'], 'E684: list index out of range: 4', 1)
23722372
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
23732373
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
2374-
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:', 2)
2374+
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684: list index out of range: -5', 2)
23752375

23762376
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
23772377
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
@@ -2802,15 +2802,23 @@ enddef
28022802
def Test_expr7_string_subscript()
28032803
var lines =<< trim END
28042804
var text = 'abcdef'
2805-
assert_equal('', text[-1])
2805+
assert_equal('f', text[-1])
28062806
assert_equal('a', text[0])
28072807
assert_equal('e', text[4])
28082808
assert_equal('f', text[5])
28092809
assert_equal('', text[6])
28102810

2811+
text = 'ábçdë'
2812+
assert_equal('ë', text[-1])
2813+
assert_equal('d', text[-2])
2814+
assert_equal('ç', text[-3])
2815+
assert_equal('b', text[-4])
2816+
assert_equal('á', text[-5])
2817+
assert_equal('', text[-6])
2818+
28112819
text = 'ábçdëf'
28122820
assert_equal('', text[-999])
2813-
assert_equal('', text[-1])
2821+
assert_equal('f', text[-1])
28142822
assert_equal('á', text[0])
28152823
assert_equal('b', text[1])
28162824
assert_equal('ç', text[2])
@@ -2904,8 +2912,7 @@ def Test_expr7_list_subscript()
29042912
assert_equal([], list[0 : -6])
29052913
assert_equal([], list[0 : -99])
29062914
END
2907-
CheckDefSuccess(lines)
2908-
CheckScriptSuccess(['vim9script'] + lines)
2915+
CheckDefAndScriptSuccess(lines)
29092916

29102917
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
29112918
CheckDefExecFailure(lines, 'E1012:')

src/version.c

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

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2318,
753755
/**/
754756
2317,
755757
/**/

src/vim9execute.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,108 @@ allocate_if_null(typval_T *tv)
863863
}
864864
}
865865

866+
/*
867+
* Return the character "str[index]" where "index" is the character index. If
868+
* "index" is out of range NULL is returned.
869+
*/
870+
char_u *
871+
char_from_string(char_u *str, varnumber_T index)
872+
{
873+
size_t nbyte = 0;
874+
varnumber_T nchar = index;
875+
size_t slen;
876+
877+
if (str == NULL)
878+
return NULL;
879+
slen = STRLEN(str);
880+
881+
// do the same as for a list: a negative index counts from the end
882+
if (index < 0)
883+
{
884+
int clen = 0;
885+
886+
for (nbyte = 0; nbyte < slen; ++clen)
887+
nbyte += MB_CPTR2LEN(str + nbyte);
888+
nchar = clen + index;
889+
if (nchar < 0)
890+
// unlike list: index out of range results in empty string
891+
return NULL;
892+
}
893+
894+
for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
895+
nbyte += MB_CPTR2LEN(str + nbyte);
896+
if (nbyte >= slen)
897+
return NULL;
898+
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
899+
}
900+
901+
/*
902+
* Get the byte index for character index "idx" in string "str" with length
903+
* "str_len".
904+
* If going over the end return "str_len".
905+
* If "idx" is negative count from the end, -1 is the last character.
906+
* When going over the start return -1.
907+
*/
908+
static long
909+
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
910+
{
911+
varnumber_T nchar = idx;
912+
size_t nbyte = 0;
913+
914+
if (nchar >= 0)
915+
{
916+
while (nchar > 0 && nbyte < str_len)
917+
{
918+
nbyte += MB_CPTR2LEN(str + nbyte);
919+
--nchar;
920+
}
921+
}
922+
else
923+
{
924+
nbyte = str_len;
925+
while (nchar < 0 && nbyte > 0)
926+
{
927+
--nbyte;
928+
nbyte -= mb_head_off(str, str + nbyte);
929+
++nchar;
930+
}
931+
if (nchar < 0)
932+
return -1;
933+
}
934+
return (long)nbyte;
935+
}
936+
937+
/*
938+
* Return the slice "str[first:last]" using character indexes.
939+
* Return NULL when the result is empty.
940+
*/
941+
char_u *
942+
string_slice(char_u *str, varnumber_T first, varnumber_T last)
943+
{
944+
long start_byte, end_byte;
945+
size_t slen;
946+
947+
if (str == NULL)
948+
return NULL;
949+
slen = STRLEN(str);
950+
start_byte = char_idx2byte(str, slen, first);
951+
if (start_byte < 0)
952+
start_byte = 0; // first index very negative: use zero
953+
if (last == -1)
954+
end_byte = (long)slen;
955+
else
956+
{
957+
end_byte = char_idx2byte(str, slen, last);
958+
if (end_byte >= 0 && end_byte < (long)slen)
959+
// end index is inclusive
960+
end_byte += MB_CPTR2LEN(str + end_byte);
961+
}
962+
963+
if (start_byte >= (long)slen || end_byte <= start_byte)
964+
return NULL;
965+
return vim_strnsave(str + start_byte, end_byte - start_byte);
966+
}
967+
866968
static svar_T *
867969
get_script_svar(scriptref_T *sref, ectx_T *ectx)
868970
{

0 commit comments

Comments
 (0)