]> granicus.if.org Git - vim/commitdiff
patch 8.2.2344: using inclusive index for slice is not always desired v8.2.2344
authorBram Moolenaar <Bram@vim.org>
Wed, 13 Jan 2021 20:47:15 +0000 (21:47 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 13 Jan 2021 20:47:15 +0000 (21:47 +0100)
Problem:    Using inclusive index for slice is not always desired.
Solution:   Add the slice() method, which has an exclusive index. (closes
            #7408)

runtime/doc/eval.txt
runtime/doc/usr_41.txt
src/eval.c
src/evalfunc.c
src/list.c
src/proto/eval.pro
src/proto/list.pro
src/proto/vim9execute.pro
src/testdir/test_vim9_builtin.vim
src/version.c
src/vim9execute.c

index 01a1235653d5cb23f38810af0baf5854fc793cd0..4bab6ef6f8036c7362be9506265f5acbffd2902b 100644 (file)
@@ -314,6 +314,9 @@ similar to -1. >
        :let shortlist = mylist[2:2]    " List with one item: [3]
        :let otherlist = mylist[:]      " make a copy of the List
 
+Notice that the last index is inclusive.  If you prefer using an exclusive
+index use the |slice()| method.
+
 If the first index is beyond the last item of the List or the second item is
 before the first item, the result is an empty list.  There is no error
 message.
@@ -1217,6 +1220,9 @@ a Number it is first converted to a String.
 In Vim9 script the indexes are character indexes.  To use byte indexes use
 |strpart()|.
 
+The item at index expr1b is included, it is inclusive.  For an exclusive index
+use the |slice()| function.
+
 If expr1a is omitted zero is used.  If expr1b is omitted the length of the
 string minus one is used.
 
@@ -2884,6 +2890,8 @@ sign_unplacelist({list})  List    unplace a list of signs
 simplify({filename})           String  simplify filename as much as possible
 sin({expr})                    Float   sine of {expr}
 sinh({expr})                   Float   hyperbolic sine of {expr}
+slice({expr}, {start} [, {end}])  String, List or Blob
+                                       slice of a String, List or Blob
 sort({list} [, {func} [, {dict}]])
                                List    sort {list}, using {func} to compare
 sound_clear()                  none    stop playing all sounds
@@ -9862,6 +9870,18 @@ sinh({expr})                                             *sinh()*
                {only available when compiled with the |+float| feature}
 
 
+slice({expr}, {start} [, {end}])                       *slice()* 
+               Similar to using a |slice| "expr[start : end]", but "end" is
+               used exclusive.  And for a string the indexes are used as
+               character indexes instead of byte indexes, like in
+               |vim9script|.
+               When {end} is omitted the slice continues to the last item.
+               When {end} is -1 the last item is omitted.
+
+               Can also be used as a |method|: >
+                       GetList()->slice(offset)
+
+
 sort({list} [, {func} [, {dict}]])                     *sort()* *E702*
                Sort the items in {list} in-place.  Returns {list}.
 
index bac064cf7d46f4559cafb5a7c86129e6d3dba660..5d4686ae04f5961d15c021255a1c0c69e06a96fc 100644 (file)
@@ -619,6 +619,8 @@ String manipulation:                                        *string-functions*
        submatch()              get a specific match in ":s" and substitute()
        strpart()               get part of a string using byte index
        strcharpart()           get part of a string using char index
+       slice()                 take a slice of a string, using char index in
+                               Vim9 script
        strgetchar()            get character from a string using char index
        expand()                expand special keywords
        expandcmd()             expand a command like done for `:edit`
@@ -648,6 +650,7 @@ List manipulation:                                  *list-functions*
        map()                   change each List item
        mapnew()                make a new List with changed items
        reduce()                reduce a List to a value
+       slice()                 take a slice of a List
        sort()                  sort a List
        reverse()               reverse the order of a List
        uniq()                  remove copies of repeated adjacent items
index 8115c7cb023e778947f318279e5c0944dac5f067..84f6c2bf27517259a9031a90e65d647556b10129 100644 (file)
@@ -3877,8 +3877,9 @@ eval_index(
     if (evaluate)
     {
        int res = eval_index_inner(rettv, range,
-               empty1 ? NULL : &var1, empty2 ? NULL : &var2,
+               empty1 ? NULL : &var1, empty2 ? NULL : &var2, FALSE,
                key, keylen, verbose);
+
        if (!empty1)
            clear_tv(&var1);
        if (range)
@@ -3937,10 +3938,27 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
     return OK;
 }
 
+/*
+ * slice() function
+ */
+    void
+f_slice(typval_T *argvars, typval_T *rettv)
+{
+    if (check_can_index(argvars, TRUE, FALSE) == OK)
+    {
+       copy_tv(argvars, rettv);
+       eval_index_inner(rettv, TRUE, argvars + 1,
+               argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
+               TRUE, NULL, 0, FALSE);
+    }
+}
+
 /*
  * Apply index or range to "rettv".
  * "var1" is the first index, NULL for [:expr].
  * "var2" is the second index, NULL for [expr] and [expr: ]
+ * "exclusive" is TRUE for slice(): second index is exclusive, use character
+ * index for string.
  * Alternatively, "key" is not NULL, then key[keylen] is the dict index.
  */
     int
@@ -3949,12 +3967,13 @@ eval_index_inner(
        int         is_range,
        typval_T    *var1,
        typval_T    *var2,
+       int         exclusive,
        char_u      *key,
        int         keylen,
        int         verbose)
 {
-    long       n1, n2 = 0;
-    long       len;
+    varnumber_T            n1, n2 = 0;
+    long           len;
 
     n1 = 0;
     if (var1 != NULL && rettv->v_type != VAR_DICT)
@@ -3968,10 +3987,10 @@ eval_index_inner(
                emsg(_(e_cannot_slice_dictionary));
            return FAIL;
        }
-       if (var2 == NULL)
-           n2 = -1;
-       else
+       if (var2 != NULL)
            n2 = tv_get_number(var2);
+       else
+           n2 = VARNUM_MAX;
     }
 
     switch (rettv->v_type)
@@ -3994,10 +4013,10 @@ eval_index_inner(
                char_u  *s = tv_get_string(rettv);
 
                len = (long)STRLEN(s);
-               if (in_vim9script())
+               if (in_vim9script() || exclusive)
                {
                    if (is_range)
-                       s = string_slice(s, n1, n2);
+                       s = string_slice(s, n1, n2, exclusive);
                    else
                        s = char_from_string(s, n1);
                }
@@ -4015,6 +4034,8 @@ eval_index_inner(
                        n2 = len + n2;
                    else if (n2 >= len)
                        n2 = len;
+                   if (exclusive)
+                       --n2;
                    if (n1 >= len || n2 < 0 || n1 > n2)
                        s = NULL;
                    else
@@ -4051,7 +4072,9 @@ eval_index_inner(
                if (n2 < 0)
                    n2 = len + n2;
                else if (n2 >= len)
-                   n2 = len - 1;
+                   n2 = len - (exclusive ? 0 : 1);
+               if (exclusive)
+                   --n2;
                if (n1 >= len || n2 < 0 || n1 > n2)
                {
                    clear_tv(rettv);
@@ -4103,9 +4126,9 @@ eval_index_inner(
            if (var1 == NULL)
                n1 = 0;
            if (var2 == NULL)
-               n2 = -1;
+               n2 = VARNUM_MAX;
            if (list_slice_or_index(rettv->vval.v_list,
-                                   is_range, n1, n2, rettv, verbose) == FAIL)
+                         is_range, n1, n2, exclusive, rettv, verbose) == FAIL)
                return FAIL;
            break;
 
index a891378ee8670d626e942e7f6e469dd8e169e806..0e800c2b6f5aae77db3f5b308c8caf0f8068458c 100644 (file)
@@ -1500,6 +1500,8 @@ static funcentry_T global_functions[] =
                        ret_float,          FLOAT_FUNC(f_sin)},
     {"sinh",           1, 1, FEARG_1,      NULL,
                        ret_float,          FLOAT_FUNC(f_sinh)},
+    {"slice",          2, 3, FEARG_1,      NULL,
+                       ret_first_arg,      f_slice},
     {"sort",           1, 3, FEARG_1,      NULL,
                        ret_first_arg,      f_sort},
     {"sound_clear",    0, 0, 0,            NULL,
index f7842fa875d7065bef8e4084119fa5effac3057a..0bca0b5530a7b7096c6e0dd1f8075faa3bbd5237 100644 (file)
@@ -905,14 +905,15 @@ list_slice(list_T *ol, long n1, long n2)
 list_slice_or_index(
            list_T      *list,
            int         range,
-           long        n1_arg,
-           long        n2_arg,
+           varnumber_T n1_arg,
+           varnumber_T n2_arg,
+           int         exclusive,
            typval_T    *rettv,
            int         verbose)
 {
     long       len = list_len(list);
-    long       n1 = n1_arg;
-    long       n2 = n2_arg;
+    varnumber_T        n1 = n1_arg;
+    varnumber_T        n2 = n2_arg;
     typval_T   var1;
 
     if (n1 < 0)
@@ -936,7 +937,9 @@ list_slice_or_index(
        if (n2 < 0)
            n2 = len + n2;
        else if (n2 >= len)
-           n2 = len - 1;
+           n2 = len - (exclusive ? 0 : 1);
+       if (exclusive)
+           --n2;
        if (n2 < 0 || n2 + 1 < n1)
            n2 = -1;
        l = list_slice(list, n1, n2);
index 66aa430334af9bee26dcb5891b26b228e2702aa7..f611a0efa74fbf07b589d359be784d1b5e8181a1 100644 (file)
@@ -41,7 +41,8 @@ void eval_addblob(typval_T *tv1, typval_T *tv2);
 int eval_addlist(typval_T *tv1, typval_T *tv2);
 int eval_leader(char_u **arg, int vim9);
 int check_can_index(typval_T *rettv, int evaluate, int verbose);
-int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, char_u *key, int keylen, int verbose);
+void f_slice(typval_T *argvars, typval_T *rettv);
+int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, int exclusive, char_u *key, int keylen, int verbose);
 char_u *partial_name(partial_T *pt);
 void partial_unref(partial_T *pt);
 int get_copyID(void);
@@ -58,7 +59,7 @@ int string2float(char_u *text, float_T *value);
 int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx);
 int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx);
 pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol);
-int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int char_col);
+int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int charcol);
 int get_env_len(char_u **arg);
 int get_id_len(char_u **arg);
 int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose);
