mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
Merge pull request #23081 from zeertzjq/vim-8.2.1062
vim-patch:8.2.{1062,1063,1064,1065,1068,1069,1070,1071,1073,1074,1075,1076,1079,1080,1098,1099,1100,1125,1161,1162,1163,1203,3216}
This commit is contained in:
commit
72a327cad2
@ -173,6 +173,7 @@ Object nvim_eval(String expr, Error *err)
|
||||
|
||||
TRY_WRAP(err, {
|
||||
ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE);
|
||||
clear_evalarg(&EVALARG_EVALUATE, NULL);
|
||||
});
|
||||
|
||||
if (!ERROR_SET(err)) {
|
||||
@ -294,6 +295,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Failed to evaluate dict expression");
|
||||
}
|
||||
clear_evalarg(&EVALARG_EVALUATE, NULL);
|
||||
if (try_end(err)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -494,7 +494,7 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
|
||||
{
|
||||
// Disable error messages, a bad expression would make Vim unusable.
|
||||
emsg_off++;
|
||||
typval_T *const tv = eval_expr(bp->dbg_name);
|
||||
typval_T *const tv = eval_expr(bp->dbg_name, NULL);
|
||||
emsg_off--;
|
||||
return tv;
|
||||
}
|
||||
|
265
src/nvim/eval.c
265
src/nvim/eval.c
@ -692,6 +692,17 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
|
||||
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
|
||||
}
|
||||
|
||||
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
|
||||
{
|
||||
*evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
|
||||
if (eap != NULL) {
|
||||
if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
|
||||
evalarg->eval_getline = eap->getline;
|
||||
evalarg->eval_cookie = eap->cookie;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Top level evaluation function, returning a boolean.
|
||||
/// Sets "error" to true if there was an error.
|
||||
///
|
||||
@ -702,11 +713,14 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
|
||||
{
|
||||
typval_T tv;
|
||||
bool retval = false;
|
||||
evalarg_T evalarg;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, skip);
|
||||
|
||||
if (skip) {
|
||||
emsg_skip++;
|
||||
}
|
||||
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) {
|
||||
if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
|
||||
*error = true;
|
||||
} else {
|
||||
*error = false;
|
||||
@ -718,19 +732,23 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
|
||||
if (skip) {
|
||||
emsg_skip--;
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Call eval1() and give an error message if not done at a lower level.
|
||||
static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
|
||||
static int eval1_emsg(char **arg, typval_T *rettv, exarg_T *eap)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
const char *const start = *arg;
|
||||
const int did_emsg_before = did_emsg;
|
||||
const int called_emsg_before = called_emsg;
|
||||
evalarg_T evalarg;
|
||||
|
||||
const int ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL);
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
|
||||
|
||||
const int ret = eval1(arg, rettv, &evalarg);
|
||||
if (ret == FAIL) {
|
||||
// Report the invalid expression unless the expression evaluation has
|
||||
// been cancelled due to an aborting error, an interrupt, or an
|
||||
@ -742,6 +760,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
|
||||
semsg(_(e_invexpr2), start);
|
||||
}
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -786,7 +805,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
|
||||
return FAIL;
|
||||
}
|
||||
s = skipwhite(s);
|
||||
if (eval1_emsg(&s, rettv, true) == FAIL) {
|
||||
if (eval1_emsg(&s, rettv, NULL) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
if (*skipwhite(s) != NUL) { // check for trailing chars after expr
|
||||
@ -827,11 +846,13 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
|
||||
{
|
||||
typval_T tv;
|
||||
char *retval;
|
||||
evalarg_T evalarg;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, skip);
|
||||
if (skip) {
|
||||
emsg_skip++;
|
||||
}
|
||||
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) {
|
||||
if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) {
|
||||
retval = NULL;
|
||||
} else {
|
||||
retval = xstrdup(tv_get_string(&tv));
|
||||
@ -840,6 +861,7 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
|
||||
if (skip) {
|
||||
emsg_skip--;
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -847,12 +869,24 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
|
||||
/// Skip over an expression at "*pp".
|
||||
///
|
||||
/// @return FAIL for an error, OK otherwise.
|
||||
int skip_expr(char **pp)
|
||||
int skip_expr(char **pp, evalarg_T *const evalarg)
|
||||
{
|
||||
typval_T rettv;
|
||||
const int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
|
||||
// Don't evaluate the expression.
|
||||
if (evalarg != NULL) {
|
||||
evalarg->eval_flags &= ~EVAL_EVALUATE;
|
||||
}
|
||||
|
||||
*pp = skipwhite(*pp);
|
||||
return eval1(pp, &rettv, NULL);
|
||||
typval_T rettv;
|
||||
int res = eval1(pp, &rettv, NULL);
|
||||
|
||||
if (evalarg != NULL) {
|
||||
evalarg->eval_flags = save_flags;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Top level evaluation function, returning a string.
|
||||
@ -889,6 +923,7 @@ char *eval_to_string(char *arg, bool convert)
|
||||
}
|
||||
tv_clear(&tv);
|
||||
}
|
||||
clear_evalarg(&EVALARG_EVALUATE, NULL);
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -943,12 +978,18 @@ varnumber_T eval_to_number(char *expr)
|
||||
///
|
||||
/// @return an allocated typval_T with the result or
|
||||
/// NULL when there is an error.
|
||||
typval_T *eval_expr(char *arg)
|
||||
typval_T *eval_expr(char *arg, exarg_T *eap)
|
||||
{
|
||||
typval_T *tv = xmalloc(sizeof(*tv));
|
||||
if (eval0(arg, tv, NULL, &EVALARG_EVALUATE) == FAIL) {
|
||||
evalarg_T evalarg;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
|
||||
|
||||
if (eval0(arg, tv, eap, &evalarg) == FAIL) {
|
||||
XFREE_CLEAR(tv);
|
||||
}
|
||||
|
||||
clear_evalarg(&evalarg, eap);
|
||||
return tv;
|
||||
}
|
||||
|
||||
@ -1194,6 +1235,7 @@ int eval_foldexpr(char *arg, int *cp)
|
||||
sandbox--;
|
||||
}
|
||||
textlock--;
|
||||
clear_evalarg(&EVALARG_EVALUATE, NULL);
|
||||
|
||||
return (int)retval;
|
||||
}
|
||||
@ -1776,14 +1818,14 @@ notify:
|
||||
/// @param[out] *errp set to true for an error, false otherwise;
|
||||
///
|
||||
/// @return a pointer that holds the info. Null when there is an error.
|
||||
void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
|
||||
void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg)
|
||||
{
|
||||
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
|
||||
const char *expr;
|
||||
typval_T tv;
|
||||
list_T *l;
|
||||
const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
evalarg_T evalarg = { .eval_flags = skip ? 0 : EVAL_EVALUATE };
|
||||
*errp = true; // Default: there is an error.
|
||||
|
||||
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
|
||||
@ -1792,7 +1834,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
|
||||
}
|
||||
|
||||
expr = skipwhite(expr);
|
||||
if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) {
|
||||
if (expr[0] != 'i' || expr[1] != 'n'
|
||||
|| !(expr[2] == NUL || ascii_iswhite(expr[2]))) {
|
||||
emsg(_("E690: Missing \"in\" after :for"));
|
||||
return fi;
|
||||
}
|
||||
@ -1800,7 +1843,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
|
||||
if (skip) {
|
||||
emsg_skip++;
|
||||
}
|
||||
if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) {
|
||||
expr = skipwhite(expr + 2);
|
||||
if (eval0((char *)expr, &tv, eap, evalarg) == OK) {
|
||||
*errp = false;
|
||||
if (!skip) {
|
||||
if (tv.v_type == VAR_LIST) {
|
||||
@ -2164,9 +2208,10 @@ int pattern_match(const char *pat, const char *text, bool ic)
|
||||
/// @param basetv "expr" for "expr->name(arg)"
|
||||
///
|
||||
/// @return OK or FAIL.
|
||||
static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv,
|
||||
const int flags, typval_T *const basetv)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
||||
static int eval_func(char **const arg, evalarg_T *const evalarg, char *const name,
|
||||
const int name_len, typval_T *const rettv, const int flags,
|
||||
typval_T *const basetv)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 3, 5)
|
||||
{
|
||||
const bool evaluate = flags & EVAL_EVALUATE;
|
||||
char *s = name;
|
||||
@ -2192,7 +2237,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
|
||||
funcexe.fe_evaluate = evaluate;
|
||||
funcexe.fe_partial = partial;
|
||||
funcexe.fe_basetv = basetv;
|
||||
int ret = get_func_tv(s, len, rettv, arg, &funcexe);
|
||||
int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
|
||||
|
||||
xfree(s);
|
||||
|
||||
@ -2216,6 +2261,26 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// After using "evalarg" filled from "eap": free the memory.
|
||||
void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
|
||||
{
|
||||
if (evalarg != NULL) {
|
||||
if (evalarg->eval_tofree != NULL) {
|
||||
if (eap != NULL) {
|
||||
// We may need to keep the original command line, e.g. for
|
||||
// ":let" it has the variable names. But we may also need the
|
||||
// new one, "nextcmd" points into it. Keep both.
|
||||
xfree(eap->cmdline_tofree);
|
||||
eap->cmdline_tofree = *eap->cmdlinep;
|
||||
*eap->cmdlinep = evalarg->eval_tofree;
|
||||
} else {
|
||||
xfree(evalarg->eval_tofree);
|
||||
}
|
||||
evalarg->eval_tofree = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The "evaluate" argument: When false, the argument is only parsed but not
|
||||
/// executed. The function may return OK, but the rettv will be of type
|
||||
/// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
|
||||
@ -2236,9 +2301,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
|
||||
const int called_emsg_before = called_emsg;
|
||||
bool end_error = false;
|
||||
|
||||
if (evalarg != NULL) {
|
||||
evalarg->eval_tofree = NULL;
|
||||
}
|
||||
p = skipwhite(arg);
|
||||
ret = eval1(&p, rettv, evalarg);
|
||||
|
||||
@ -2269,21 +2331,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
|
||||
eap->nextcmd = check_nextcmd(p);
|
||||
}
|
||||
|
||||
if (evalarg != NULL) {
|
||||
if (eap != NULL) {
|
||||
if (evalarg->eval_tofree != NULL) {
|
||||
// We may need to keep the original command line, e.g. for
|
||||
// ":let" it has the variable names. But we may also need the
|
||||
// new one, "nextcmd" points into it. Keep both.
|
||||
xfree(eap->cmdline_tofree);
|
||||
eap->cmdline_tofree = *eap->cmdlinep;
|
||||
*eap->cmdlinep = evalarg->eval_tofree;
|
||||
}
|
||||
} else {
|
||||
xfree(evalarg->eval_tofree);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2303,7 +2350,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if ((*arg)[0] == '?') {
|
||||
char *p = *arg;
|
||||
if (*p == '?') {
|
||||
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
|
||||
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE;
|
||||
@ -2329,7 +2377,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
}
|
||||
|
||||
// Check for the ":".
|
||||
if ((*arg)[0] != ':') {
|
||||
p = *arg;
|
||||
if (*p != ':') {
|
||||
emsg(_("E109: Missing ':' after '?'"));
|
||||
if (evaluate && result) {
|
||||
tv_clear(rettv);
|
||||
@ -2375,7 +2424,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
// Repeat until there is no following "||".
|
||||
bool first = true;
|
||||
bool result = false;
|
||||
while ((*arg)[0] == '|' && (*arg)[1] == '|') {
|
||||
char *p = *arg;
|
||||
while (p[0] == '|' && p[1] == '|') {
|
||||
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
|
||||
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
const bool evaluate = orig_flags & EVAL_EVALUATE;
|
||||
@ -2412,6 +2462,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = result;
|
||||
}
|
||||
|
||||
p = *arg;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -2437,7 +2489,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
// Repeat until there is no following "&&".
|
||||
bool first = true;
|
||||
bool result = true;
|
||||
while ((*arg)[0] == '&' && (*arg)[1] == '&') {
|
||||
char *p = *arg;
|
||||
while (p[0] == '&' && p[1] == '&') {
|
||||
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
|
||||
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
const bool evaluate = orig_flags & EVAL_EVALUATE;
|
||||
@ -2474,6 +2527,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = result;
|
||||
}
|
||||
|
||||
p = *arg;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -2498,7 +2553,6 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
{
|
||||
typval_T var2;
|
||||
char *p;
|
||||
exprtype_T type = EXPR_UNKNOWN;
|
||||
int len = 2;
|
||||
|
||||
@ -2507,7 +2561,7 @@ static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p = *arg;
|
||||
char *p = *arg;
|
||||
switch (p[0]) {
|
||||
case '=':
|
||||
if (p[1] == '=') {
|
||||
@ -2627,8 +2681,6 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2)
|
||||
/// @return OK or FAIL.
|
||||
static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
{
|
||||
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
// Get the first variable.
|
||||
if (eval6(arg, rettv, evalarg, false) == FAIL) {
|
||||
return FAIL;
|
||||
@ -2642,6 +2694,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
break;
|
||||
}
|
||||
|
||||
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB))
|
||||
&& (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) {
|
||||
// For "list + ...", an illegal use of the first operand as
|
||||
@ -2770,12 +2823,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
|
||||
FUNC_ATTR_NO_SANITIZE_UNDEFINED
|
||||
{
|
||||
typval_T var2;
|
||||
int op;
|
||||
varnumber_T n1, n2;
|
||||
bool use_float = false;
|
||||
float_T f1 = 0, f2 = 0;
|
||||
bool error = false;
|
||||
|
||||
// Get the first variable.
|
||||
if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
|
||||
@ -2784,12 +2832,15 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
|
||||
// Repeat computing, until no '*', '/' or '%' is following.
|
||||
for (;;) {
|
||||
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
op = (uint8_t)(**arg);
|
||||
int op = (uint8_t)(**arg);
|
||||
if (op != '*' && op != '/' && op != '%') {
|
||||
break;
|
||||
}
|
||||
|
||||
varnumber_T n1, n2;
|
||||
float_T f1 = 0, f2 = 0;
|
||||
bool error = false;
|
||||
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
if (evaluate) {
|
||||
if (rettv->v_type == VAR_FLOAT) {
|
||||
f1 = rettv->vval.v_float;
|
||||
@ -2808,6 +2859,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
|
||||
// Get the second variable.
|
||||
*arg = skipwhite(*arg + 1);
|
||||
typval_T var2;
|
||||
if (eval7(arg, &var2, evalarg, false) == FAIL) {
|
||||
return FAIL;
|
||||
}
|
||||
@ -2901,7 +2953,6 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
/// @return OK or FAIL.
|
||||
static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
|
||||
{
|
||||
const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
int ret = OK;
|
||||
static int recurse = 0;
|
||||
@ -2964,14 +3015,14 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
|
||||
// List: [expr, expr]
|
||||
case '[':
|
||||
ret = get_list_tv(arg, rettv, flags);
|
||||
ret = get_list_tv(arg, rettv, evalarg);
|
||||
break;
|
||||
|
||||
// Dictionary: #{key: val, key: val}
|
||||
case '#':
|
||||
if ((*arg)[1] == '{') {
|
||||
(*arg)++;
|
||||
ret = eval_dict(arg, rettv, flags, true);
|
||||
ret = eval_dict(arg, rettv, evalarg, true);
|
||||
} else {
|
||||
ret = NOTDONE;
|
||||
}
|
||||
@ -2980,9 +3031,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
// Lambda: {arg, arg -> expr}
|
||||
// Dictionary: {'key': val, 'key': val}
|
||||
case '{':
|
||||
ret = get_lambda_tv(arg, rettv, evaluate);
|
||||
ret = get_lambda_tv(arg, rettv, evalarg);
|
||||
if (ret == NOTDONE) {
|
||||
ret = eval_dict(arg, rettv, flags, false);
|
||||
ret = eval_dict(arg, rettv, evalarg, false);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3010,6 +3061,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
// nested expression: (expression).
|
||||
case '(':
|
||||
*arg = skipwhite(*arg + 1);
|
||||
|
||||
ret = eval1(arg, rettv, evalarg); // recursive!
|
||||
if (**arg == ')') {
|
||||
(*arg)++;
|
||||
@ -3038,8 +3090,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
if (len <= 0) {
|
||||
ret = FAIL;
|
||||
} else {
|
||||
const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
|
||||
if (**arg == '(') { // recursive!
|
||||
ret = eval_func(arg, s, len, rettv, flags, NULL);
|
||||
ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
|
||||
} else if (evaluate) {
|
||||
ret = get_var_tv(s, len, rettv, NULL, true, false);
|
||||
} else {
|
||||
@ -3055,7 +3108,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
|
||||
// Handle following '[', '(' and '.' for expr[expr], expr.name,
|
||||
// expr(expr), expr->name(expr)
|
||||
if (ret == OK) {
|
||||
ret = handle_subscript((const char **)arg, rettv, flags, true);
|
||||
ret = handle_subscript((const char **)arg, rettv, evalarg, true);
|
||||
}
|
||||
|
||||
// Apply logical NOT and unary '-', from right to left, ignore '+'.
|
||||
@ -3131,10 +3184,10 @@ static int eval7_leader(typval_T *const rettv, const bool numeric_only,
|
||||
/// to the name of the Lua function to call (after the
|
||||
/// "v:lua." prefix).
|
||||
/// @return OK on success, FAIL on failure.
|
||||
static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate,
|
||||
dict_T *const selfdict, typval_T *const basetv,
|
||||
static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T *const rettv,
|
||||
const bool evaluate, dict_T *const selfdict, typval_T *const basetv,
|
||||
const char *const lua_funcname)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 3)
|
||||
{
|
||||
partial_T *pt = NULL;
|
||||
typval_T functv;
|
||||
@ -3166,7 +3219,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
|
||||
funcexe.fe_selfdict = selfdict;
|
||||
funcexe.fe_basetv = basetv;
|
||||
const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
|
||||
arg, &funcexe);
|
||||
arg, evalarg, &funcexe);
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
// evaluating the arguments is possible (see test55).
|
||||
@ -3185,16 +3238,17 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
|
||||
/// @return FAIL or OK.
|
||||
///
|
||||
/// @note "*arg" is advanced to after the ')'.
|
||||
static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate,
|
||||
static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
|
||||
const bool verbose)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
// Skip over the ->.
|
||||
*arg += 2;
|
||||
typval_T base = *rettv;
|
||||
rettv->v_type = VAR_UNKNOWN;
|
||||
|
||||
int ret = get_lambda_tv(arg, rettv, evaluate);
|
||||
int ret = get_lambda_tv(arg, rettv, evalarg);
|
||||
if (ret != OK) {
|
||||
return FAIL;
|
||||
} else if (**arg != '(') {
|
||||
@ -3208,7 +3262,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
} else {
|
||||
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL);
|
||||
ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, NULL);
|
||||
}
|
||||
|
||||
// Clear the funcref afterwards, so that deleting it while
|
||||
@ -3225,10 +3279,12 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
|
||||
/// @param *arg points to the '-'.
|
||||
///
|
||||
/// @return FAIL or OK. "*arg" is advanced to after the ')'.
|
||||
static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate,
|
||||
static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
|
||||
const bool verbose)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
|
||||
// Skip over the ->.
|
||||
*arg += 2;
|
||||
typval_T base = *rettv;
|
||||
@ -3278,9 +3334,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
|
||||
rettv->vval.v_partial = vvlua_partial;
|
||||
rettv->vval.v_partial->pt_refcount++;
|
||||
}
|
||||
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname);
|
||||
ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
|
||||
} else {
|
||||
ret = eval_func(arg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
|
||||
ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3299,9 +3355,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
|
||||
/// @param verbose give error messages
|
||||
///
|
||||
/// @returns FAIL or OK. "*arg" is advanced to after the ']'.
|
||||
static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose)
|
||||
static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose)
|
||||
{
|
||||
const bool evaluate = flags & EVAL_EVALUATE;
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
bool empty1 = false;
|
||||
bool empty2 = false;
|
||||
ptrdiff_t len = -1;
|
||||
@ -3350,15 +3406,13 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose
|
||||
}
|
||||
*arg = skipwhite(key + len);
|
||||
} else {
|
||||
evalarg_T evalarg = { .eval_flags = flags };
|
||||
|
||||
// something[idx]
|
||||
//
|
||||
// Get the (first) variable from inside the [].
|
||||
*arg = skipwhite(*arg + 1);
|
||||
if (**arg == ':') {
|
||||
empty1 = true;
|
||||
} else if (eval1(arg, &var1, &evalarg) == FAIL) { // Recursive!
|
||||
} else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive!
|
||||
return FAIL;
|
||||
} else if (evaluate && !tv_check_str(&var1)) {
|
||||
// Not a number or string.
|
||||
@ -3372,7 +3426,7 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose
|
||||
*arg = skipwhite(*arg + 1);
|
||||
if (**arg == ']') {
|
||||
empty2 = true;
|
||||
} else if (eval1(arg, &var2, &evalarg) == FAIL) { // Recursive!
|
||||
} else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive!
|
||||
if (!empty1) {
|
||||
tv_clear(&var1);
|
||||
}
|
||||
@ -3974,14 +4028,13 @@ void partial_unref(partial_T *pt)
|
||||
|
||||
/// Allocate a variable for a List and fill it from "*arg".
|
||||
///
|
||||
/// @param arg "*arg" points to the "[".
|
||||
/// @return OK or FAIL.
|
||||
static int get_list_tv(char **arg, typval_T *rettv, const int flags)
|
||||
static int get_list_tv(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
{
|
||||
const bool evaluate = flags & EVAL_EVALUATE;
|
||||
const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
|
||||
list_T *l = NULL;
|
||||
|
||||
evalarg_T evalarg = { .eval_flags = flags };
|
||||
|
||||
if (evaluate) {
|
||||
l = tv_list_alloc(kListLenShouldKnow);
|
||||
}
|
||||
@ -3989,7 +4042,7 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags)
|
||||
*arg = skipwhite(*arg + 1);
|
||||
while (**arg != ']' && **arg != NUL) {
|
||||
typval_T tv;
|
||||
if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive!
|
||||
if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
|
||||
goto failret;
|
||||
}
|
||||
if (evaluate) {
|
||||
@ -3997,14 +4050,20 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags)
|
||||
tv_list_append_owned_tv(l, tv);
|
||||
}
|
||||
|
||||
// the comma must come after the value
|
||||
bool had_comma = **arg == ',';
|
||||
if (had_comma) {
|
||||
*arg = skipwhite(*arg + 1);
|
||||
}
|
||||
|
||||
if (**arg == ']') {
|
||||
break;
|
||||
}
|
||||
if (**arg != ',') {
|
||||
|
||||
if (!had_comma) {
|
||||
semsg(_("E696: Missing comma in List: %s"), *arg);
|
||||
goto failret;
|
||||
}
|
||||
*arg = skipwhite(*arg + 1);
|
||||
}
|
||||
|
||||
if (**arg != ']') {
|
||||
@ -4622,20 +4681,18 @@ static int get_literal_key(char **arg, typval_T *tv)
|
||||
|
||||
/// Allocate a variable for a Dictionary and fill it from "*arg".
|
||||
///
|
||||
/// @param arg "*arg" points to the "{".
|
||||
/// @param literal true for #{key: val}
|
||||
/// @param flags can have EVAL_EVALUATE and other EVAL_ flags.
|
||||
///
|
||||
/// @return OK or FAIL. Returns NOTDONE for {expr}.
|
||||
static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
|
||||
static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal)
|
||||
{
|
||||
const bool evaluate = flags & EVAL_EVALUATE;
|
||||
const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
|
||||
typval_T tv;
|
||||
char *key = NULL;
|
||||
char *curly_expr = skipwhite(*arg + 1);
|
||||
char buf[NUMBUFLEN];
|
||||
|
||||
evalarg_T evalarg = { .eval_flags = flags };
|
||||
|
||||
// First check if it's not a curly-braces expression: {expr}.
|
||||
// Must do this without evaluating, otherwise a function may be called
|
||||
// twice. Unfortunately this means we need to call eval1() twice for the
|
||||
@ -4661,7 +4718,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
|
||||
while (**arg != '}' && **arg != NUL) {
|
||||
if ((literal
|
||||
? get_literal_key(arg, &tvkey)
|
||||
: eval1(arg, &tvkey, &evalarg)) == FAIL) { // recursive!
|
||||
: eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive!
|
||||
goto failret;
|
||||
}
|
||||
if (**arg != ':') {
|
||||
@ -4679,7 +4736,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
|
||||
}
|
||||
|
||||
*arg = skipwhite(*arg + 1);
|
||||
if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive!
|
||||
if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
|
||||
if (evaluate) {
|
||||
tv_clear(&tvkey);
|
||||
}
|
||||
@ -4702,14 +4759,19 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
|
||||
}
|
||||
tv_clear(&tvkey);
|
||||
|
||||
// the comma must come after the value
|
||||
bool had_comma = **arg == ',';
|
||||
if (had_comma) {
|
||||
*arg = skipwhite(*arg + 1);
|
||||
}
|
||||
|
||||
if (**arg == '}') {
|
||||
break;
|
||||
}
|
||||
if (**arg != ',') {
|
||||
if (!had_comma) {
|
||||
semsg(_("E722: Missing comma in Dictionary: %s"), *arg);
|
||||
goto failret;
|
||||
}
|
||||
*arg = skipwhite(*arg + 1);
|
||||
}
|
||||
|
||||
if (**arg != '}') {
|
||||
@ -6992,9 +7054,10 @@ int check_luafunc_name(const char *const str, const bool paren)
|
||||
/// @param verbose give error messages
|
||||
/// @param start_leader start of '!' and '-' prefixes
|
||||
/// @param end_leaderp end of '!' and '-' prefixes
|
||||
int handle_subscript(const char **const arg, typval_T *rettv, const int flags, bool verbose)
|
||||
int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg,
|
||||
bool verbose)
|
||||
{
|
||||
const bool evaluate = flags & EVAL_EVALUATE;
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
int ret = OK;
|
||||
dict_T *selfdict = NULL;
|
||||
const char *lua_funcname = NULL;
|
||||
@ -7023,7 +7086,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
|
||||
&& !ascii_iswhite(*(*arg - 1)))
|
||||
|| (**arg == '-' && (*arg)[1] == '>'))) {
|
||||
if (**arg == '(') {
|
||||
ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname);
|
||||
ret = call_func_rettv((char **)arg, evalarg, rettv, evaluate, selfdict, NULL, lua_funcname);
|
||||
|
||||
// Stop the expression evaluation when immediately aborting on
|
||||
// error, or when an interrupt occurred or an exception was thrown
|
||||
@ -7039,10 +7102,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
|
||||
} else if (**arg == '-') {
|
||||
if ((*arg)[2] == '{') {
|
||||
// expr->{lambda}()
|
||||
ret = eval_lambda((char **)arg, rettv, evaluate, verbose);
|
||||
ret = eval_lambda((char **)arg, rettv, evalarg, verbose);
|
||||
} else {
|
||||
// expr->name()
|
||||
ret = eval_method((char **)arg, rettv, evaluate, verbose);
|
||||
ret = eval_method((char **)arg, rettv, evalarg, verbose);
|
||||
}
|
||||
} else { // **arg == '[' || **arg == '.'
|
||||
tv_dict_unref(selfdict);
|
||||
@ -7054,7 +7117,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
|
||||
} else {
|
||||
selfdict = NULL;
|
||||
}
|
||||
if (eval_index((char **)arg, rettv, flags, verbose) == FAIL) {
|
||||
if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) {
|
||||
tv_clear(rettv);
|
||||
ret = FAIL;
|
||||
}
|
||||
@ -7426,8 +7489,9 @@ void ex_echo(exarg_T *eap)
|
||||
bool need_clear = true;
|
||||
const int did_emsg_before = did_emsg;
|
||||
const int called_emsg_before = called_emsg;
|
||||
evalarg_T evalarg;
|
||||
|
||||
evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE };
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
|
||||
|
||||
if (eap->skip) {
|
||||
emsg_skip++;
|
||||
@ -7479,6 +7543,7 @@ void ex_echo(exarg_T *eap)
|
||||
arg = skipwhite(arg);
|
||||
}
|
||||
eap->nextcmd = check_nextcmd(arg);
|
||||
clear_evalarg(&evalarg, eap);
|
||||
|
||||
if (eap->skip) {
|
||||
emsg_skip--;
|
||||
@ -7517,7 +7582,7 @@ void ex_execute(exarg_T *eap)
|
||||
emsg_skip++;
|
||||
}
|
||||
while (*arg != NUL && *arg != '|' && *arg != '\n') {
|
||||
ret = eval1_emsg(&arg, &rettv, !eap->skip);
|
||||
ret = eval1_emsg(&arg, &rettv, eap);
|
||||
if (ret == FAIL) {
|
||||
break;
|
||||
}
|
||||
|
@ -272,9 +272,10 @@ typedef struct {
|
||||
int eval_flags; ///< EVAL_ flag values below
|
||||
|
||||
/// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
|
||||
void *eval_cookie; // argument for getline()
|
||||
LineGetter eval_getline;
|
||||
void *eval_cookie; ///< argument for eval_getline()
|
||||
|
||||
/// pointer to the line obtained with getsourceline()
|
||||
/// pointer to the last line obtained with getsourceline()
|
||||
char *eval_tofree;
|
||||
} evalarg_T;
|
||||
|
||||
@ -284,7 +285,7 @@ enum {
|
||||
};
|
||||
|
||||
/// Passed to an eval() function to enable evaluation.
|
||||
EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL });
|
||||
EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL, NULL });
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "eval.h.generated.h"
|
||||
|
@ -252,22 +252,23 @@ static void set_ufunc_name(ufunc_T *fp, char *name)
|
||||
/// Parse a lambda expression and get a Funcref from "*arg".
|
||||
///
|
||||
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
|
||||
int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
|
||||
int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
|
||||
{
|
||||
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
|
||||
garray_T newargs = GA_EMPTY_INIT_VALUE;
|
||||
garray_T *pnewargs;
|
||||
ufunc_T *fp = NULL;
|
||||
partial_T *pt = NULL;
|
||||
int varargs;
|
||||
int ret;
|
||||
char *start = skipwhite(*arg + 1);
|
||||
char *s, *e;
|
||||
bool *old_eval_lavars = eval_lavars_used;
|
||||
bool eval_lavars = false;
|
||||
char *tofree = NULL;
|
||||
|
||||
// First, check if this is a lambda expression. "->" must exists.
|
||||
ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
|
||||
if (ret == FAIL || *start != '>') {
|
||||
char *s = skipwhite(*arg + 1);
|
||||
ret = get_function_args(&s, '-', NULL, NULL, NULL, true);
|
||||
if (ret == FAIL || *s != '>') {
|
||||
return NOTDONE;
|
||||
}
|
||||
|
||||
@ -290,12 +291,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
|
||||
|
||||
// Get the start and the end of the expression.
|
||||
*arg = skipwhite((*arg) + 1);
|
||||
s = *arg;
|
||||
ret = skip_expr(arg);
|
||||
char *start = *arg;
|
||||
ret = skip_expr(arg, evalarg);
|
||||
char *end = *arg;
|
||||
if (ret == FAIL) {
|
||||
goto errret;
|
||||
}
|
||||
e = *arg;
|
||||
if (evalarg != NULL) {
|
||||
// avoid that the expression gets freed when another line break follows
|
||||
tofree = evalarg->eval_tofree;
|
||||
evalarg->eval_tofree = NULL;
|
||||
}
|
||||
|
||||
*arg = skipwhite(*arg);
|
||||
if (**arg != '}') {
|
||||
semsg(_("E451: Expected }: %s"), *arg);
|
||||
@ -317,11 +324,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
|
||||
ga_grow(&newlines, 1);
|
||||
|
||||
// Add "return " before the expression.
|
||||
size_t len = (size_t)(7 + e - s + 1);
|
||||
size_t len = (size_t)(7 + end - start + 1);
|
||||
p = xmalloc(len);
|
||||
((char **)(newlines.ga_data))[newlines.ga_len++] = p;
|
||||
STRCPY(p, "return ");
|
||||
xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
|
||||
xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
|
||||
if (strstr(p + 7, "a:") == NULL) {
|
||||
// No a: variables are used for sure.
|
||||
flags |= FC_NOARGS;
|
||||
@ -359,12 +366,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
|
||||
}
|
||||
|
||||
eval_lavars_used = old_eval_lavars;
|
||||
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
|
||||
evalarg->eval_tofree = tofree;
|
||||
} else {
|
||||
xfree(tofree);
|
||||
}
|
||||
return OK;
|
||||
|
||||
errret:
|
||||
ga_clear_strings(&newargs);
|
||||
xfree(fp);
|
||||
xfree(pt);
|
||||
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
|
||||
evalarg->eval_tofree = tofree;
|
||||
} else {
|
||||
xfree(tofree);
|
||||
}
|
||||
eval_lavars_used = old_eval_lavars;
|
||||
return FAIL;
|
||||
}
|
||||
@ -448,15 +465,14 @@ void emsg_funcname(const char *errmsg, const char *name)
|
||||
/// @param funcexe various values
|
||||
///
|
||||
/// @return OK or FAIL.
|
||||
int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
|
||||
int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg,
|
||||
funcexe_T *funcexe)
|
||||
{
|
||||
char *argp;
|
||||
int ret = OK;
|
||||
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
|
||||
int argcount = 0; // number of arguments found
|
||||
|
||||
evalarg_T evalarg = { .eval_flags = funcexe->fe_evaluate ? EVAL_EVALUATE : 0 };
|
||||
|
||||
// Get the arguments.
|
||||
argp = *arg;
|
||||
while (argcount < MAX_FUNC_ARGS
|
||||
@ -465,7 +481,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_
|
||||
if (*argp == ')' || *argp == ',' || *argp == NUL) {
|
||||
break;
|
||||
}
|
||||
if (eval1(&argp, &argvars[argcount], &evalarg) == FAIL) {
|
||||
if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) {
|
||||
ret = FAIL;
|
||||
break;
|
||||
}
|
||||
@ -2986,6 +3002,7 @@ void ex_return(exarg_T *eap)
|
||||
if (eap->skip) {
|
||||
emsg_skip--;
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
}
|
||||
|
||||
/// ":1,25call func(arg1, arg2)" function call.
|
||||
@ -3002,16 +3019,19 @@ void ex_call(exarg_T *eap)
|
||||
bool failed = false;
|
||||
funcdict_T fudi;
|
||||
partial_T *partial = NULL;
|
||||
evalarg_T evalarg;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
|
||||
if (eap->skip) {
|
||||
// trans_function_name() doesn't work well when skipping, use eval0()
|
||||
// instead to skip to any following command, e.g. for:
|
||||
// :if 0 | call dict.foo().bar() | endif.
|
||||
emsg_skip++;
|
||||
if (eval0(eap->arg, &rettv, eap, NULL) != FAIL) {
|
||||
if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
|
||||
tv_clear(&rettv);
|
||||
}
|
||||
emsg_skip--;
|
||||
clear_evalarg(&evalarg, eap);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3069,14 +3089,13 @@ void ex_call(exarg_T *eap)
|
||||
funcexe.fe_evaluate = true;
|
||||
funcexe.fe_partial = partial;
|
||||
funcexe.fe_selfdict = fudi.fd_dict;
|
||||
if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
|
||||
if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle a function returning a Funcref, Dictionary or List.
|
||||
if (handle_subscript((const char **)&arg, &rettv, EVAL_EVALUATE, true)
|
||||
== FAIL) {
|
||||
if (handle_subscript((const char **)&arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
@ -3108,6 +3127,7 @@ void ex_call(exarg_T *eap)
|
||||
eap->nextcmd = check_nextcmd(arg);
|
||||
}
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
|
||||
end:
|
||||
tv_dict_unref(fudi.fd_dict);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
|
@ -259,14 +259,13 @@ void ex_let(exarg_T *eap)
|
||||
if (eap->skip) {
|
||||
emsg_skip++;
|
||||
}
|
||||
evalarg_T evalarg = {
|
||||
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE,
|
||||
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL,
|
||||
};
|
||||
evalarg_T evalarg;
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
|
||||
int eval_res = eval0(expr, &rettv, eap, &evalarg);
|
||||
if (eap->skip) {
|
||||
emsg_skip--;
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
|
||||
if (!eap->skip && eval_res != FAIL) {
|
||||
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
|
||||
@ -510,7 +509,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
|
||||
} else {
|
||||
// handle d.key, l[idx], f(expr)
|
||||
const char *const arg_subsc = arg;
|
||||
if (handle_subscript(&arg, &tv, EVAL_EVALUATE, true) == FAIL) {
|
||||
if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
|
||||
error = true;
|
||||
} else {
|
||||
if (arg == arg_subsc && len == 2 && name[1] == ':') {
|
||||
@ -1717,7 +1716,7 @@ bool var_exists(const char *var)
|
||||
n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
|
||||
if (n) {
|
||||
// Handle d.key, l[idx], f(expr).
|
||||
n = handle_subscript(&var, &tv, EVAL_EVALUATE, false) == OK;
|
||||
n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
|
||||
if (n) {
|
||||
tv_clear(&tv);
|
||||
}
|
||||
|
@ -484,24 +484,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
if (cstack.cs_looplevel > 0) {
|
||||
// Inside a while/for loop we need to store the lines and use them
|
||||
// again. Pass a different "fgetline" function to do_one_cmd()
|
||||
// below, so that it stores lines in or reads them from
|
||||
// "lines_ga". Makes it possible to define a function inside a
|
||||
// while/for loop.
|
||||
cmd_getline = get_loop_line;
|
||||
cmd_cookie = (void *)&cmd_loop_cookie;
|
||||
cmd_loop_cookie.lines_gap = &lines_ga;
|
||||
cmd_loop_cookie.current_line = current_line;
|
||||
cmd_loop_cookie.getline = fgetline;
|
||||
cmd_loop_cookie.cookie = cookie;
|
||||
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
|
||||
} else {
|
||||
cmd_getline = fgetline;
|
||||
cmd_cookie = cookie;
|
||||
}
|
||||
|
||||
// 2. If no line given, get an allocated line with fgetline().
|
||||
if (next_cmdline == NULL) {
|
||||
// Need to set msg_didout for the first line after an ":if",
|
||||
@ -540,15 +522,37 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
|
||||
}
|
||||
cmdline_copy = next_cmdline;
|
||||
|
||||
// Save the current line when inside a ":while" or ":for", and when
|
||||
// the command looks like a ":while" or ":for", because we may need it
|
||||
// later. When there is a '|' and another command, it is stored
|
||||
// separately, because we need to be able to jump back to it from an
|
||||
int current_line_before = 0;
|
||||
// Inside a while/for loop, and when the command looks like a ":while"
|
||||
// or ":for", the line is stored, because we may need it later when
|
||||
// looping.
|
||||
//
|
||||
// When there is a '|' and another command, it is stored separately,
|
||||
// because we need to be able to jump back to it from an
|
||||
// :endwhile/:endfor.
|
||||
if (current_line == lines_ga.ga_len
|
||||
&& (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) {
|
||||
store_loop_line(&lines_ga, next_cmdline);
|
||||
//
|
||||
// Pass a different "fgetline" function to do_one_cmd() below,
|
||||
// that it stores lines in or reads them from "lines_ga". Makes it
|
||||
// possible to define a function inside a while/for loop.
|
||||
if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) {
|
||||
cmd_getline = get_loop_line;
|
||||
cmd_cookie = (void *)&cmd_loop_cookie;
|
||||
cmd_loop_cookie.lines_gap = &lines_ga;
|
||||
cmd_loop_cookie.current_line = current_line;
|
||||
cmd_loop_cookie.getline = fgetline;
|
||||
cmd_loop_cookie.cookie = cookie;
|
||||
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
|
||||
|
||||
// Save the current line when encountering it the first time.
|
||||
if (current_line == lines_ga.ga_len) {
|
||||
store_loop_line(&lines_ga, next_cmdline);
|
||||
}
|
||||
current_line_before = current_line;
|
||||
} else {
|
||||
cmd_getline = fgetline;
|
||||
cmd_cookie = cookie;
|
||||
}
|
||||
|
||||
did_endif = false;
|
||||
|
||||
if (count++ == 0) {
|
||||
@ -651,7 +655,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
|
||||
} else if (cstack.cs_lflags & CSL_HAD_LOOP) {
|
||||
// For a ":while" or ":for" we need to remember the line number.
|
||||
cstack.cs_lflags &= ~CSL_HAD_LOOP;
|
||||
cstack.cs_line[cstack.cs_idx] = current_line - 1;
|
||||
cstack.cs_line[cstack.cs_idx] = current_line_before;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3751,7 +3755,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
|
||||
// Skip over `=expr`, wildcards in it are not expanded.
|
||||
if (p[0] == '`' && p[1] == '=') {
|
||||
p += 2;
|
||||
(void)skip_expr(&p);
|
||||
(void)skip_expr(&p, NULL);
|
||||
if (*p == '`') {
|
||||
p++;
|
||||
}
|
||||
@ -3970,7 +3974,7 @@ void separate_nextcmd(exarg_T *eap)
|
||||
} else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) {
|
||||
// Skip over `=expr` when wildcards are expanded.
|
||||
p += 2;
|
||||
(void)skip_expr(&p);
|
||||
(void)skip_expr(&p, NULL);
|
||||
if (*p == NUL) { // stop at NUL after CTRL-V
|
||||
break;
|
||||
}
|
||||
|
@ -792,13 +792,15 @@ void report_discard_pending(int pending, void *value)
|
||||
void ex_eval(exarg_T *eap)
|
||||
{
|
||||
typval_T tv;
|
||||
evalarg_T evalarg = {
|
||||
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE,
|
||||
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL,
|
||||
};
|
||||
evalarg_T evalarg;
|
||||
|
||||
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
|
||||
|
||||
if (eval0(eap->arg, &tv, eap, &evalarg) == OK) {
|
||||
tv_clear(&tv);
|
||||
}
|
||||
|
||||
clear_evalarg(&evalarg, eap);
|
||||
}
|
||||
|
||||
/// Handle ":if".
|
||||
@ -955,13 +957,12 @@ void ex_while(exarg_T *eap)
|
||||
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
|
||||
|
||||
int skip = CHECK_SKIP;
|
||||
if (eap->cmdidx == CMD_while) {
|
||||
// ":while bool-expr"
|
||||
if (eap->cmdidx == CMD_while) { // ":while bool-expr"
|
||||
result = eval_to_bool(eap->arg, &error, eap, skip);
|
||||
} else {
|
||||
} else { // ":for var in list-expr"
|
||||
evalarg_T evalarg;
|
||||
fill_evalarg_from_eap(&evalarg, eap, skip);
|
||||
void *fi;
|
||||
|
||||
// ":for var in list-expr"
|
||||
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) {
|
||||
// Jumping here from a ":continue" or ":endfor": use the
|
||||
// previously evaluated list.
|
||||
@ -969,7 +970,7 @@ void ex_while(exarg_T *eap)
|
||||
error = false;
|
||||
} else {
|
||||
// Evaluate the argument and get the info in a structure.
|
||||
fi = eval_for_line(eap->arg, &error, eap, skip);
|
||||
fi = eval_for_line(eap->arg, &error, eap, &evalarg);
|
||||
cstack->cs_forinfo[cstack->cs_idx] = fi;
|
||||
}
|
||||
|
||||
@ -984,6 +985,7 @@ void ex_while(exarg_T *eap)
|
||||
free_for_info(fi);
|
||||
cstack->cs_forinfo[cstack->cs_idx] = NULL;
|
||||
}
|
||||
clear_evalarg(&evalarg, eap);
|
||||
}
|
||||
|
||||
// If this cstack entry was just initialised and is active, set the
|
||||
|
@ -5266,7 +5266,7 @@ int option_set_callback_func(char *optval, Callback *optcb)
|
||||
|| (strncmp(optval, "function(", 9) == 0)
|
||||
|| (strncmp(optval, "funcref(", 8) == 0)) {
|
||||
// Lambda expression or a funcref
|
||||
tv = eval_expr(optval);
|
||||
tv = eval_expr(optval, NULL);
|
||||
if (tv == NULL) {
|
||||
return FAIL;
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es
|
||||
if (src[0] == '`' && src[1] == '=') {
|
||||
var = src;
|
||||
src += 2;
|
||||
(void)skip_expr(&src);
|
||||
(void)skip_expr(&src, NULL);
|
||||
if (*src == '`') {
|
||||
src++;
|
||||
}
|
||||
|
@ -6957,15 +6957,15 @@ void ex_cexpr(exarg_T *eap)
|
||||
|
||||
// Evaluate the expression. When the result is a string or a list we can
|
||||
// use it to fill the errorlist.
|
||||
typval_T tv;
|
||||
if (eval0(eap->arg, &tv, eap, &EVALARG_EVALUATE) == FAIL) {
|
||||
typval_T *tv = eval_expr(eap->arg, eap);
|
||||
if (tv == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL)
|
||||
|| tv.v_type == VAR_LIST) {
|
||||
if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
|
||||
|| tv->v_type == VAR_LIST) {
|
||||
incr_quickfix_busy();
|
||||
int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm,
|
||||
int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
|
||||
(eap->cmdidx != CMD_caddexpr
|
||||
&& eap->cmdidx != CMD_laddexpr),
|
||||
(linenr_T)0, (linenr_T)0,
|
||||
@ -6996,7 +6996,7 @@ void ex_cexpr(exarg_T *eap)
|
||||
emsg(_("E777: String or List expected"));
|
||||
}
|
||||
cleanup:
|
||||
tv_clear(&tv);
|
||||
tv_free(tv);
|
||||
}
|
||||
|
||||
// Get the location list for ":lhelpgrep"
|
||||
|
@ -183,22 +183,25 @@ func Test_argument()
|
||||
|
||||
let save_columns = &columns
|
||||
let &columns = 79
|
||||
exe 'args ' .. join(range(1, 81))
|
||||
call assert_equal(join([
|
||||
\ '',
|
||||
\ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ',
|
||||
\ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ',
|
||||
\ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ',
|
||||
\ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ',
|
||||
\ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ',
|
||||
\ ], "\n"),
|
||||
\ execute('args'))
|
||||
try
|
||||
exe 'args ' .. join(range(1, 81))
|
||||
call assert_equal(join([
|
||||
\ '',
|
||||
\ '[1] 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 ',
|
||||
\ '2 7 12 17 22 27 32 37 42 47 52 57 62 67 72 77 ',
|
||||
\ '3 8 13 18 23 28 33 38 43 48 53 58 63 68 73 78 ',
|
||||
\ '4 9 14 19 24 29 34 39 44 49 54 59 64 69 74 79 ',
|
||||
\ '5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 ',
|
||||
\ ], "\n"),
|
||||
\ execute('args'))
|
||||
|
||||
" No trailing newline with one item per row.
|
||||
let long_arg = repeat('X', 81)
|
||||
exe 'args ' .. long_arg
|
||||
call assert_equal("\n[".long_arg.']', execute('args'))
|
||||
let &columns = save_columns
|
||||
" No trailing newline with one item per row.
|
||||
let long_arg = repeat('X', 81)
|
||||
exe 'args ' .. long_arg
|
||||
call assert_equal("\n[".long_arg.']', execute('args'))
|
||||
finally
|
||||
let &columns = save_columns
|
||||
endtry
|
||||
|
||||
" Setting argument list should fail when the current buffer has unsaved
|
||||
" changes
|
||||
|
Loading…
Reference in New Issue
Block a user