diff src/vim9execute.c @ 23551:1bb7fa4f9b35 v8.2.2318

patch 8.2.2318: Vim9: string and list index work differently Commit: https://github.com/vim/vim/commit/e7525c552060dd04aacdbca6bb5fe6460cf4da60 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 9 13:20:37 2021 +0100 patch 8.2.2318: Vim9: string and list index work differently Problem: Vim9: string and list index work differently. Solution: Make string index work like list index. (closes https://github.com/vim/vim/issues/7643)
author Bram Moolenaar <Bram@vim.org>
date Sat, 09 Jan 2021 13:30:04 +0100
parents f90e429453fd
children 5c094273c015
line wrap: on
line diff
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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)
 {