mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
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)
This commit is contained in:
parent
9e0f883f89
commit
e7525c5520
91
src/eval.c
91
src/eval.c
@ -5548,97 +5548,6 @@ eval_isdictc(int c)
|
|||||||
return ASCII_ISALNUM(c) || c == '_';
|
return ASCII_ISALNUM(c) || c == '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the character "str[index]" where "index" is the character index. If
|
|
||||||
* "index" is out of range NULL is returned.
|
|
||||||
*/
|
|
||||||
char_u *
|
|
||||||
char_from_string(char_u *str, varnumber_T index)
|
|
||||||
{
|
|
||||||
size_t nbyte = 0;
|
|
||||||
varnumber_T nchar = index;
|
|
||||||
size_t slen;
|
|
||||||
|
|
||||||
if (str == NULL || index < 0)
|
|
||||||
return NULL;
|
|
||||||
slen = STRLEN(str);
|
|
||||||
while (nchar > 0 && nbyte < slen)
|
|
||||||
{
|
|
||||||
nbyte += MB_CPTR2LEN(str + nbyte);
|
|
||||||
--nchar;
|
|
||||||
}
|
|
||||||
if (nbyte >= slen)
|
|
||||||
return NULL;
|
|
||||||
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the byte index for character index "idx" in string "str" with length
|
|
||||||
* "str_len".
|
|
||||||
* If going over the end return "str_len".
|
|
||||||
* If "idx" is negative count from the end, -1 is the last character.
|
|
||||||
* When going over the start return -1.
|
|
||||||
*/
|
|
||||||
static long
|
|
||||||
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
|
|
||||||
{
|
|
||||||
varnumber_T nchar = idx;
|
|
||||||
size_t nbyte = 0;
|
|
||||||
|
|
||||||
if (nchar >= 0)
|
|
||||||
{
|
|
||||||
while (nchar > 0 && nbyte < str_len)
|
|
||||||
{
|
|
||||||
nbyte += MB_CPTR2LEN(str + nbyte);
|
|
||||||
--nchar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nbyte = str_len;
|
|
||||||
while (nchar < 0 && nbyte > 0)
|
|
||||||
{
|
|
||||||
--nbyte;
|
|
||||||
nbyte -= mb_head_off(str, str + nbyte);
|
|
||||||
++nchar;
|
|
||||||
}
|
|
||||||
if (nchar < 0)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return (long)nbyte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the slice "str[first:last]" using character indexes.
|
|
||||||
* Return NULL when the result is empty.
|
|
||||||
*/
|
|
||||||
char_u *
|
|
||||||
string_slice(char_u *str, varnumber_T first, varnumber_T last)
|
|
||||||
{
|
|
||||||
long start_byte, end_byte;
|
|
||||||
size_t slen;
|
|
||||||
|
|
||||||
if (str == NULL)
|
|
||||||
return NULL;
|
|
||||||
slen = STRLEN(str);
|
|
||||||
start_byte = char_idx2byte(str, slen, first);
|
|
||||||
if (start_byte < 0)
|
|
||||||
start_byte = 0; // first index very negative: use zero
|
|
||||||
if (last == -1)
|
|
||||||
end_byte = (long)slen;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
end_byte = char_idx2byte(str, slen, last);
|
|
||||||
if (end_byte >= 0 && end_byte < (long)slen)
|
|
||||||
// end index is inclusive
|
|
||||||
end_byte += MB_CPTR2LEN(str + end_byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_byte >= (long)slen || end_byte <= start_byte)
|
|
||||||
return NULL;
|
|
||||||
return vim_strnsave(str + start_byte, end_byte - start_byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle:
|
* Handle:
|
||||||
* - expr[expr], expr[expr:expr] subscript
|
* - expr[expr], expr[expr:expr] subscript
|
||||||
|
@ -924,7 +924,7 @@ list_slice_or_index(
|
|||||||
if (!range)
|
if (!range)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
semsg(_(e_listidx), n1);
|
semsg(_(e_listidx), n1_arg);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
n1 = n1 < 0 ? 0 : len;
|
n1 = n1 < 0 ? 0 : len;
|
||||||
|
@ -64,8 +64,6 @@ char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int f
|
|||||||
int eval_isnamec(int c);
|
int eval_isnamec(int c);
|
||||||
int eval_isnamec1(int c);
|
int eval_isnamec1(int c);
|
||||||
int eval_isdictc(int c);
|
int eval_isdictc(int c);
|
||||||
char_u *char_from_string(char_u *str, varnumber_T index);
|
|
||||||
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
|
|
||||||
int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
|
int item_copy(typval_T *from, typval_T *to, int deep, int copyID);
|
||||||
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
|
void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/* vim9execute.c */
|
/* vim9execute.c */
|
||||||
void to_string_error(vartype_T vartype);
|
void to_string_error(vartype_T vartype);
|
||||||
void funcstack_check_refcount(funcstack_T *funcstack);
|
void funcstack_check_refcount(funcstack_T *funcstack);
|
||||||
|
char_u *char_from_string(char_u *str, varnumber_T index);
|
||||||
|
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
|
||||||
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
|
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
|
||||||
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
|
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
|
||||||
void ex_disassemble(exarg_T *eap);
|
void ex_disassemble(exarg_T *eap);
|
||||||
|
@ -2304,7 +2304,7 @@ def Test_expr7_any_index_slice()
|
|||||||
# string is permissive, index out of range accepted
|
# string is permissive, index out of range accepted
|
||||||
g:teststring = 'abcdef'
|
g:teststring = 'abcdef'
|
||||||
assert_equal('b', g:teststring[1])
|
assert_equal('b', g:teststring[1])
|
||||||
assert_equal('', g:teststring[-1])
|
assert_equal('f', g:teststring[-1])
|
||||||
assert_equal('', g:teststring[99])
|
assert_equal('', g:teststring[99])
|
||||||
|
|
||||||
assert_equal('b', g:teststring[1 : 1])
|
assert_equal('b', g:teststring[1 : 1])
|
||||||
@ -2368,10 +2368,10 @@ def Test_expr7_any_index_slice()
|
|||||||
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
|
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
|
||||||
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
|
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
|
||||||
|
|
||||||
CheckDefExecFailure(['echo g:testlist[4]'], 'E684:', 1)
|
CheckDefExecFailure(['echo g:testlist[4]'], 'E684: list index out of range: 4', 1)
|
||||||
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
|
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
|
||||||
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
|
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
|
||||||
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:', 2)
|
CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684: list index out of range: -5', 2)
|
||||||
|
|
||||||
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
|
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
|
||||||
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
|
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
|
||||||
@ -2802,15 +2802,23 @@ enddef
|
|||||||
def Test_expr7_string_subscript()
|
def Test_expr7_string_subscript()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
var text = 'abcdef'
|
var text = 'abcdef'
|
||||||
assert_equal('', text[-1])
|
assert_equal('f', text[-1])
|
||||||
assert_equal('a', text[0])
|
assert_equal('a', text[0])
|
||||||
assert_equal('e', text[4])
|
assert_equal('e', text[4])
|
||||||
assert_equal('f', text[5])
|
assert_equal('f', text[5])
|
||||||
assert_equal('', text[6])
|
assert_equal('', text[6])
|
||||||
|
|
||||||
|
text = 'ábçdë'
|
||||||
|
assert_equal('ë', text[-1])
|
||||||
|
assert_equal('d', text[-2])
|
||||||
|
assert_equal('ç', text[-3])
|
||||||
|
assert_equal('b', text[-4])
|
||||||
|
assert_equal('á', text[-5])
|
||||||
|
assert_equal('', text[-6])
|
||||||
|
|
||||||
text = 'ábçdëf'
|
text = 'ábçdëf'
|
||||||
assert_equal('', text[-999])
|
assert_equal('', text[-999])
|
||||||
assert_equal('', text[-1])
|
assert_equal('f', text[-1])
|
||||||
assert_equal('á', text[0])
|
assert_equal('á', text[0])
|
||||||
assert_equal('b', text[1])
|
assert_equal('b', text[1])
|
||||||
assert_equal('ç', text[2])
|
assert_equal('ç', text[2])
|
||||||
@ -2904,8 +2912,7 @@ def Test_expr7_list_subscript()
|
|||||||
assert_equal([], list[0 : -6])
|
assert_equal([], list[0 : -6])
|
||||||
assert_equal([], list[0 : -99])
|
assert_equal([], list[0 : -99])
|
||||||
END
|
END
|
||||||
CheckDefSuccess(lines)
|
CheckDefAndScriptSuccess(lines)
|
||||||
CheckScriptSuccess(['vim9script'] + lines)
|
|
||||||
|
|
||||||
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
|
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
|
||||||
CheckDefExecFailure(lines, 'E1012:')
|
CheckDefExecFailure(lines, 'E1012:')
|
||||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2318,
|
||||||
/**/
|
/**/
|
||||||
2317,
|
2317,
|
||||||
/**/
|
/**/
|
||||||
|
@ -863,6 +863,108 @@ allocate_if_null(typval_T *tv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the character "str[index]" where "index" is the character index. If
|
||||||
|
* "index" is out of range NULL is returned.
|
||||||
|
*/
|
||||||
|
char_u *
|
||||||
|
char_from_string(char_u *str, varnumber_T index)
|
||||||
|
{
|
||||||
|
size_t nbyte = 0;
|
||||||
|
varnumber_T nchar = index;
|
||||||
|
size_t slen;
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
return NULL;
|
||||||
|
slen = STRLEN(str);
|
||||||
|
|
||||||
|
// do the same as for a list: a negative index counts from the end
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
int clen = 0;
|
||||||
|
|
||||||
|
for (nbyte = 0; nbyte < slen; ++clen)
|
||||||
|
nbyte += MB_CPTR2LEN(str + nbyte);
|
||||||
|
nchar = clen + index;
|
||||||
|
if (nchar < 0)
|
||||||
|
// unlike list: index out of range results in empty string
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
|
||||||
|
nbyte += MB_CPTR2LEN(str + nbyte);
|
||||||
|
if (nbyte >= slen)
|
||||||
|
return NULL;
|
||||||
|
return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the byte index for character index "idx" in string "str" with length
|
||||||
|
* "str_len".
|
||||||
|
* If going over the end return "str_len".
|
||||||
|
* If "idx" is negative count from the end, -1 is the last character.
|
||||||
|
* When going over the start return -1.
|
||||||
|
*/
|
||||||
|
static long
|
||||||
|
char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
|
||||||
|
{
|
||||||
|
varnumber_T nchar = idx;
|
||||||
|
size_t nbyte = 0;
|
||||||
|
|
||||||
|
if (nchar >= 0)
|
||||||
|
{
|
||||||
|
while (nchar > 0 && nbyte < str_len)
|
||||||
|
{
|
||||||
|
nbyte += MB_CPTR2LEN(str + nbyte);
|
||||||
|
--nchar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nbyte = str_len;
|
||||||
|
while (nchar < 0 && nbyte > 0)
|
||||||
|
{
|
||||||
|
--nbyte;
|
||||||
|
nbyte -= mb_head_off(str, str + nbyte);
|
||||||
|
++nchar;
|
||||||
|
}
|
||||||
|
if (nchar < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (long)nbyte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the slice "str[first:last]" using character indexes.
|
||||||
|
* Return NULL when the result is empty.
|
||||||
|
*/
|
||||||
|
char_u *
|
||||||
|
string_slice(char_u *str, varnumber_T first, varnumber_T last)
|
||||||
|
{
|
||||||
|
long start_byte, end_byte;
|
||||||
|
size_t slen;
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
return NULL;
|
||||||
|
slen = STRLEN(str);
|
||||||
|
start_byte = char_idx2byte(str, slen, first);
|
||||||
|
if (start_byte < 0)
|
||||||
|
start_byte = 0; // first index very negative: use zero
|
||||||
|
if (last == -1)
|
||||||
|
end_byte = (long)slen;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
end_byte = char_idx2byte(str, slen, last);
|
||||||
|
if (end_byte >= 0 && end_byte < (long)slen)
|
||||||
|
// end index is inclusive
|
||||||
|
end_byte += MB_CPTR2LEN(str + end_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_byte >= (long)slen || end_byte <= start_byte)
|
||||||
|
return NULL;
|
||||||
|
return vim_strnsave(str + start_byte, end_byte - start_byte);
|
||||||
|
}
|
||||||
|
|
||||||
static svar_T *
|
static svar_T *
|
||||||
get_script_svar(scriptref_T *sref, ectx_T *ectx)
|
get_script_svar(scriptref_T *sref, ectx_T *ectx)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user