aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval.c52
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