Mercurial > vim
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) {