Patch 9.0.1617
Problem:    charidx() and utf16idx() result is not consistent with byteidx().
Solution:   When the index is equal to the length of the text return the
            lenght of the text instead of -1. (Yegappan Lakshmanan,
            closes #12503)
Files:      runtime/doc/builtin.txt, src/evalfunc.c, src/strings.c,
            src/testdir/test_functions.vim, src/testdir/test_vim9_builtin.vim


*** ../vim-9.0.1616/runtime/doc/builtin.txt     2023-05-06 14:08:10.139045046 
+0100
--- runtime/doc/builtin.txt     2023-06-08 17:04:20.819726310 +0100
***************
*** 1528,1538 ****
                When {utf16} is present and TRUE, {idx} is used as the UTF-16
                index in the String {expr} instead of as the byte index.
  
!               Returns -1 if the arguments are invalid or if {idx} is greater
!               than the index of the last byte in {string}.  An error is
!               given if the first argument is not a string, the second
!               argument is not a number or when the third argument is present
!               and is not zero or one.
  
                See |byteidx()| and |byteidxcomp()| for getting the byte index
                from the character index and |utf16idx()| for getting the
--- 1528,1540 ----
                When {utf16} is present and TRUE, {idx} is used as the UTF-16
                index in the String {expr} instead of as the byte index.
  
!               Returns -1 if the arguments are invalid or if there are less
!               than {idx} bytes. If there are exactly {idx} bytes the length
!               of the string in characters is returned.
! 
!               An error is given and -1 is returned if the first argument is
!               not a string, the second argument is not a number or when the
!               third argument is present and is not zero or one.
  
                See |byteidx()| and |byteidxcomp()| for getting the byte index
                from the character index and |utf16idx()| for getting the
***************
*** 10106,10113 ****
  <
                                                        *utf16idx()*
  utf16idx({string}, {idx} [, {countcc} [, {charidx}]])
!               Same as |charidx()| but returns the UTF-16 index of the byte
!               at {idx} in {string} (after converting it to UTF-16).
  
                When {charidx} is present and TRUE, {idx} is used as the
                character index in the String {string} instead of as the byte
--- 10121,10128 ----
  <
                                                        *utf16idx()*
  utf16idx({string}, {idx} [, {countcc} [, {charidx}]])
!               Same as |charidx()| but returns the UTF-16 code unit index of
!               the byte at {idx} in {string} (after converting it to UTF-16).
  
                When {charidx} is present and TRUE, {idx} is used as the
                character index in the String {string} instead of as the byte
***************
*** 10115,10120 ****
--- 10130,10139 ----
                An {idx} in the middle of a UTF-8 sequence is rounded upwards
                to the end of that sequence.
  
+               Returns -1 if the arguments are invalid or if there are less
+               than {idx} bytes in {string}. If there are exactly {idx} bytes
+               the length of the string in UTF-16 code units is returned.
+ 
                See |byteidx()| and |byteidxcomp()| for getting the byte index
                from the UTF-16 index and |charidx()| for getting the
                character index from the UTF-16 index.
*** ../vim-9.0.1616/src/evalfunc.c      2023-06-01 20:26:52.060180250 +0100
--- src/evalfunc.c      2023-06-08 16:58:53.682021173 +0100
***************
*** 1099,1104 ****
--- 1099,1105 ----
  static argcheck_T arg4_number_number_string_any[] = {arg_number, arg_number, 
arg_string, NULL};
  static argcheck_T arg4_string_string_any_string[] = {arg_string, arg_string, 
NULL, arg_string};
  static argcheck_T arg4_string_string_number_string[] = {arg_string, 
arg_string, arg_number, arg_string};
+ static argcheck_T arg4_string_number_bool_bool[] = {arg_string, arg_number, 
arg_bool, arg_bool};
  /* Function specific argument types (not covered by the above) */
  static argcheck_T arg15_assert_fails[] = {arg_string_or_nr, 
arg_string_or_list_any, NULL, arg_number, arg_string};
  static argcheck_T arg34_assert_inrange[] = {arg_float_or_nr, arg_float_or_nr, 
arg_float_or_nr, arg_string};
***************
*** 1814,1820 ****
                        ret_number,         f_charclass},
      {"charcol",               1, 2, FEARG_1,      arg2_string_or_list_number,
                        ret_number,         f_charcol},