index b77add546bc457cb4dc0f01133449fbfac5aba85..7c9ddaef133f4f72b96096fe6b5eacf737e5ccd7 100644 (file)
@@ -34,7 +34,7 @@ void f_flatten(typval_T *argvars, typval_T *rettv);
 int list_extend(list_T *l1, list_T *l2, listitem_T *bef);
 int list_concat(list_T *l1, list_T *l2, typval_T *tv);
 list_T *list_slice(list_T *ol, long n1, long n2);
-int list_slice_or_index(list_T *list, int range, long n1_arg, long n2_arg, typval_T *rettv, int verbose);
+int list_slice_or_index(list_T *list, int range, varnumber_T n1_arg, varnumber_T n2_arg, int exclusive, typval_T *rettv, int verbose);
 list_T *list_copy(list_T *orig, int deep, int copyID);
 void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
 char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
index 2337a6c45078e37217b6d562d7233f94c7e1fc84..212428219d08a7d6f49b9656a914fac3ba2155ea 100644 (file)
@@ -2,7 +2,7 @@
 void to_string_error(vartype_T vartype);
 void funcstack_check_refcount(funcstack_T *funcstack);
 char_u *char_from_string(char_u *str, varnumber_T index);
-char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last);
+char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
 int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
 int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
 void ex_disassemble(exarg_T *eap);
