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:
zeertzjq 2023-04-14 16:33:37 +08:00 committed by GitHub
commit 72a327cad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 286 additions and 189 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

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

View File

@ -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"

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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++;
}

View File

@ -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"

View File

@ -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