summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-01-13 21:47:15 +0100
committerBram Moolenaar <Bram@vim.org>2021-01-13 21:47:15 +0100
commit6601b62943a19d4f8818c3638440663d67a17b6a (patch)
treeec8681cc9c635a6cb05eb30b4e1d7eb88a46dcf2
parentc423ad77ed763c11ba67729bbf63c1cf0915231f (diff)
downloadvim-git-6601b62943a19d4f8818c3638440663d67a17b6a.tar.gz
patch 8.2.2344: using inclusive index for slice is not always desiredv8.2.2344
Problem: Using inclusive index for slice is not always desired. Solution: Add the slice() method, which has an exclusive index. (closes #7408)
-rw-r--r--runtime/doc/eval.txt20
-rw-r--r--runtime/doc/usr_41.txt3
-rw-r--r--src/eval.c45
-rw-r--r--src/evalfunc.c2
-rw-r--r--src/list.c13
-rw-r--r--src/proto/eval.pro5
-rw-r--r--src/proto/list.pro2
-rw-r--r--src/proto/vim9execute.pro2
-rw-r--r--src/testdir/test_vim9_builtin.vim23
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c17
11 files changed, 106 insertions, 28 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 01a123565..4bab6ef6f 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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}.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index bac064cf7..5d4686ae0 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -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
diff --git a/src/eval.c b/src/eval.c
index 8115c7cb0..84f6c2bf2 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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)
@@ -3938,9 +3939,26 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
}
/*
+ * 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;
diff --git a/src/evalfunc.c b/src/evalfunc.c
index a891378ee..0e800c2b6 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -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,
diff --git a/src/list.c b/src/list.c
index f7842fa87..0bca0b553 100644
--- a/src/list.c
+++ b/src/list.c
@@ -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);
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 66aa43033..f611a0efa 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -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);
diff --git a/src/proto/list.pro b/src/proto/list.pro
index b77add546..7c9ddaef1 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -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);
diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro
index 2337a6c45..212428219 100644
--- a/src/proto/vim9execute.pro
+++ b/src/proto/vim9execute.pro
@@ -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);
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 3d474f3ed..f1c5de14e 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -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'
diff --git a/src/version.c b/src/version.c
index 07df2f30b..dc1b7523c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2344,
+/**/
2343,
/**/
2342,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 7c4ef2a92..938fc2e19 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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);