index 3d474f3ed8ec25a19a48f8c2a9532dbf842c17ce..f1c5de14e0ce7caff5c27eb04d1f916d43052dcf 100644 (file)
@@ -741,6 +741,29 @@ def Test_setreg()
   getreginfo('a')->assert_equal(reginfo)
 enddef 
 
+def Test_slice()
+  assert_equal('12345', slice('012345', 1))
+  assert_equal('123', slice('012345', 1, 4))
+  assert_equal('1234', slice('012345', 1, -1))
+  assert_equal('1', slice('012345', 1, -4))
+  assert_equal('', slice('012345', 1, -5))
+  assert_equal('', slice('012345', 1, -6))
+
+  assert_equal([1, 2, 3, 4, 5], slice(range(6), 1))
+  assert_equal([1, 2, 3], slice(range(6), 1, 4))
+  assert_equal([1, 2, 3, 4], slice(range(6), 1, -1))
+  assert_equal([1], slice(range(6), 1, -4))
+  assert_equal([], slice(range(6), 1, -5))
+  assert_equal([], slice(range(6), 1, -6))
+
+  assert_equal(0z1122334455, slice(0z001122334455, 1))
+  assert_equal(0z112233, slice(0z001122334455, 1, 4))
+  assert_equal(0z11223344, slice(0z001122334455, 1, -1))
+  assert_equal(0z11, slice(0z001122334455, 1, -4))
+  assert_equal(0z, slice(0z001122334455, 1, -5))
+  assert_equal(0z, slice(0z001122334455, 1, -6))
+enddef
+
 def Test_spellsuggest()
   if !has('spell')
     MissingFeature 'spell'
