Patch 8.2.2318
Problem: Vim9: string and list index work differently.
Solution: Make string index work like list index. (closes #7643)
Files: src/eval.c, src/proto/
eval.pro, src/vim9execute.c, src/list.c,
src/proto/
vim9execute.pro, src/testdir/test_vim9_expr.vim
*** ../vim-8.2.2317/src/eval.c 2021-01-06 21:59:35.170021945 +0100
--- src/eval.c 2021-01-09 12:54:26.802568463 +0100
***************
*** 5549,5645 ****
}
/*
- * 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:
* - expr[expr], expr[expr:expr] subscript
* - ".name" lookup
--- 5549,5554 ----
*** ../vim-8.2.2317/src/proto/
eval.pro 2021-01-05 22:08:17.205806639 +0100
--- src/proto/
eval.pro 2021-01-09 12:54:08.242616422 +0100
***************
*** 64,71 ****
int eval_isnamec(int c);
int eval_isnamec1(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 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);
--- 64,69 ----
*** ../vim-8.2.2317/src/vim9execute.c 2021-01-08 20:53:05.736404852 +0100
--- src/vim9execute.c 2021-01-09 13:11:00.382282339 +0100
***************
*** 863,868 ****
--- 863,970 ----
}
}
+ /*
+ * 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 *
get_script_svar(scriptref_T *sref, ectx_T *ectx)
{
*** ../vim-8.2.2317/src/list.c 2021-01-02 15:41:00.189079039 +0100
--- src/list.c 2021-01-09 13:05:02.775563656 +0100
***************
*** 924,930 ****
if (!range)
{
if (verbose)
! semsg(_(e_listidx), n1);
return FAIL;
}
n1 = n1 < 0 ? 0 : len;
--- 924,930 ----
if (!range)
{
if (verbose)
! semsg(_(e_listidx), n1_arg);
return FAIL;
}
n1 = n1 < 0 ? 0 : len;
*** ../vim-8.2.2317/src/proto/
vim9execute.pro 2020-12-22 17:35:50.043978116 +0100
--- src/proto/
vim9execute.pro 2021-01-09 12:54:05.318623985 +0100
***************
*** 1,6 ****
--- 1,8 ----
/* vim9execute.c */
void to_string_error(vartype_T vartype);
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 call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
void ex_disassemble(exarg_T *eap);
*** ../vim-8.2.2317/src/testdir/test_vim9_expr.vim 2021-01-05 22:08:17.205806639 +0100
--- src/testdir/test_vim9_expr.vim 2021-01-09 13:10:27.422393673 +0100
***************
*** 2304,2310 ****
# string is permissive, index out of range accepted
g:teststring = 'abcdef'
assert_equal('b', g:teststring[1])
! assert_equal('', g:teststring[-1])
assert_equal('', g:teststring[99])
assert_equal('b', g:teststring[1 : 1])
--- 2304,2310 ----
# string is permissive, index out of range accepted
g:teststring = 'abcdef'
assert_equal('b', g:teststring[1])
! assert_equal('f', g:teststring[-1])
assert_equal('', g:teststring[99])
assert_equal('b', g:teststring[1 : 1])
***************
*** 2368,2377 ****
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
! CheckDefExecFailure(['echo g:testlist[4]'], 'E684:', 1)
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
! CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:', 2)
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
--- 2368,2377 ----
CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2)
! CheckDefExecFailure(['echo g:testlist[4]'], 'E684: list index out of range: 4', 1)
CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2)
CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1)
! CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684: list index out of range: -5', 2)
CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1)
CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
***************
*** 2802,2816 ****
def Test_expr7_string_subscript()
var lines =<< trim END
var text = 'abcdef'
! assert_equal('', text[-1])
assert_equal('a', text[0])
assert_equal('e', text[4])
assert_equal('f', text[5])
assert_equal('', text[6])
text = 'ábçdëf'
assert_equal('', text[-999])
! assert_equal('', text[-1])
assert_equal('á', text[0])
assert_equal('b', text[1])
assert_equal('ç', text[2])
--- 2802,2824 ----
def Test_expr7_string_subscript()
var lines =<< trim END
var text = 'abcdef'
! assert_equal('f', text[-1])
assert_equal('a', text[0])
assert_equal('e', text[4])
assert_equal('f', text[5])
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'
assert_equal('', text[-999])
! assert_equal('f', text[-1])
assert_equal('á', text[0])
assert_equal('b', text[1])
assert_equal('ç', text[2])
***************
*** 2904,2911 ****
assert_equal([], list[0 : -6])
assert_equal([], list[0 : -99])
END
! CheckDefSuccess(lines)
! CheckScriptSuccess(['vim9script'] + lines)
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
CheckDefExecFailure(lines, 'E1012:')
--- 2912,2918 ----
assert_equal([], list[0 : -6])
assert_equal([], list[0 : -99])
END
! CheckDefAndScriptSuccess(lines)
lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
CheckDefExecFailure(lines, 'E1012:')
*** ../vim-8.2.2317/src/version.c 2021-01-09 12:09:18.403881398 +0100
--- src/version.c 2021-01-09 12:39:51.453011675 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2318,
/**/
--
hundred-and-one symptoms of being an internet addict:
117. You are more comfortable typing in html.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///