mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #23468 from zeertzjq/vim-8.2.1466
vim-patch:8.2.{1466,3336}
This commit is contained in:
commit
38f9ee6366
197
src/nvim/eval.c
197
src/nvim/eval.c
@ -88,7 +88,10 @@
|
|||||||
|
|
||||||
static const char *e_missbrac = N_("E111: Missing ']'");
|
static const char *e_missbrac = N_("E111: Missing ']'");
|
||||||
static const char *e_list_end = N_("E697: Missing end of List ']': %s");
|
static const char *e_list_end = N_("E697: Missing end of List ']': %s");
|
||||||
static const char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
|
static const char *e_cannot_slice_dictionary
|
||||||
|
= N_("E719: cannot slice a Dictionary");
|
||||||
|
static const char e_cannot_index_special_variable[]
|
||||||
|
= N_("E909: Cannot index a special variable");
|
||||||
static const char *e_nowhitespace
|
static const char *e_nowhitespace
|
||||||
= N_("E274: No white space allowed before parenthesis");
|
= N_("E274: No white space allowed before parenthesis");
|
||||||
static const char *e_write2 = N_("E80: Error while writing: %s");
|
static const char *e_write2 = N_("E80: Error while writing: %s");
|
||||||
@ -1438,7 +1441,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
|
|||||||
if (*p == ':') {
|
if (*p == ':') {
|
||||||
if (lp->ll_tv->v_type == VAR_DICT) {
|
if (lp->ll_tv->v_type == VAR_DICT) {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
emsg(_(e_dictrange));
|
emsg(_(e_cannot_slice_dictionary));
|
||||||
}
|
}
|
||||||
tv_clear(&var1);
|
tv_clear(&var1);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -3469,39 +3472,12 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||||
bool empty1 = false;
|
bool empty1 = false;
|
||||||
bool empty2 = false;
|
bool empty2 = false;
|
||||||
ptrdiff_t len = -1;
|
|
||||||
bool range = false;
|
bool range = false;
|
||||||
const char *key = NULL;
|
const char *key = NULL;
|
||||||
|
ptrdiff_t keylen = -1;
|
||||||
|
|
||||||
switch (rettv->v_type) {
|
if (check_can_index(rettv, evaluate, verbose) == FAIL) {
|
||||||
case VAR_FUNC:
|
|
||||||
case VAR_PARTIAL:
|
|
||||||
if (verbose) {
|
|
||||||
emsg(_("E695: Cannot index a Funcref"));
|
|
||||||
}
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
case VAR_FLOAT:
|
|
||||||
if (verbose) {
|
|
||||||
emsg(_(e_float_as_string));
|
|
||||||
}
|
|
||||||
return FAIL;
|
|
||||||
case VAR_BOOL:
|
|
||||||
case VAR_SPECIAL:
|
|
||||||
if (verbose) {
|
|
||||||
emsg(_("E909: Cannot index a special variable"));
|
|
||||||
}
|
|
||||||
return FAIL;
|
|
||||||
case VAR_UNKNOWN:
|
|
||||||
if (evaluate) {
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
FALLTHROUGH;
|
|
||||||
case VAR_STRING:
|
|
||||||
case VAR_NUMBER:
|
|
||||||
case VAR_LIST:
|
|
||||||
case VAR_DICT:
|
|
||||||
case VAR_BLOB:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typval_T var1 = TV_INITIAL_VALUE;
|
typval_T var1 = TV_INITIAL_VALUE;
|
||||||
@ -3509,11 +3485,11 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
if (**arg == '.') {
|
if (**arg == '.') {
|
||||||
// dict.name
|
// dict.name
|
||||||
key = *arg + 1;
|
key = *arg + 1;
|
||||||
for (len = 0; eval_isdictc(key[len]); len++) {}
|
for (keylen = 0; eval_isdictc(key[keylen]); keylen++) {}
|
||||||
if (len == 0) {
|
if (keylen == 0) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
*arg = skipwhite(key + len);
|
*arg = skipwhite(key + keylen);
|
||||||
} else {
|
} else {
|
||||||
// something[idx]
|
// something[idx]
|
||||||
//
|
//
|
||||||
@ -3565,40 +3541,110 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
int n2 = 0;
|
int res = eval_index_inner(rettv, range,
|
||||||
int n1 = 0;
|
empty1 ? NULL : &var1, empty2 ? NULL : &var2,
|
||||||
if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) {
|
key, keylen, verbose);
|
||||||
n1 = (int)tv_get_number(&var1);
|
if (!empty1) {
|
||||||
tv_clear(&var1);
|
tv_clear(&var1);
|
||||||
}
|
}
|
||||||
if (range) {
|
if (range) {
|
||||||
if (empty2) {
|
tv_clear(&var2);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if "rettv" can have an [index] or [sli:ce]
|
||||||
|
static int check_can_index(typval_T *rettv, bool evaluate, bool verbose)
|
||||||
|
{
|
||||||
|
switch (rettv->v_type) {
|
||||||
|
case VAR_FUNC:
|
||||||
|
case VAR_PARTIAL:
|
||||||
|
if (verbose) {
|
||||||
|
emsg(_("E695: Cannot index a Funcref"));
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
case VAR_FLOAT:
|
||||||
|
if (verbose) {
|
||||||
|
emsg(_(e_float_as_string));
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
case VAR_BOOL:
|
||||||
|
case VAR_SPECIAL:
|
||||||
|
if (verbose) {
|
||||||
|
emsg(_(e_cannot_index_special_variable));
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
case VAR_UNKNOWN:
|
||||||
|
if (evaluate) {
|
||||||
|
emsg(_(e_cannot_index_special_variable));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
FALLTHROUGH;
|
||||||
|
case VAR_STRING:
|
||||||
|
case VAR_NUMBER:
|
||||||
|
case VAR_LIST:
|
||||||
|
case VAR_DICT:
|
||||||
|
case VAR_BLOB:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply index or range to "rettv".
|
||||||
|
/// "var1" is the first index, NULL for [:expr].
|
||||||
|
/// "var2" is the second index, NULL for [expr] and [expr: ]
|
||||||
|
/// Alternatively, "key" is not NULL, then key[keylen] is the dict index.
|
||||||
|
static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typval_T *var2,
|
||||||
|
const char *key, ptrdiff_t keylen, bool verbose)
|
||||||
|
{
|
||||||
|
int n1 = 0;
|
||||||
|
int n2 = 0;
|
||||||
|
if (var1 != NULL && rettv->v_type != VAR_DICT) {
|
||||||
|
n1 = (int)tv_get_number(var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_range) {
|
||||||
|
if (rettv->v_type == VAR_DICT) {
|
||||||
|
if (verbose) {
|
||||||
|
emsg(_(e_cannot_slice_dictionary));
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (var2 == NULL) {
|
||||||
n2 = -1;
|
n2 = -1;
|
||||||
} else {
|
} else {
|
||||||
n2 = (int)tv_get_number(&var2);
|
n2 = (int)tv_get_number(var2);
|
||||||
tv_clear(&var2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rettv->v_type) {
|
switch (rettv->v_type) {
|
||||||
|
case VAR_BOOL:
|
||||||
|
case VAR_SPECIAL:
|
||||||
|
case VAR_FUNC:
|
||||||
|
case VAR_FLOAT:
|
||||||
|
case VAR_PARTIAL:
|
||||||
|
case VAR_UNKNOWN:
|
||||||
|
break; // Not evaluating, skipping over subscript
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_STRING: {
|
case VAR_STRING: {
|
||||||
const char *const s = tv_get_string(rettv);
|
const char *const s = tv_get_string(rettv);
|
||||||
char *v;
|
char *v;
|
||||||
len = (ptrdiff_t)strlen(s);
|
int len = (int)strlen(s);
|
||||||
if (range) {
|
if (is_range) {
|
||||||
// The resulting variable is a substring. If the indexes
|
// The resulting variable is a substring. If the indexes
|
||||||
// are out of range the result is empty.
|
// are out of range the result is empty.
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
n1 = (int)len + n1;
|
n1 = len + n1;
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
n1 = 0;
|
n1 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (n2 < 0) {
|
if (n2 < 0) {
|
||||||
n2 = (int)len + n2;
|
n2 = len + n2;
|
||||||
} else if (n2 >= len) {
|
} else if (n2 >= len) {
|
||||||
n2 = (int)len;
|
n2 = len;
|
||||||
}
|
}
|
||||||
if (n1 >= len || n2 < 0 || n1 > n2) {
|
if (n1 >= len || n2 < 0 || n1 > n2) {
|
||||||
v = NULL;
|
v = NULL;
|
||||||
@ -3620,21 +3666,21 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
rettv->vval.v_string = v;
|
rettv->vval.v_string = v;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAR_BLOB:
|
case VAR_BLOB: {
|
||||||
len = tv_blob_len(rettv->vval.v_blob);
|
int len = tv_blob_len(rettv->vval.v_blob);
|
||||||
if (range) {
|
if (is_range) {
|
||||||
// The resulting variable is a sub-blob. If the indexes
|
// The resulting variable is a sub-blob. If the indexes
|
||||||
// are out of range the result is empty.
|
// are out of range the result is empty.
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
n1 = (int)len + n1;
|
n1 = len + n1;
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
n1 = 0;
|
n1 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (n2 < 0) {
|
if (n2 < 0) {
|
||||||
n2 = (int)len + n2;
|
n2 = len + n2;
|
||||||
} else if (n2 >= len) {
|
} else if (n2 >= len) {
|
||||||
n2 = (int)len - 1;
|
n2 = len - 1;
|
||||||
}
|
}
|
||||||
if (n1 >= len || n2 < 0 || n1 > n2) {
|
if (n1 >= len || n2 < 0 || n1 > n2) {
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
@ -3644,8 +3690,8 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
blob_T *const blob = tv_blob_alloc();
|
blob_T *const blob = tv_blob_alloc();
|
||||||
ga_grow(&blob->bv_ga, n2 - n1 + 1);
|
ga_grow(&blob->bv_ga, n2 - n1 + 1);
|
||||||
blob->bv_ga.ga_len = n2 - n1 + 1;
|
blob->bv_ga.ga_len = n2 - n1 + 1;
|
||||||
for (long i = n1; i <= n2; i++) {
|
for (int i = n1; i <= n2; i++) {
|
||||||
tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i));
|
tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i));
|
||||||
}
|
}
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
tv_blob_set_ret(rettv, blob);
|
tv_blob_set_ret(rettv, blob);
|
||||||
@ -3654,7 +3700,7 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
// The resulting variable is a byte value.
|
// The resulting variable is a byte value.
|
||||||
// If the index is too big or negative that is an error.
|
// If the index is too big or negative that is an error.
|
||||||
if (n1 < 0) {
|
if (n1 < 0) {
|
||||||
n1 = (int)len + n1;
|
n1 = len + n1;
|
||||||
}
|
}
|
||||||
if (n1 < len && n1 >= 0) {
|
if (n1 < len && n1 >= 0) {
|
||||||
const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
|
const int v = (int)tv_blob_get(rettv->vval.v_blob, n1);
|
||||||
@ -3666,64 +3712,43 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
if (empty1) {
|
if (var1 == NULL) {
|
||||||
n1 = 0;
|
n1 = 0;
|
||||||
}
|
}
|
||||||
if (empty2) {
|
if (var2 == NULL) {
|
||||||
n2 = -1;
|
n2 = -1;
|
||||||
}
|
}
|
||||||
if (tv_list_slice_or_index(rettv->vval.v_list,
|
if (tv_list_slice_or_index(rettv->vval.v_list,
|
||||||
range, n1, n2, rettv, verbose) == FAIL) {
|
is_range, n1, n2, rettv, verbose) == FAIL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VAR_DICT: {
|
case VAR_DICT: {
|
||||||
if (range) {
|
|
||||||
if (verbose) {
|
|
||||||
emsg(_(e_dictrange));
|
|
||||||
}
|
|
||||||
if (len == -1) {
|
|
||||||
tv_clear(&var1);
|
|
||||||
}
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == -1) {
|
|
||||||
key = tv_get_string_chk(&var1);
|
|
||||||
if (key == NULL) {
|
if (key == NULL) {
|
||||||
tv_clear(&var1);
|
key = tv_get_string_chk(var1);
|
||||||
|
if (key == NULL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, len);
|
dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen);
|
||||||
|
|
||||||
if (item == NULL && verbose) {
|
if (item == NULL && verbose) {
|
||||||
semsg(_(e_dictkey), key);
|
semsg(_(e_dictkey), key);
|
||||||
}
|
}
|
||||||
if (len == -1) {
|
|
||||||
tv_clear(&var1);
|
|
||||||
}
|
|
||||||
if (item == NULL || tv_is_luafunc(&item->di_tv)) {
|
if (item == NULL || tv_is_luafunc(&item->di_tv)) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tv_copy(&item->di_tv, &var1);
|
typval_T tmp;
|
||||||
|
tv_copy(&item->di_tv, &tmp);
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
*rettv = var1;
|
*rettv = tmp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAR_BOOL:
|
|
||||||
case VAR_SPECIAL:
|
|
||||||
case VAR_FUNC:
|
|
||||||
case VAR_FLOAT:
|
|
||||||
case VAR_PARTIAL:
|
|
||||||
case VAR_UNKNOWN:
|
|
||||||
break; // Not evaluating, skipping over subscript
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,8 +787,8 @@ int tv_list_slice_or_index(list_T *list, bool range, int n1_arg, int n2_arg, typ
|
|||||||
n1 = len + n1;
|
n1 = len + n1;
|
||||||
}
|
}
|
||||||
if (n1 < 0 || n1 >= len) {
|
if (n1 < 0 || n1 >= len) {
|
||||||
// For a range we allow invalid values and return an empty
|
// For a range we allow invalid values and return an empty list.
|
||||||
// list. A list index out of range is an error.
|
// A list index out of range is an error.
|
||||||
if (!range) {
|
if (!range) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
semsg(_(e_listidx), (int64_t)n1);
|
semsg(_(e_listidx), (int64_t)n1);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
" Tests for the List and Dict types
|
" Tests for the List and Dict types
|
||||||
|
|
||||||
|
source vim9.vim
|
||||||
|
|
||||||
func TearDown()
|
func TearDown()
|
||||||
" Run garbage collection after every test
|
" Run garbage collection after every test
|
||||||
call test_garbagecollect_now()
|
call test_garbagecollect_now()
|
||||||
@ -37,6 +39,23 @@ func Test_list_slice()
|
|||||||
let l[:1] += [1, 2]
|
let l[:1] += [1, 2]
|
||||||
let l[2:] -= [1]
|
let l[2:] -= [1]
|
||||||
call assert_equal([2, 4, 2], l)
|
call assert_equal([2, 4, 2], l)
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
VAR l = [1, 2]
|
||||||
|
call assert_equal([1, 2], l[:])
|
||||||
|
call assert_equal([2], l[-1 : -1])
|
||||||
|
call assert_equal([1, 2], l[-2 : -1])
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
let l = [1, 2]
|
||||||
|
call assert_equal([], l[-3 : -1])
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
var l = [1, 2]
|
||||||
|
assert_equal([1, 2], l[-3 : -1])
|
||||||
|
END
|
||||||
|
call CheckDefAndScriptSuccess(lines)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" List identity
|
" List identity
|
||||||
|
Loading…
Reference in New Issue
Block a user