patch 8.2.2605: Vim9: string index and slice does not include composing chars

Problem:    Vim9: string index and slice does not include composing chars.
Solution:   Include composing characters. (issue #6563)
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index a78cf19..9829d88 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -2367,6 +2367,35 @@
     assert_equal('abcd', g:teststring[: -3])
     assert_equal('', g:teststring[: -9])
 
+    # composing characters are included
+    g:teststring = 'àéû'
+    assert_equal('à', g:teststring[0])
+    assert_equal('é', g:teststring[1])
+    assert_equal('û', g:teststring[2])
+    assert_equal('', g:teststring[3])
+    assert_equal('', g:teststring[4])
+
+    assert_equal('û', g:teststring[-1])
+    assert_equal('é', g:teststring[-2])
+    assert_equal('à', g:teststring[-3])
+    assert_equal('', g:teststring[-4])
+    assert_equal('', g:teststring[-5])
+
+    assert_equal('à', g:teststring[0 : 0])
+    assert_equal('é', g:teststring[1 : 1])
+    assert_equal('àé', g:teststring[0 : 1])
+    assert_equal('àéû', g:teststring[0 : -1])
+    assert_equal('àé', g:teststring[0 : -2])
+    assert_equal('à', g:teststring[0 : -3])
+    assert_equal('', g:teststring[0 : -4])
+    assert_equal('', g:teststring[0 : -5])
+    assert_equal('àéû', g:teststring[ : ])
+    assert_equal('àéû', g:teststring[0 : ])
+    assert_equal('éû', g:teststring[1 : ])
+    assert_equal('û', g:teststring[2 : ])
+    assert_equal('', g:teststring[3 : ])
+    assert_equal('', g:teststring[4 : ])
+
     # blob index cannot be out of range
     g:testblob = 0z01ab
     assert_equal(0x01, g:testblob[0])
diff --git a/src/version.c b/src/version.c
index b4307fb..c5797c3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2605,
+/**/
     2604,
 /**/
     2603,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index b2c28a3..c0a4874 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -985,8 +985,9 @@
 }
 
 /*
- * Return the character "str[index]" where "index" is the character index.  If
- * "index" is out of range NULL is returned.
+ * Return the character "str[index]" where "index" is the character index,
+ * including composing characters.
+ * If "index" is out of range NULL is returned.
  */
     char_u *
 char_from_string(char_u *str, varnumber_T index)
@@ -1005,7 +1006,7 @@
 	int	clen = 0;
 
 	for (nbyte = 0; nbyte < slen; ++clen)
-	    nbyte += MB_CPTR2LEN(str + nbyte);
+	    nbyte += mb_ptr2len(str + nbyte);
 	nchar = clen + index;
 	if (nchar < 0)
 	    // unlike list: index out of range results in empty string
@@ -1013,15 +1014,15 @@
     }
 
     for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
-	nbyte += MB_CPTR2LEN(str + nbyte);
+	nbyte += mb_ptr2len(str + nbyte);
     if (nbyte >= slen)
 	return NULL;
-    return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte));
+    return vim_strnsave(str + nbyte, mb_ptr2len(str + nbyte));
 }
 
 /*
  * Get the byte index for character index "idx" in string "str" with length
- * "str_len".
+ * "str_len".  Composing characters are included.
  * 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.
@@ -1036,7 +1037,7 @@
     {
 	while (nchar > 0 && nbyte < str_len)
 	{
-	    nbyte += MB_CPTR2LEN(str + nbyte);
+	    nbyte += mb_ptr2len(str + nbyte);
 	    --nchar;
 	}
     }
@@ -1056,7 +1057,8 @@
 }
 
 /*
- * Return the slice "str[first:last]" using character indexes.
+ * Return the slice "str[first : last]" using character indexes.  Composing
+ * characters are included.
  * "exclusive" is TRUE for slice().
  * Return NULL when the result is empty.
  */
@@ -1079,7 +1081,7 @@
 	end_byte = char_idx2byte(str, slen, last);
 	if (!exclusive && end_byte >= 0 && end_byte < (long)slen)
 	    // end index is inclusive
-	    end_byte += MB_CPTR2LEN(str + end_byte);
+	    end_byte += mb_ptr2len(str + end_byte);
     }
 
     if (start_byte >= (long)slen || end_byte <= start_byte)
@@ -3249,8 +3251,9 @@
 			res = string_slice(tv->vval.v_string, n1, n2, FALSE);
 		    else
 			// Index: The resulting variable is a string of a
-			// single character.  If the index is too big or
-			// negative the result is empty.
+			// single character (including composing characters).
+			// If the index is too big or negative the result is
+			// empty.
 			res = char_from_string(tv->vval.v_string, n2);
 		    vim_free(tv->vval.v_string);
 		    tv->vval.v_string = res;