diff options
-rw-r--r-- | src/nvim/eval.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b06c102e2c..0a41ffd4bf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7285,6 +7285,58 @@ char *char_from_string(char *str, varnumber_T index) return xstrnsave(str + nbyte, (size_t)utf_ptr2len(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 zero. +static size_t char_idx2byte(char *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 += (size_t)utf_ptr2len(str + nbyte); + nchar--; + } + } else { + nbyte = str_len; + while (nchar < 0 && nbyte > 0) { + nbyte--; + nbyte -= (size_t)utf_head_off(str, str + nbyte); + nchar++; + } + } + return nbyte; +} + +/// Return the slice "str[first:last]" using character indexes. +/// Return NULL when the result is empty. +char *string_slice(char *str, varnumber_T first, varnumber_T last) +{ + if (str == NULL) { + return NULL; + } + size_t slen = strlen(str); + size_t start_byte = char_idx2byte(str, slen, first); + size_t end_byte; + if (last == -1) { + end_byte = slen; + } else { + end_byte = char_idx2byte(str, slen, last); + if (end_byte < slen) { + // end index is inclusive + end_byte += (size_t)utf_ptr2len(str + end_byte); + } + } + + if (start_byte >= slen || end_byte <= start_byte) { + return NULL; + } + return xstrnsave(str + start_byte, end_byte - start_byte); +} + /// Handle: /// - expr[expr], expr[expr:expr] subscript /// - ".name" lookup |