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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202101091221.109CLgFc032898%40masaka.moolenaar.net.

Raspunde prin e-mail lui