!     {"charidx",               2, 4, FEARG_1,      arg3_string_number_bool,
                        ret_number,         f_charidx},
      {"chdir",         1, 1, FEARG_1,      arg1_string,
                        ret_string,         f_chdir},
--- 1815,1821 ----
                        ret_number,         f_charclass},
      {"charcol",               1, 2, FEARG_1,      arg2_string_or_list_number,
                        ret_number,         f_charcol},
!     {"charidx",               2, 4, FEARG_1,      
arg4_string_number_bool_bool,
                        ret_number,         f_charidx},
      {"chdir",         1, 1, FEARG_1,      arg1_string,
                        ret_string,         f_chdir},
***************
*** 2798,2804 ****
                        ret_dict_any,       f_undotree},
      {"uniq",          1, 3, FEARG_1,      arg13_sortuniq,
                        ret_first_arg,      f_uniq},
!     {"utf16idx",      2, 4, FEARG_1,      arg3_string_number_bool,
                        ret_number,         f_utf16idx},
      {"values",                1, 1, FEARG_1,      arg1_dict_any,
                        ret_list_member,    f_values},
--- 2799,2805 ----
                        ret_dict_any,       f_undotree},
      {"uniq",          1, 3, FEARG_1,      arg13_sortuniq,
                        ret_first_arg,      f_uniq},
!     {"utf16idx",      2, 4, FEARG_1,      arg4_string_number_bool_bool,
                        ret_number,         f_utf16idx},
      {"values",                1, 1, FEARG_1,      arg1_dict_any,
                        ret_list_member,    f_values},
***************
*** 3630,3636 ****
  
  /*
   * Set the cursor position.
!  * If 'charcol' is TRUE, then use the column number as a character offset.
   * Otherwise use the column number as a byte offset.
   */
      static void
--- 3631,3637 ----
  
  /*
   * Set the cursor position.
!  * If "charcol" is TRUE, then use the column number as a character offset.
   * Otherwise use the column number as a byte offset.
   */
      static void
*** ../vim-9.0.1616/src/strings.c       2023-05-08 15:31:34.247545088 +0100
--- src/strings.c       2023-06-08 16:58:53.682021173 +0100
***************
*** 1054,1060 ****
  
      if (in_vim9script()
            && (check_for_string_arg(argvars, 0) == FAIL
!               || check_for_number_arg(argvars, 1) == FAIL))
        return;
  
      char_u *str = tv_get_string_chk(&argvars[0]);
--- 1054,1061 ----
  
      if (in_vim9script()
            && (check_for_string_arg(argvars, 0) == FAIL
!               || check_for_number_arg(argvars, 1) == FAIL
!               || check_for_opt_bool_arg(argvars, 2) == FAIL))
        return;
  
      char_u *str = tv_get_string_chk(&argvars[0]);
