]> granicus.if.org Git - vim/commitdiff
patch 8.2.2318: Vim9: string and list index work differently v8.2.2318
authorBram Moolenaar <Bram@vim.org>
Sat, 9 Jan 2021 12:20:37 +0000 (13:20 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 9 Jan 2021 12:20:37 +0000 (13:20 +0100)
Problem:    Vim9: string and list index work differently.
Solution:   Make string index work like list index. (closes #7643)

src/eval.c
src/list.c
src/proto/eval.pro
src/proto/vim9execute.pro
src/testdir/test_vim9_expr.vim
src/version.c
src/vim9execute.c

index 373c02753cd4d0bb617cefa48f610c37b1f4cb80..5ebe0375f22f47c42dc3f5190501895991ba4539 100644 (file)
@@ -5548,97 +5548,6 @@ eval_isdictc(int 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:
  * - expr[expr], expr[expr:expr] subscript
index 56ee5a5778bf5362866e2f146c90cec44ec2477c..4531e5885a126d896a08d526e341ef00d60adde2 100644 (file)
@@ -924,7 +924,7 @@ list_slice_or_index(
        if (!range)
        {
            if (verbose)
-               semsg(_(e_listidx), n1);
+               semsg(_(e_listidx), n1_arg);
            return FAIL;
        }
        n1 = n1 < 0 ? 0 : len;
index ec453645a17d6d6482f7619b02f82a560cad807b..cc86a5d26321a727a23121f2763bef1793a82854 100644 (file)
@@ -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_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);
index 8e948c5508523a18b189915223d4038e89281f5d..2337a6c45078e37217b6d562d7233f94c7e1fc84 100644 (file)
@@ -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);
index ae5ca2816cb97178225246303f82a294edf0a6a9..6d82e5e27df26257bb1de4c5facfafcb0b507fd3 100644 (file)
@@ -2304,7 +2304,7 @@ def Test_expr7_any_index_slice()
     # string is permissive, index out of range accepted
     g:teststring = 'abcdef'
     assert_equal('b', g:teststring[1])
-    assert_equal('', g:teststring[-1])
+    assert_equal('f', g:teststring[-1])
     assert_equal('', g:teststring[99])
 
     assert_equal('b', g:teststring[1 : 1])
@@ -2368,10 +2368,10 @@ def Test_expr7_any_index_slice()
   CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1)
   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)
   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)
   CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2)
@@ -2802,15 +2802,23 @@ enddef
 def Test_expr7_string_subscript()
   var lines =<< trim END
     var text = 'abcdef'
-    assert_equal('', text[-1])
+    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('', text[-1])
+    assert_equal('f', text[-1])
     assert_equal('á', text[0])
     assert_equal('b', text[1])
     assert_equal('ç', text[2])
@@ -2904,8 +2912,7 @@ def Test_expr7_list_subscript()
     assert_equal([], list[0 : -6])
     assert_equal([], list[0 : -99])
   END
-  CheckDefSuccess(lines)
-  CheckScriptSuccess(['vim9script'] + lines)
+  CheckDefAndScriptSuccess(lines)
 
   lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]']
   CheckDefExecFailure(lines, 'E1012:')
index dc1114cc6a8a95fa588d0e6cfa689d59e73158b5..06d525691001a21d63306bc0cf8dde996117f0ad 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2318,
 /**/
     2317,
 /**/
index 9a76cc9c36c90bcaab2d9a3ec98e8a05ae5705e9..e7a632a19312e32cee7fac5cf9109397553de030 100644 (file)
@@ -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 *
 get_script_svar(scriptref_T *sref, ectx_T *ectx)
 {