0
0
mirror of https://github.com/vim/vim.git synced 2025-10-04 05:25:06 -04:00

patch 8.2.2344: using inclusive index for slice is not always desired

Problem:    Using inclusive index for slice is not always desired.
Solution:   Add the slice() method, which has an exclusive index. (closes
            #7408)
This commit is contained in:
Bram Moolenaar
2021-01-13 21:47:15 +01:00
parent c423ad77ed
commit 6601b62943
11 changed files with 106 additions and 28 deletions

View 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;

View 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,

View 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);

View 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);

View 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);

View 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);

View 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'

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2344,
/**/
2343,
/**/

View 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);