***************
*** 1158,1164 ****
--- 1159,1172 ----
      for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++)
      {
        if (*p == NUL)
+       {
+           // If the index is exactly the number of bytes or utf-16 code units
+           // in the string then return the length of the string in
+           // characters.
+           if (utf16idx ? (idx == 0) : (p == (str + idx)))
+               rettv->vval.v_number = len;
            return;
+       }
        if (utf16idx)
        {
            idx--;
***************
*** 1775,1781 ****
--- 1783,1796 ----
      for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++)
      {
        if (*p == NUL)
+       {
+           // If the index is exactly the number of bytes or characters in the
+           // string then return the length of the string in utf-16 code
+           // units.
+           if (charidx ? (idx == 0) : (p == (str + idx)))
+               rettv->vval.v_number = len;
            return;
+       }
        int clen = ptr2len(p);
        int c = (clen > 1) ? utf_ptr2char(p) : *p;
        if (c > 0xFFFF)
*** ../vim-9.0.1616/src/testdir/test_functions.vim      2023-06-01 
20:26:52.060180250 +0100
--- src/testdir/test_functions.vim      2023-06-08 16:58:53.682021173 +0100
***************
*** 1395,1401 ****
    call assert_equal(1, charidx(a, 3))
    call assert_equal(2, charidx(a, 4))
    call assert_equal(3, charidx(a, 7))
!   call assert_equal(-1, charidx(a, 8))
    call assert_equal(-1, charidx(a, -1))
  
    " count composing characters
--- 1395,1402 ----
    call assert_equal(1, charidx(a, 3))
    call assert_equal(2, charidx(a, 4))
    call assert_equal(3, charidx(a, 7))
!   call assert_equal(4, charidx(a, 8))
!   call assert_equal(-1, charidx(a, 9))
    call assert_equal(-1, charidx(a, -1))
  
    " count composing characters
***************
*** 1403,1416 ****
    call assert_equal(2, a->charidx(2, 1))
    call assert_equal(3, a->charidx(4, 1))
    call assert_equal(5, a->charidx(7, 1))
!   call assert_equal(-1, a->charidx(8, 1))
  
    " empty string
!   call assert_equal(-1, charidx('', 0))
!   call assert_equal(-1, charidx('', 0, 1))
  
    " error cases
!   call assert_equal(-1, charidx(test_null_string(), 0))
    call assert_fails('let x = charidx([], 1)', 'E1174:')
    call assert_fails('let x = charidx("abc", [])', 'E1210:')
    call assert_fails('let x = charidx("abc", 1, [])', 'E1212:')
--- 1404,1421 ----
    call assert_equal(2, a->charidx(2, 1))
    call assert_equal(3, a->charidx(4, 1))
    call assert_equal(5, a->charidx(7, 1))
!   call assert_equal(6, a->charidx(8, 1))
!   call assert_equal(-1, a->charidx(9, 1))
  
    " empty string
!   call assert_equal(0, charidx('', 0))
!   call assert_equal(-1, charidx('', 1))
!   call assert_equal(0, charidx('', 0, 1))
!   call assert_equal(-1, charidx('', 1, 1))
  
    " error cases
!   call assert_equal(0, charidx(test_null_string(), 0))
!   call assert_equal(-1, charidx(test_null_string(), 1))
    call assert_fails('let x = charidx([], 1)', 'E1174:')
    call assert_fails('let x = charidx("abc", [])', 'E1210:')
    call assert_fails('let x = charidx("abc", 1, [])', 'E1212:')
***************
*** 1422,1431 ****
  func Test_charidx_from_utf16_index()
    " string with single byte characters
    let str = "abc"
!   for i in range(3)
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 3, v:false, v:true))
  
    " string with two byte characters
    let str = "a漏漏b"
--- 1427,1436 ----
  func Test_charidx_from_utf16_index()
    " string with single byte characters
    let str = "abc"
!   for i in range(4)
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 4, v:false, v:true))
  
    " string with two byte characters
    let str = "a漏漏b"
***************
*** 1433,1439 ****
    call assert_equal(1, charidx(str, 1, v:false, v:true))
    call assert_equal(2, charidx(str, 2, v:false, v:true))
    call assert_equal(3, charidx(str, 3, v:false, v:true))
!   call assert_equal(-1, charidx(str, 4, v:false, v:true))
  
    " string with four byte characters
    let str = "a馃槉馃槉b"
--- 1438,1445 ----
    call assert_equal(1, charidx(str, 1, v:false, v:true))
    call assert_equal(2, charidx(str, 2, v:false, v:true))
    call assert_equal(3, charidx(str, 3, v:false, v:true))