index 07df2f30b7307aecdddd16cc8c7d09f8b551507f..dc1b7523ca91c73c819570284225b78c7c1f3e66 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2344,
 /**/
     2343,
 /**/
index 7c4ef2a92c030f60bc4738f60f09c3f91902986e..938fc2e191e8c6fd857160c6ab910912cdb0eef2 100644 (file)
@@ -965,10 +965,11 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx)
 
 /*
  * Return the slice "str[first:last]" using character indexes.
+ * "exclusive" is TRUE for slice().
  * Return NULL when the result is empty.
  */
     char_u *
-string_slice(char_u *str, varnumber_T first, varnumber_T last)
+string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive)
 {
     long       start_byte, end_byte;
     size_t     slen;
@@ -979,12 +980,12 @@ string_slice(char_u *str, varnumber_T first, varnumber_T last)
     start_byte = char_idx2byte(str, slen, first);
     if (start_byte < 0)
        start_byte = 0; // first index very negative: use zero
-    if (last == -1)
+    if ((last == -1 && !exclusive) || last == VARNUM_MAX)
        end_byte = (long)slen;
     else
     {
        end_byte = char_idx2byte(str, slen, last);
-       if (end_byte >= 0 && end_byte < (long)slen)
+       if (!exclusive && end_byte >= 0 && end_byte < (long)slen)
            // end index is inclusive
            end_byte += MB_CPTR2LEN(str + end_byte);
     }
@@ -2992,7 +2993,7 @@ call_def_function(
                    tv = STACK_TV_BOT(-1);
                    if (is_slice)
                        // Slice: Select the characters from the string
-                       res = string_slice(tv->vval.v_string, n1, n2);
+                       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
@@ -3030,8 +3031,8 @@ call_def_function(
                    ectx.ec_stack.ga_len -= is_slice ? 2 : 1;
                    tv = STACK_TV_BOT(-1);
                    SOURCING_LNUM = iptr->isn_lnum;
-                   if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE)
-                                                                      == FAIL)
+                   if (list_slice_or_index(list, is_slice, n1, n2, FALSE,
+                                                            tv, TRUE) == FAIL)
                        goto on_error;
                }
                break;
@@ -3052,8 +3053,8 @@ call_def_function(
                        goto on_error;
                    var1 = is_slice ? STACK_TV_BOT(-2) : STACK_TV_BOT(-1);
                    var2 = is_slice ? STACK_TV_BOT(-1) : NULL;
-                   res = eval_index_inner(tv, is_slice,
-                                                  var1, var2, NULL, -1, TRUE);
+                   res = eval_index_inner(tv, is_slice, var1, var2,
+                                                       FALSE, NULL, -1, TRUE);
                    clear_tv(var1);
                    if (is_slice)
                        clear_tv(var2);