!   call assert_equal(4, charidx(str, 4, v:false, v:true))
!   call assert_equal(-1, charidx(str, 5, v:false, v:true))
  
    " string with four byte characters
    let str = "a馃槉馃槉b"
***************
*** 1443,1480 ****
    call assert_equal(2, charidx(str, 3, v:false, v:true))
    call assert_equal(2, charidx(str, 4, v:false, v:true))
    call assert_equal(3, charidx(str, 5, v:false, v:true))
!   call assert_equal(-1, charidx(str, 6, v:false, v:true))
  
    " string with composing characters
    let str = '-a虂-b虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 4, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, charidx(str, i, v:true, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 6, v:true, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 4, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, charidx(str, i, v:true, v:true))
    endfor
!   call assert_equal(-1, charidx(str, 8, v:true, v:true))
  
    " empty string
!   call assert_equal(-1, charidx('', 0, v:false, v:true))
!   call assert_equal(-1, charidx('', 0, v:true, v:true))
  
    " error cases
!   call assert_equal(-1, charidx('', 0, v:false, v:true))
!   call assert_equal(-1, charidx('', 0, v:true, v:true))
!   call assert_equal(-1, charidx(test_null_string(), 0, v:false, v:true))
    call assert_fails('let x = charidx("abc", 1, v:false, [])', 'E1212:')
    call assert_fails('let x = charidx("abc", 1, v:true, [])', 'E1212:')
  endfunc
--- 1449,1496 ----
    call assert_equal(2, charidx(str, 3, v:false, v:true))
    call assert_equal(2, charidx(str, 4, v:false, v:true))
    call assert_equal(3, charidx(str, 5, v:false, v:true))
!   call assert_equal(4, charidx(str, 6, v:false, v:true))
!   call assert_equal(-1, charidx(str, 7, v:false, v:true))
  
    " string with composing characters
    let str = '-a虂-b虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(4, charidx(str, 4, v:false, v:true))
!   call assert_equal(-1, charidx(str, 5, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, charidx(str, i, v:true, v:true))
    endfor
!   call assert_equal(6, charidx(str, 6, v:true, v:true))
!   call assert_equal(-1, charidx(str, 7, v:true, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, charidx(str, i, v:false, v:true))
    endfor
!   call assert_equal(4, charidx(str, 4, v:false, v:true))
!   call assert_equal(-1, charidx(str, 5, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, charidx(str, i, v:true, v:true))
    endfor
!   call assert_equal(8, charidx(str, 8, v:true, v:true))
!   call assert_equal(-1, charidx(str, 9, v:true, v:true))
  
    " empty string
!   call assert_equal(0, charidx('', 0, v:false, v:true))
!   call assert_equal(-1, charidx('', 1, v:false, v:true))
!   call assert_equal(0, charidx('', 0, v:true, v:true))
!   call assert_equal(-1, charidx('', 1, v:true, v:true))
  
    " error cases
!   call assert_equal(0, charidx('', 0, v:false, v:true))
!   call assert_equal(-1, charidx('', 1, v:false, v:true))
!   call assert_equal(0, charidx('', 0, v:true, v:true))
!   call assert_equal(-1, charidx('', 1, v:true, v:true))
!   call assert_equal(0, charidx(test_null_string(), 0, v:false, v:true))
!   call assert_equal(-1, charidx(test_null_string(), 1, v:false, v:true))
    call assert_fails('let x = charidx("abc", 1, v:false, [])', 'E1212:')
    call assert_fails('let x = charidx("abc", 1, v:true, [])', 'E1212:')
  endfunc
***************
*** 1483,1492 ****
  func Test_utf16idx_from_byteidx()
    " UTF-16 index of a string with single byte characters
    let str = "abc"
!   for i in range(3)
      call assert_equal(i, utf16idx(str, i))
    endfor
!   call assert_equal(-1, utf16idx(str, 3))
  
    " UTF-16 index of a string with two byte characters
    let str = 'a漏漏b'
--- 1499,1508 ----
  func Test_utf16idx_from_byteidx()
    " UTF-16 index of a string with single byte characters
    let str = "abc"
!   for i in range(4)
      call assert_equal(i, utf16idx(str, i))
    endfor
!   call assert_equal(-1, utf16idx(str, 4))
  
    " UTF-16 index of a string with two byte characters
    let str = 'a漏漏b'
***************
*** 1496,1502 ****
    call assert_equal(2, str->utf16idx(3))
    call assert_equal(2, str->utf16idx(4))
    call assert_equal(3, str->utf16idx(5))
!   call assert_equal(-1, str->utf16idx(6))
  
    " UTF-16 index of a string with four byte characters
    let str = 'a馃槉馃槉b'
--- 1512,1519 ----
    call assert_equal(2, str->utf16idx(3))
    call assert_equal(2, str->utf16idx(4))
    call assert_equal(3, str->utf16idx(5))
!   call assert_equal(4, str->utf16idx(6))
!   call assert_equal(-1, str->utf16idx(7))
  
    " UTF-16 index of a string with four byte characters
    let str = 'a馃槉馃槉b'
***************
*** 1510,1516 ****
    call assert_equal(4, utf16idx(str, 7))
    call assert_equal(4, utf16idx(str, 8))
    call assert_equal(5, utf16idx(str, 9))
!   call assert_equal(-1, utf16idx(str, 10))
  
    " UTF-16 index of a string with composing characters
    let str = '-a虂-b虂'
--- 1527,1534 ----
    call assert_equal(4, utf16idx(str, 7))
    call assert_equal(4, utf16idx(str, 8))
    call assert_equal(5, utf16idx(str, 9))
!   call assert_equal(6, utf16idx(str, 10))
!   call assert_equal(-1, utf16idx(str, 11))
  
    " UTF-16 index of a string with composing characters
    let str = '-a虂-b虂'
***************
*** 1522,1528 ****
    call assert_equal(3, utf16idx(str, 5))
    call assert_equal(3, utf16idx(str, 6))
    call assert_equal(3, utf16idx(str, 7))
!   call assert_equal(-1, utf16idx(str, 8))
    call assert_equal(0, utf16idx(str, 0, v:true))
    call assert_equal(1, utf16idx(str, 1, v:true))
    call assert_equal(2, utf16idx(str, 2, v:true))
--- 1540,1547 ----
    call assert_equal(3, utf16idx(str, 5))
    call assert_equal(3, utf16idx(str, 6))
    call assert_equal(3, utf16idx(str, 7))
!   call assert_equal(4, utf16idx(str, 8))
!   call assert_equal(-1, utf16idx(str, 9))
    call assert_equal(0, utf16idx(str, 0, v:true))
    call assert_equal(1, utf16idx(str, 1, v:true))
    call assert_equal(2, utf16idx(str, 2, v:true))
***************
*** 1531,1537 ****
    call assert_equal(4, utf16idx(str, 5, v:true))
    call assert_equal(5, utf16idx(str, 6, v:true))
    call assert_equal(5, utf16idx(str, 7, v:true))
!   call assert_equal(-1, utf16idx(str, 8, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
--- 1550,1557 ----
    call assert_equal(4, utf16idx(str, 5, v:true))
    call assert_equal(5, utf16idx(str, 6, v:true))
    call assert_equal(5, utf16idx(str, 7, v:true))
!   call assert_equal(6, utf16idx(str, 8, v:true))
!   call assert_equal(-1, utf16idx(str, 9, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
***************
*** 1547,1553 ****
    call assert_equal(3, utf16idx(str, 9))
    call assert_equal(3, utf16idx(str, 10))
    call assert_equal(3, utf16idx(str, 11))
!   call assert_equal(-1, utf16idx(str, 12))
    call assert_equal(0, utf16idx(str, 0, v:true))
    call assert_equal(1, utf16idx(str, 1, v:true))
    call assert_equal(2, utf16idx(str, 2, v:true))
--- 1567,1574 ----
    call assert_equal(3, utf16idx(str, 9))
    call assert_equal(3, utf16idx(str, 10))
    call assert_equal(3, utf16idx(str, 11))
!   call assert_equal(4, utf16idx(str, 12))
!   call assert_equal(-1, utf16idx(str, 13))
    call assert_equal(0, utf16idx(str, 0, v:true))
    call assert_equal(1, utf16idx(str, 1, v:true))
    call assert_equal(2, utf16idx(str, 2, v:true))
***************
*** 1560,1575 ****
    call assert_equal(6, utf16idx(str, 9, v:true))
    call assert_equal(7, utf16idx(str, 10, v:true))
    call assert_equal(7, utf16idx(str, 11, v:true))
!   call assert_equal(-1, utf16idx(str, 12, v:true))
  
    " empty string
!   call assert_equal(-1, utf16idx('', 0))
!   call assert_equal(-1, utf16idx('', 0, v:true))
  
    " error cases
!   call assert_equal(-1, utf16idx("", 0))
    call assert_equal(-1, utf16idx("abc", -1))
!   call assert_equal(-1, utf16idx(test_null_string(), 0))
    call assert_fails('let l = utf16idx([], 0)', 'E1174:')
    call assert_fails('let l = utf16idx("ab", [])', 'E1210:')
    call assert_fails('let l = utf16idx("ab", 0, [])', 'E1212:')
--- 1581,1601 ----
    call assert_equal(6, utf16idx(str, 9, v:true))
    call assert_equal(7, utf16idx(str, 10, v:true))
    call assert_equal(7, utf16idx(str, 11, v:true))
!   call assert_equal(8, utf16idx(str, 12, v:true))
!   call assert_equal(-1, utf16idx(str, 13, v:true))
  
    " empty string
!   call assert_equal(0, utf16idx('', 0))
!   call assert_equal(-1, utf16idx('', 1))
!   call assert_equal(0, utf16idx('', 0, v:true))
!   call assert_equal(-1, utf16idx('', 1, v:true))
  
    " error cases
!   call assert_equal(0, utf16idx("", 0))
!   call assert_equal(-1, utf16idx("", 1))
    call assert_equal(-1, utf16idx("abc", -1))
!   call assert_equal(0, utf16idx(test_null_string(), 0))
!   call assert_equal(-1, utf16idx(test_null_string(), 1))
    call assert_fails('let l = utf16idx([], 0)', 'E1174:')
    call assert_fails('let l = utf16idx("ab", [])', 'E1210:')
    call assert_fails('let l = utf16idx("ab", 0, [])', 'E1212:')
***************
*** 1581,1594 ****
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 3, v:false, v:true))
  
    " UTF-16 index of a string with two byte characters
    let str = "a漏漏b"
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 4, v:false, v:true))
  
    " UTF-16 index of a string with four byte characters
    let str = "a馃槉馃槉b"
--- 1607,1622 ----
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(3, utf16idx(str, 3, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 4, v:false, v:true))
  
    " UTF-16 index of a string with two byte characters
    let str = "a漏漏b"
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(4, utf16idx(str, 4, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
  
    " UTF-16 index of a string with four byte characters
    let str = "a馃槉馃槉b"
***************
*** 1596,1631 ****
    call assert_equal(2, utf16idx(str, 1, v:false, v:true))
    call assert_equal(4, utf16idx(str, 2, v:false, v:true))
    call assert_equal(5, utf16idx(str, 3, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 4, v:false, v:true))
  
    " UTF-16 index of a string with composing characters
    let str = '-a虂-b虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 4, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, utf16idx(str, i, v:true, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 6, v:true, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 4, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, utf16idx(str, i, v:true, v:true))
    endfor
!   call assert_equal(-1, utf16idx(str, 8, v:true, v:true))
  
    " empty string
!   call assert_equal(-1, utf16idx('', 0, v:false, v:true))
!   call assert_equal(-1, utf16idx('', 0, v:true, v:true))
  
    " error cases
!   call assert_equal(-1, utf16idx(test_null_string(), 0, v:true, v:true))
    call assert_fails('let l = utf16idx("ab", 0, v:false, [])', 'E1212:')
  endfunc
  
--- 1624,1667 ----
    call assert_equal(2, utf16idx(str, 1, v:false, v:true))
    call assert_equal(4, utf16idx(str, 2, v:false, v:true))
    call assert_equal(5, utf16idx(str, 3, v:false, v:true))
!   call assert_equal(6, utf16idx(str, 4, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
  
    " UTF-16 index of a string with composing characters
    let str = '-a虂-b虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(4, utf16idx(str, 4, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, utf16idx(str, i, v:true, v:true))
    endfor
!   call assert_equal(6, utf16idx(str, 6, v:true, v:true))
!   call assert_equal(-1, utf16idx(str, 7, v:true, v:true))
  
    " string with multiple composing characters
    let str = '-a台虂-a台虂'
    for i in str->strcharlen()->range()
      call assert_equal(i, utf16idx(str, i, v:false, v:true))
    endfor
!   call assert_equal(4, utf16idx(str, 4, v:false, v:true))
!   call assert_equal(-1, utf16idx(str, 5, v:false, v:true))
    for i in str->strchars()->range()
      call assert_equal(i, utf16idx(str, i, v:true, v:true))
    endfor
!   call assert_equal(8, utf16idx(str, 8, v:true, v:true))
!   call assert_equal(-1, utf16idx(str, 9, v:true, v:true))
  
    " empty string
!   call assert_equal(0, utf16idx('', 0, v:false, v:true))
!   call assert_equal(-1, utf16idx('', 1, v:false, v:true))
!   call assert_equal(0, utf16idx('', 0, v:true, v:true))
!   call assert_equal(-1, utf16idx('', 1, v:true, v:true))
  
    " error cases
!   call assert_equal(0, utf16idx(test_null_string(), 0, v:true, v:true))
!   call assert_equal(-1, utf16idx(test_null_string(), 1, v:true, v:true))
    call assert_fails('let l = utf16idx("ab", 0, v:false, [])', 'E1212:')
  endfunc
  
*** ../vim-9.0.1616/src/testdir/test_vim9_builtin.vim   2023-05-11 
15:02:52.231456894 +0100
--- src/testdir/test_vim9_builtin.vim   2023-06-08 16:58:53.682021173 +0100
***************
*** 460,465 ****
--- 460,466 ----
  def Test_byteidx()
    v9.CheckDefAndScriptFailure(['byteidx(1, 2)'], ['E1013: Argument 1: type 
mismatch, expected string but got number', 'E1174: String required for argument 
1'])
    v9.CheckDefAndScriptFailure(['byteidx("a", "b")'], ['E1013: Argument 2: 
type mismatch, expected number but got string', 'E1210: Number required for 
argument 2'])
+   v9.CheckDefAndScriptFailure(['byteidx("a", 0, "")'], ['E1013: Argument 3: 
type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 3'])
    byteidx('', 0)->assert_equal(0)
    byteidx('', 1)->assert_equal(-1)
  enddef
***************
*** 467,472 ****
--- 468,474 ----
  def Test_byteidxcomp()
    v9.CheckDefAndScriptFailure(['byteidxcomp(1, 2)'], ['E1013: Argument 1: 
type mismatch, expected string but got number', 'E1174: String required for 
argument 1'])
    v9.CheckDefAndScriptFailure(['byteidxcomp("a", "b")'], ['E1013: Argument 2: 
type mismatch, expected number but got string', 'E1210: Number required for 
argument 2'])
+   v9.CheckDefAndScriptFailure(['byteidxcomp("a", 0, "")'], ['E1013: Argument 
3: type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 3'])
  enddef
  
  def Test_call_call()
***************
*** 702,708 ****
    v9.CheckDefAndScriptFailure(['charidx(0z10, 1)'], ['E1013: Argument 1: type 
mismatch, expected string but got blob', 'E1174: String required for argument 
1'])
    v9.CheckDefAndScriptFailure(['charidx("a", "b")'], ['E1013: Argument 2: 
type mismatch, expected number but got string', 'E1210: Number required for 
argument 2'])
    v9.CheckDefAndScriptFailure(['charidx("a", 1, "")'], ['E1013: Argument 3: 
type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 3'])
!   charidx('', 0)->assert_equal(-1)
    charidx('', 1)->assert_equal(-1)
  enddef
  
--- 704,711 ----
    v9.CheckDefAndScriptFailure(['charidx(0z10, 1)'], ['E1013: Argument 1: type 
mismatch, expected string but got blob', 'E1174: String required for argument 
1'])
    v9.CheckDefAndScriptFailure(['charidx("a", "b")'], ['E1013: Argument 2: 
type mismatch, expected number but got string', 'E1210: Number required for 
argument 2'])
    v9.CheckDefAndScriptFailure(['charidx("a", 1, "")'], ['E1013: Argument 3: 
type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 3'])
!   v9.CheckDefAndScriptFailure(['charidx("a", 1, 0, "")'], ['E1013: Argument 
4: type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 4'])
!   charidx('', 0)->assert_equal(0)
    charidx('', 1)->assert_equal(-1)
  enddef
  
***************
*** 4305,4310 ****
--- 4308,4321 ----
    strtrans('')->assert_equal('')
  enddef
  
+ def Test_strutf16len()
+   v9.CheckDefAndScriptFailure(['strutf16len([])'], ['E1013: Argument 1: type 
mismatch, expected string but got list<unknown>', 'E1174: String required for 
argument 1'])
+   v9.CheckDefAndScriptFailure(['strutf16len("a", "")'], ['E1013: Argument 2: 
type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 2'])
+   ""->strutf16len()->assert_equal(0)
+   '-a台虂-a台虂'->strutf16len(true)->assert_equal(8)
+   '-a台虂-a台虂'->strutf16len(false)->assert_equal(4)
+ enddef
+ 
  def Test_strwidth()
    v9.CheckDefAndScriptFailure(['strwidth(10)'], ['E1013: Argument 1: type 
mismatch, expected string but got number', 'E1174: String required for argument 
1'])
    assert_equal(4, strwidth('abcd'))
***************
*** 4727,4732 ****
--- 4738,4752 ----
    v9.CheckDefFailure(['var l: list<number> = uniq(["a", "b"])'], 'E1012: Type 
mismatch; expected list<number> but got list<string>')
  enddef
  
+ def Test_utf16idx()
+   v9.CheckDefAndScriptFailure(['utf16idx(0z10, 1)'], ['E1013: Argument 1: 
type mismatch, expected string but got blob', 'E1174: String required for 
argument 1'])
+   v9.CheckDefAndScriptFailure(['utf16idx("a", "b")'], ['E1013: Argument 2: 
type mismatch, expected number but got string', 'E1210: Number required for 
argument 2'])
+   v9.CheckDefAndScriptFailure(['utf16idx("a", 1, "")'], ['E1013: Argument 3: 
type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 3'])
+   v9.CheckDefAndScriptFailure(['utf16idx("a", 1, 0, "")'], ['E1013: Argument 
4: type mismatch, expected bool but got string', 'E1212: Bool required for 
argument 4'])
+   utf16idx('', 0)->assert_equal(0)
+   utf16idx('', 1)->assert_equal(-1)
+ enddef
+ 
  def Test_uniq_const()
    var lines =<< trim END
        const l = [1, 2, 3, 4]
*** ../vim-9.0.1616/src/version.c       2023-06-07 19:09:52.536499675 +0100
--- src/version.c       2023-06-08 17:03:06.647411042 +0100
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1617,
  /**/

-- 
There are three kinds of persons: Those who can count and those who can't.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/20230608161024.74D221C0642%40moolenaar.net.

Raspunde prin e-mail lui