aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/api/vimscript.c6
-rw-r--r--src/nvim/api/win_config.c12
-rw-r--r--src/nvim/autocmd.c6
-rw-r--r--src/nvim/autocmd.h1
-rw-r--r--src/nvim/cmdexpand.c18
-rw-r--r--src/nvim/debugger.c2
-rw-r--r--src/nvim/digraph.c2
-rw-r--r--src/nvim/drawline.c27
-rw-r--r--src/nvim/eval.c573
-rw-r--r--src/nvim/eval.h21
-rw-r--r--src/nvim/eval/funcs.c4
-rw-r--r--src/nvim/eval/typval.c31
-rw-r--r--src/nvim/eval/userfunc.c72
-rw-r--r--src/nvim/eval/userfunc.h1
-rw-r--r--src/nvim/eval/vars.c102
-rw-r--r--src/nvim/ex_cmds.lua8
-rw-r--r--src/nvim/ex_cmds_defs.h1
-rw-r--r--src/nvim/ex_docmd.c81
-rw-r--r--src/nvim/ex_eval.c30
-rw-r--r--src/nvim/ex_getln.c3
-rw-r--r--src/nvim/fold.c3
-rw-r--r--src/nvim/highlight_group.c4
-rw-r--r--src/nvim/mapping.c2
-rw-r--r--src/nvim/mark.c2
-rw-r--r--src/nvim/mark.h2
-rw-r--r--src/nvim/normal.c18
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/os/env.c2
-rw-r--r--src/nvim/path.c4
-rw-r--r--src/nvim/po/fi.po2
-rw-r--r--src/nvim/quickfix.c12
-rw-r--r--src/nvim/regexp.c2
-rw-r--r--src/nvim/shada.c2
-rw-r--r--src/nvim/statusline.c4
-rw-r--r--src/nvim/tag.c9
-rw-r--r--src/nvim/usercmd.c1
-rw-r--r--src/nvim/vim.h1
38 files changed, 669 insertions, 406 deletions
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 2438a5cf1d..208aa165c9 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -172,7 +172,8 @@ Object nvim_eval(String expr, Error *err)
int ok;
TRY_WRAP(err, {
- ok = eval0(expr.data, &rettv, NULL, true);
+ ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE);
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
});
if (!ERROR_SET(err)) {
@@ -290,10 +291,11 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
switch (dict.type) {
case kObjectTypeString:
try_start();
- if (eval0(dict.data.string.data, &rettv, NULL, true) == FAIL) {
+ if (eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE) == FAIL) {
api_set_error(err, kErrorTypeException,
"Failed to evaluate dict expression");
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
if (try_end(err)) {
return rv;
}
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 23bd5d2f48..c267fee39a 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -108,8 +108,8 @@
/// The default value for floats are 50. In general, values below 100 are
/// recommended, unless there is a good reason to overshadow builtin
/// elements.
-/// - style: Configure the appearance of the window. Currently only takes
-/// one non-empty value:
+/// - style: (optional) Configure the appearance of the window. Currently
+/// only supports one value:
/// - "minimal" Nvim will display the window with many UI options
/// disabled. This is useful when displaying a temporary
/// float where the text should not be edited. Disables
@@ -222,9 +222,11 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
- if (fconfig.style == kWinStyleMinimal) {
- win_set_minimal_style(win);
- didset_window_options(win, true);
+ if (HAS_KEY(config->style)) {
+ if (fconfig.style == kWinStyleMinimal) {
+ win_set_minimal_style(win);
+ didset_window_options(win, true);
+ }
}
}
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index f1ce919942..726344a42b 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1421,6 +1421,8 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf)
aco->save_curwin_handle = curwin->handle;
aco->save_curbuf = curbuf;
aco->save_prevwin_handle = prevwin == NULL ? 0 : prevwin->handle;
+ aco->save_State = State;
+
if (win != NULL) {
// There is a window for "buf" in the current tab page, make it the
// curwin. This is preferred, it has the least side effects (esp. if
@@ -1497,6 +1499,10 @@ void aucmd_restbuf(aco_save_T *aco)
win_found:
// May need to stop Insert mode if we were in a prompt buffer.
leaving_window(curwin);
+ // Do not stop Insert mode when already in Insert mode before.
+ if (aco->save_State & MODE_INSERT) {
+ stop_insert_mode = false;
+ }
// Remove the window.
win_remove(curwin, NULL);
pmap_del(handle_T)(&window_handles, curwin->handle);
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index 791b589167..6dbd18ba7c 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -32,6 +32,7 @@ typedef struct {
bufref_T new_curbuf; ///< new curbuf
char *globaldir; ///< saved value of globaldir
bool save_VIsual_active; ///< saved VIsual_active
+ int save_State; ///< saved State
} aco_save_T;
typedef struct AutoCmd_S AutoCmd;
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 774e16f73d..f2776191b7 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -2104,10 +2104,6 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
xp->xp_context = EXPAND_CHECKHEALTH;
xp->xp_pattern = (char *)arg;
break;
- case CMD_behave:
- xp->xp_context = EXPAND_BEHAVE;
- xp->xp_pattern = (char *)arg;
- break;
case CMD_messages:
xp->xp_context = EXPAND_MESSAGES;
@@ -2479,19 +2475,6 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":behave {mswin,xterm}" command.
-static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx == 0) {
- return "mswin";
- }
- if (idx == 1) {
- return "xterm";
- }
- return NULL;
-}
-
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":breakadd {expr, file, func, here}" command.
/// ":breakdel {func, file, here}" command.
static char *get_breakadd_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
@@ -2585,7 +2568,6 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches
int escaped;
} tab[] = {
{ EXPAND_COMMANDS, get_command_name, false, true },
- { EXPAND_BEHAVE, get_behave_arg, true, true },
{ EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
{ EXPAND_MESSAGES, get_messages_arg, true, true },
{ EXPAND_HISTORY, get_history_arg, true, true },
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 3c9a63f5a3..90723372a9 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -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;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 7d6349e552..de3808e4f5 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -2203,7 +2203,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
curwin = wp;
STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- s = p = eval_to_string(buf, NULL, false);
+ s = p = eval_to_string(buf, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index a05fb4e08e..2268a7cd3e 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -656,7 +656,15 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T
wp->w_statuscol_line_count = wp->w_nrwidth_line_count;
set_vim_var_nr(VV_VIRTNUM, 0);
build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, stcp);
- stcp->width += stcp->truncate;
+ if (stcp->truncate > 0) {
+ // Add truncated width to avoid unnecessary redraws
+ int addwidth = MIN(stcp->truncate, MAX_NUMBERWIDTH - wp->w_nrwidth);
+ stcp->truncate = 0;
+ stcp->width += addwidth;
+ wp->w_nrwidth += addwidth;
+ wp->w_nrwidth_width = wp->w_nrwidth;
+ wp->w_valid &= ~VALID_WCOL;
+ }
}
set_vim_var_nr(VV_VIRTNUM, virtnum);
@@ -2668,7 +2676,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
col_attr = cuc_attr;
} else if (draw_color_col && VCOL_HLC == *color_cols) {
- col_attr = mc_attr;
+ col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr);
}
col_attr = hl_combine_attr(col_attr, wlv.line_attr);
@@ -2967,16 +2975,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
wlv.need_showbreak = true;
}
if (statuscol.draw) {
- if (wlv.row == startrow + wlv.filler_lines + 1
- || wlv.row == startrow + wlv.filler_lines) {
- // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
- statuscol.textp = NULL;
- } else if (statuscol.textp) {
+ if (wlv.row == startrow + wlv.filler_lines) {
+ statuscol.textp = NULL; // re-evaluate for first non-filler line
+ } else if (vim_strchr(p_cpo, CPO_NUMCOL) && wlv.row > startrow + wlv.filler_lines) {
+ statuscol.draw = false; // don't draw status column if "n" is in 'cpo'
+ } else if (wlv.row == startrow + wlv.filler_lines + 1) {
+ statuscol.textp = NULL; // re-evaluate for first wrapped line
+ } else {
// Draw the already built 'statuscolumn' on the next wrapped or filler line
statuscol.textp = statuscol.text;
statuscol.hlrecp = statuscol.hlrec;
- } // Fall back to default columns if the 'n' flag isn't in 'cpo'
- statuscol.draw = vim_strchr(p_cpo, CPO_NUMCOL) == NULL;
+ }
}
wlv.filler_todo--;
virt_line_offset = -1;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ff33fdeccb..6a8b24ceed 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -692,21 +692,35 @@ 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.
///
/// @param skip only parse, don't execute
///
/// @return true or false.
-int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
+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, nextcmd, !skip) == 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, char **nextcmd, 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;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
- const int ret = eval1(arg, rettv, evaluate);
+ 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
@@ -817,22 +836,23 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error)
/// Top level evaluation function, returning a string
///
/// @param[in] arg String to evaluate.
-/// @param nextcmd Pointer to the start of the next Ex command.
/// @param[in] skip If true, only do parsing to nextcmd without reporting
/// errors or actually evaluating anything.
///
/// @return [allocated] string result of evaluation or NULL in case of error or
/// when skipping.
-char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip)
+char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{
typval_T tv;
char *retval;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) {
emsg_skip++;
}
- if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) {
+ if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) {
retval = NULL;
} else {
retval = xstrdup(tv_get_string(&tv));
@@ -841,6 +861,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip
if (skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
return retval;
}
@@ -848,12 +869,24 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, 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, false);
+ 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.
@@ -862,13 +895,13 @@ int skip_expr(char **pp)
/// a Float to a String.
///
/// @return pointer to allocated memory, or NULL for failure.
-char *eval_to_string(char *arg, char **nextcmd, bool convert)
+char *eval_to_string(char *arg, bool convert)
{
typval_T tv;
char *retval;
garray_T ga;
- if (eval0(arg, &tv, nextcmd, true) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = NULL;
} else {
if (convert && tv.v_type == VAR_LIST) {
@@ -890,6 +923,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
}
tv_clear(&tv);
}
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return retval;
}
@@ -898,7 +932,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// textlock.
///
/// @param use_sandbox when true, use the sandbox.
-char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
+char *eval_to_string_safe(char *arg, int use_sandbox)
{
char *retval;
funccal_entry_T funccal_entry;
@@ -908,7 +942,7 @@ char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
sandbox++;
}
textlock++;
- retval = eval_to_string(arg, nextcmd, false);
+ retval = eval_to_string(arg, false);
if (use_sandbox) {
sandbox--;
}
@@ -929,7 +963,7 @@ varnumber_T eval_to_number(char *expr)
emsg_off++;
- if (eval1(&p, &rettv, true) == FAIL) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) {
retval = -1;
} else {
retval = tv_get_number_chk(&rettv, NULL);
@@ -944,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, true) == 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;
}
@@ -1024,7 +1064,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
emsg_off++;
}
- if (eval1(&p, &rettv, true) == OK) {
+ if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
@@ -1171,7 +1211,7 @@ int eval_foldexpr(char *arg, int *cp)
}
textlock++;
*cp = NUL;
- if (eval0(arg, &tv, NULL, true) == FAIL) {
+ if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retval = 0;
} else {
// If the result is a number, just return the number.
@@ -1195,6 +1235,7 @@ int eval_foldexpr(char *arg, int *cp)
sandbox--;
}
textlock--;
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
return (int)retval;
}
@@ -1346,7 +1387,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
empty1 = true;
} else {
empty1 = false;
- if (eval1(&p, &var1, true) == FAIL) { // Recursive!
+ if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive!
return NULL;
}
if (!tv_check_str(&var1)) {
@@ -1380,7 +1421,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_empty2 = true;
} else {
lp->ll_empty2 = false;
- if (eval1(&p, &var2, true) == FAIL) { // Recursive!
+ // Recursive!
+ if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) {
tv_clear(&var1);
return NULL;
}
@@ -1632,8 +1674,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
// handle +=, -=, *=, /=, %= and .=
di = NULL;
- if (get_var_tv(lp->ll_name, (int)strlen(lp->ll_name),
- &tv, &di, true, false) == OK) {
+ if (eval_variable(lp->ll_name, (int)strlen(lp->ll_name),
+ &tv, &di, true, false) == OK) {
if ((di == NULL
|| (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING)
&& !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING)))
@@ -1776,12 +1818,13 @@ 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, char **nextcmdp, 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);
*errp = true; // Default: there is an error.
@@ -1791,7 +1834,8 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, 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;
}
@@ -1799,7 +1843,8 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
if (skip) {
emsg_skip++;
}
- if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
+ expr = skipwhite(expr + 2);
+ if (eval0((char *)expr, &tv, eap, evalarg) == OK) {
*errp = false;
if (!skip) {
if (tv.v_type == VAR_LIST) {
@@ -2163,10 +2208,12 @@ 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 bool evaluate, 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;
int len = name_len;
@@ -2190,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);
@@ -2214,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.
@@ -2223,8 +2290,10 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
/// Put the result in "rettv" when returning OK and "evaluate" is true.
/// Note: "rettv.v_lock" is not set.
///
+/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer.
+///
/// @return OK or FAIL.
-int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
+int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
{
int ret;
char *p;
@@ -2233,7 +2302,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
bool end_error = false;
p = skipwhite(arg);
- ret = eval1(&p, rettv, evaluate);
+ ret = eval1(&p, rettv, evalarg);
if (ret != FAIL) {
end_error = !ends_excmd(*p);
@@ -2246,7 +2315,8 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
// been cancelled due to an aborting error, an interrupt, or an
// exception, or we already gave a more specific error.
// Also check called_emsg for when using assert_fails().
- if (!aborting() && did_emsg == did_emsg_before
+ if (!aborting()
+ && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before) {
if (end_error) {
semsg(_(e_trailing_arg), p);
@@ -2256,8 +2326,9 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
}
ret = FAIL;
}
- if (nextcmd != NULL) {
- *nextcmd = check_nextcmd(p);
+
+ if (eap != NULL) {
+ eap->nextcmd = check_nextcmd(p);
}
return ret;
@@ -2265,6 +2336,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Handle top level expression:
/// expr2 ? expr1 : expr1
+/// expr2 ?? expr1
///
/// "arg" must point to the first non-white of the expression.
/// "arg" is advanced to the next non-white after the recognized expression.
@@ -2272,54 +2344,89 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// Note: "rettv.v_lock" is not set.
///
/// @return OK or FAIL.
-int eval1(char **arg, typval_T *rettv, int evaluate)
+int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
-
// Get the first variable.
- if (eval2(arg, rettv, evaluate) == FAIL) {
+ if (eval2(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- if ((*arg)[0] == '?') {
+ char *p = *arg;
+ if (*p == '?') {
+ const bool op_falsy = p[1] == '?';
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
bool result = false;
if (evaluate) {
bool error = false;
- if (tv_get_number_chk(rettv, &error) != 0) {
+ if (op_falsy) {
+ result = tv2bool(rettv);
+ } else if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
- tv_clear(rettv);
+ if (error || !op_falsy || !result) {
+ tv_clear(rettv);
+ }
if (error) {
return FAIL;
}
}
- // Get the second variable.
+ // Get the second variable. Recursive!
+ if (op_falsy) {
+ (*arg)++;
+ }
*arg = skipwhite(*arg + 1);
- if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive!
+ evalarg_used->eval_flags = (op_falsy ? !result : result)
+ ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ evalarg_used->eval_flags = orig_flags;
return FAIL;
}
+ if (!op_falsy || !result) {
+ *rettv = var2;
+ }
- // Check for the ":".
- if ((*arg)[0] != ':') {
- emsg(_("E109: Missing ':' after '?'"));
- if (evaluate && result) {
- tv_clear(rettv);
+ if (!op_falsy) {
+ // Check for the ":".
+ p = *arg;
+ if (*p != ':') {
+ emsg(_("E109: Missing ':' after '?'"));
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
}
- return FAIL;
- }
- // Get the third variable.
- *arg = skipwhite(*arg + 1);
- if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
- if (evaluate && result) {
- tv_clear(rettv);
+ // Get the third variable. Recursive!
+ *arg = skipwhite(*arg + 1);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ if (eval1(arg, &var2, evalarg_used) == FAIL) {
+ if (evaluate && result) {
+ tv_clear(rettv);
+ }
+ evalarg_used->eval_flags = orig_flags;
+ return FAIL;
+ }
+ if (evaluate && !result) {
+ *rettv = var2;
}
- return FAIL;
}
- if (evaluate && !result) {
- *rettv = var2;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2333,21 +2440,29 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
/// "arg" is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval2(char **arg, typval_T *rettv, int evaluate)
+static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval3(arg, rettv, evaluate) == FAIL) {
+ if (eval3(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "||".
- bool first = true;
- bool result = false;
- while ((*arg)[0] == '|' && (*arg)[1] == '|') {
- if (evaluate && first) {
+ // Handle the "||" operator.
+ char *p = *arg;
+ if (p[0] == '|' && p[1] == '|') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = false;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) != 0) {
result = true;
}
@@ -2355,28 +2470,41 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval3(arg, &var2, evaluate && !result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "||".
+ while (p[0] == '|' && p[1] == '|') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval3(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && !result) {
- if (tv_get_number_chk(&var2, &error) != 0) {
- result = true;
+ // Compute the result.
+ if (evaluate && !result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) != 0) {
+ result = true;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2390,21 +2518,29 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
/// `arg` is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval3(char **arg, typval_T *rettv, int evaluate)
+static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- bool error = false;
-
// Get the first variable.
- if (eval4(arg, rettv, evaluate) == FAIL) {
+ if (eval4(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- // Repeat until there is no following "&&".
- bool first = true;
- bool result = true;
- while ((*arg)[0] == '&' && (*arg)[1] == '&') {
- if (evaluate && first) {
+ char *p = *arg;
+ // Handle the "&&" operator.
+ if (p[0] == '&' && p[1] == '&') {
+ evalarg_T *evalarg_used = evalarg;
+ evalarg_T local_evalarg;
+ if (evalarg == NULL) {
+ local_evalarg = (evalarg_T){ .eval_flags = 0 };
+ evalarg_used = &local_evalarg;
+ }
+ const int orig_flags = evalarg_used->eval_flags;
+ const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE;
+
+ bool result = true;
+
+ if (evaluate) {
+ bool error = false;
if (tv_get_number_chk(rettv, &error) == 0) {
result = false;
}
@@ -2412,28 +2548,41 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
if (error) {
return FAIL;
}
- first = false;
}
- // Get the second variable.
- *arg = skipwhite(*arg + 2);
- if (eval4(arg, &var2, evaluate && result) == FAIL) {
- return FAIL;
- }
+ // Repeat until there is no following "&&".
+ while (p[0] == '&' && p[1] == '&') {
+ // Get the second variable.
+ *arg = skipwhite(*arg + 2);
+ evalarg_used->eval_flags = result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
+ typval_T var2;
+ if (eval4(arg, &var2, evalarg_used) == FAIL) {
+ return FAIL;
+ }
- // Compute the result.
- if (evaluate && result) {
- if (tv_get_number_chk(&var2, &error) == 0) {
- result = false;
+ // Compute the result.
+ if (evaluate && result) {
+ bool error = false;
+ if (tv_get_number_chk(&var2, &error) == 0) {
+ result = false;
+ }
+ tv_clear(&var2);
+ if (error) {
+ return FAIL;
+ }
}
- tv_clear(&var2);
- if (error) {
- return FAIL;
+ if (evaluate) {
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = result;
}
+
+ p = *arg;
}
- if (evaluate) {
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = result;
+
+ if (evalarg == NULL) {
+ clear_evalarg(&local_evalarg, NULL);
+ } else {
+ evalarg->eval_flags = orig_flags;
}
}
@@ -2456,19 +2605,18 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
/// "arg" is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval4(char **arg, typval_T *rettv, int evaluate)
+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;
// Get the first variable.
- if (eval5(arg, rettv, evaluate) == FAIL) {
+ if (eval5(arg, rettv, evalarg) == FAIL) {
return FAIL;
}
- p = *arg;
+ char *p = *arg;
switch (p[0]) {
case '=':
if (p[1] == '=') {
@@ -2528,11 +2676,11 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
// Get the second variable.
*arg = skipwhite(p + len);
- if (eval5(arg, &var2, evaluate) == FAIL) {
+ if (eval5(arg, &var2, evalarg) == FAIL) {
tv_clear(rettv);
return FAIL;
}
- if (evaluate) {
+ if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE)) {
const int ret = typval_compare(rettv, &var2, type, ic);
tv_clear(&var2);
@@ -2586,25 +2734,22 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2)
/// `arg` is advanced to the next non-white after the recognized expression.
///
/// @return OK or FAIL.
-static int eval5(char **arg, typval_T *rettv, int evaluate)
+static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
- typval_T var2;
- varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
- char *p;
-
// Get the first variable.
- if (eval6(arg, rettv, evaluate, false) == FAIL) {
+ if (eval6(arg, rettv, evalarg, false) == FAIL) {
return FAIL;
}
// Repeat computing, until no '+', '-' or '.' is following.
for (;;) {
int op = (uint8_t)(**arg);
- if (op != '+' && op != '-' && op != '.') {
+ bool concat = op == '.';
+ if (op != '+' && op != '-' && !concat) {
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
@@ -2625,7 +2770,8 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
(*arg)++;
}
*arg = skipwhite(*arg + 1);
- if (eval6(arg, &var2, evaluate, op == '.') == FAIL) {
+ typval_T var2;
+ if (eval6(arg, &var2, evalarg, op == '.') == FAIL) {
tv_clear(rettv);
return FAIL;
}
@@ -2643,7 +2789,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = concat_str(s1, s2);
+ char *p = concat_str(s1, s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -2655,6 +2801,8 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
} else {
bool error = false;
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2724,32 +2872,30 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
/// expression. Is advanced to the next non-whitespace
/// character after the recognized expression.
/// @param[out] rettv Location where result is saved.
-/// @param[in] evaluate If not true, rettv is not populated.
/// @param[in] want_string True if "." is string_concatenation, otherwise
/// float
/// @return OK or FAIL.
-static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
+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, evaluate, want_string) == FAIL) {
+ if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
return FAIL;
}
// Repeat computing, until no '*', '/' or '%' is following.
for (;;) {
- 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;
@@ -2768,7 +2914,8 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
// Get the second variable.
*arg = skipwhite(*arg + 1);
- if (eval7(arg, &var2, evaluate, false) == FAIL) {
+ typval_T var2;
+ if (eval7(arg, &var2, evalarg, false) == FAIL) {
return FAIL;
}
@@ -2859,8 +3006,9 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
/// @param want_string after "." operator
///
/// @return OK or FAIL.
-static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
+static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
static int recurse = 0;
@@ -2901,7 +3049,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
case '7':
case '8':
case '9':
- ret = get_number_tv(arg, rettv, evaluate, want_string);
+ ret = eval_number(arg, rettv, evaluate, want_string);
// Apply prefixed "-" and "+" now. Matters especially when
// "->" follows.
@@ -2912,24 +3060,24 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// String constant: "string".
case '"':
- ret = get_string_tv(arg, rettv, evaluate);
+ ret = eval_string(arg, rettv, evaluate);
break;
// Literal string constant: 'str''ing'.
case '\'':
- ret = get_lit_string_tv(arg, rettv, evaluate);
+ ret = eval_lit_string(arg, rettv, evaluate);
break;
// List: [expr, expr]
case '[':
- ret = get_list_tv(arg, rettv, evaluate);
+ ret = eval_list(arg, rettv, evalarg);
break;
// Dictionary: #{key: val, key: val}
case '#':
if ((*arg)[1] == '{') {
(*arg)++;
- ret = eval_dict(arg, rettv, evaluate, true);
+ ret = eval_dict(arg, rettv, evalarg, true);
} else {
ret = NOTDONE;
}
@@ -2938,19 +3086,19 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// 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, evaluate, false);
+ ret = eval_dict(arg, rettv, evalarg, false);
}
break;
// Option value: &name
case '&':
- ret = get_option_tv((const char **)arg, rettv, evaluate);
+ ret = eval_option((const char **)arg, rettv, evaluate);
break;
// Environment variable: $VAR.
case '$':
- ret = get_env_tv(arg, rettv, evaluate);
+ ret = eval_env_var(arg, rettv, evaluate);
break;
// Register contents: @r.
@@ -2968,7 +3116,8 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// nested expression: (expression).
case '(':
*arg = skipwhite(*arg + 1);
- ret = eval1(arg, rettv, evaluate); // recursive!
+
+ ret = eval1(arg, rettv, evalarg); // recursive!
if (**arg == ')') {
(*arg)++;
} else if (ret == OK) {
@@ -2996,11 +3145,15 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
if (len <= 0) {
ret = FAIL;
} else {
- if (**arg == '(') { // recursive!
- ret = eval_func(arg, s, len, rettv, evaluate, NULL);
+ const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+ if (**arg == '(') {
+ // "name(..." recursive!
+ ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
} else if (evaluate) {
- ret = get_var_tv(s, len, rettv, NULL, true, false);
+ // get value of variable
+ ret = eval_variable(s, len, rettv, NULL, true, false);
} else {
+ // skip the name
check_vars(s, (size_t)len);
ret = OK;
}
@@ -3013,7 +3166,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr)
if (ret == OK) {
- ret = handle_subscript((const char **)arg, rettv, evaluate, true);
+ ret = handle_subscript((const char **)arg, rettv, evalarg, true);
}
// Apply logical NOT and unary '-', from right to left, ignore '+'.
@@ -3089,10 +3242,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;
@@ -3124,7 +3277,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).
@@ -3143,16 +3296,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 != '(') {
@@ -3166,7 +3320,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
@@ -3183,10 +3337,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;
@@ -3236,9 +3392,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, &base);
+ ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
}
}
@@ -3257,8 +3413,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, int evaluate, int verbose)
+static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
bool empty1 = false;
bool empty2 = false;
ptrdiff_t len = -1;
@@ -3313,7 +3470,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ':') {
empty1 = true;
- } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive!
return FAIL;
} else if (evaluate && !tv_check_str(&var1)) {
// Not a number or string.
@@ -3327,7 +3484,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
*arg = skipwhite(*arg + 1);
if (**arg == ']') {
empty2 = true;
- } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive!
+ } else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive!
if (!empty1) {
tv_clear(&var1);
}
@@ -3557,7 +3714,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
/// @param[in] evaluate If not true, rettv is not populated.
///
/// @return OK or FAIL.
-int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
+int eval_option(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
const bool working = (**arg == '+'); // has("+option")
@@ -3620,7 +3777,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
/// Allocate a variable for a number constant. Also deals with "0z" for blob.
///
/// @return OK or FAIL.
-static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_string)
+static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_string)
{
char *p = skipdigits(*arg + 1);
bool get_float = false;
@@ -3705,7 +3862,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s
/// Allocate a variable for a string constant.
///
/// @return OK or FAIL.
-static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_string(char **arg, typval_T *rettv, int evaluate)
{
char *p;
unsigned int extra = 0;
@@ -3818,7 +3975,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
- iemsg("get_string_tv() used more space than allocated");
+ iemsg("eval_string() used more space than allocated");
}
break;
}
@@ -3845,7 +4002,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
/// Allocate a variable for a 'str''ing' constant.
///
/// @return OK or FAIL.
-static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_lit_string(char **arg, typval_T *rettv, int evaluate)
{
char *p;
int reduce = 0;
@@ -3929,9 +4086,11 @@ 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, int evaluate)
+static int eval_list(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
list_T *l = NULL;
if (evaluate) {
@@ -3941,7 +4100,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) {
typval_T tv;
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
goto failret;
}
if (evaluate) {
@@ -3949,14 +4108,20 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate)
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 != ']') {
@@ -4573,11 +4738,14 @@ static int get_literal_key(char **arg, typval_T *tv)
}
/// Allocate a variable for a Dictionary and fill it from "*arg".
-/// "literal" is true for #{key: val}
+///
+/// @param arg "*arg" points to the "{".
+/// @param literal true for #{key: val}
///
/// @return OK or FAIL. Returns NOTDONE for {expr}.
-static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
+static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal)
{
+ const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
typval_T tv;
char *key = NULL;
char *curly_expr = skipwhite(*arg + 1);
@@ -4591,7 +4759,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
// "#{abc}" is never a curly-braces expression.
if (*curly_expr != '}'
&& !literal
- && eval1(&curly_expr, &tv, false) == OK
+ && eval1(&curly_expr, &tv, NULL) == OK
&& *skipwhite(curly_expr) == '}') {
return NOTDONE;
}
@@ -4608,7 +4776,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
while (**arg != '}' && **arg != NUL) {
if ((literal
? get_literal_key(arg, &tvkey)
- : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive!
+ : eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive!
goto failret;
}
if (**arg != ':') {
@@ -4626,7 +4794,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal)
}
*arg = skipwhite(*arg + 1);
- if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
+ if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
if (evaluate) {
tv_clear(&tvkey);
}
@@ -4649,14 +4817,19 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, 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 != '}') {
@@ -4713,7 +4886,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
/// @param arg Points to the '$'. It is advanced to after the name.
///
/// @return FAIL if the name is invalid.
-static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
+static int eval_env_var(char **arg, typval_T *rettv, int evaluate)
{
(*arg)++;
char *name = *arg;
@@ -6501,15 +6674,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
}
char *retval = NULL;
- char *nextcmd = NULL;
*expr_start = NUL;
*expr_end = NUL;
char c1 = *in_end;
*in_end = NUL;
- char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
- if (temp_result != NULL && nextcmd == NULL) {
+ char *temp_result = eval_to_string(expr_start + 1, false);
+ if (temp_result != NULL) {
retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
STRCPY(retval, in_start);
@@ -6937,12 +7109,13 @@ int check_luafunc_name(const char *const str, const bool paren)
///
/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len()
///
-/// @param evaluate do more than finding the end
/// @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, int evaluate, int verbose)
+int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg,
+ bool verbose)
{
+ const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK;
dict_T *selfdict = NULL;
const char *lua_funcname = NULL;
@@ -6971,7 +7144,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
&& !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
@@ -6987,10 +7160,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} 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);
@@ -7002,7 +7175,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
} else {
selfdict = NULL;
}
- if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) {
+ if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) {
tv_clear(rettv);
ret = FAIL;
}
@@ -7374,6 +7547,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;
+
+ fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) {
emsg_skip++;
@@ -7385,7 +7561,7 @@ void ex_echo(exarg_T *eap)
{
char *p = arg;
- if (eval1(&arg, &rettv, !eap->skip) == FAIL) {
+ if (eval1(&arg, &rettv, &evalarg) == FAIL) {
// Report the invalid expression unless the expression evaluation
// has been cancelled due to an aborting error, an interrupt, or an
// exception.
@@ -7425,6 +7601,7 @@ void ex_echo(exarg_T *eap)
arg = skipwhite(arg);
}
eap->nextcmd = check_nextcmd(arg);
+ clear_evalarg(&evalarg, eap);
if (eap->skip) {
emsg_skip--;
@@ -7463,7 +7640,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;
}
@@ -8246,14 +8423,14 @@ bool eval_has_provider(const char *feat)
typval_T tv;
// Get the g:loaded_xx_provider variable.
int len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
- if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
+ if (eval_variable(buf, len, &tv, NULL, false, true) == FAIL) {
// Trigger autoload once.
len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name);
script_autoload(buf, (size_t)len, false);
// Retry the (non-autoload-style) variable.
len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name);
- if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) {
+ if (eval_variable(buf, len, &tv, NULL, false, true) == FAIL) {
// Show a hint if Call() is defined but g:loaded_xx_provider is missing.
snprintf(buf, sizeof(buf), "provider#%s#Call", name);
if (!!find_func(buf) && p_lpl) {
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index aa034cb2b3..e9cdb108a8 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -266,6 +266,27 @@ typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int);
// Used for checking if local variables or arguments used in a lambda.
extern bool *eval_lavars_used;
+/// Struct passed through eval() functions.
+/// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE.
+typedef struct {
+ int eval_flags; ///< EVAL_ flag values below
+
+ /// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
+ LineGetter eval_getline;
+ void *eval_cookie; ///< argument for eval_getline()
+
+ /// pointer to the last line obtained with getsourceline()
+ char *eval_tofree;
+} evalarg_T;
+
+/// Flag for expression evaluation.
+enum {
+ EVAL_EVALUATE = 1, ///< when missing don't actually evaluate
+};
+
+/// Passed to an eval() function to enable evaluation.
+EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL, NULL });
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
#endif
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 7f224f371c..23d0242ce4 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -1550,7 +1550,7 @@ static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
const char *const expr_start = s;
- if (s == NULL || eval1((char **)&s, rettv, true) == FAIL) {
+ if (s == NULL || eval1((char **)&s, rettv, &EVALARG_EVALUATE) == FAIL) {
if (expr_start != NULL && !aborting()) {
semsg(_(e_invexpr2), expr_start);
}
@@ -1722,7 +1722,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(exp);
}
} else if (*p == '&' || *p == '+') { // Option.
- n = (get_option_tv(&p, NULL, true) == OK);
+ n = (eval_option(&p, NULL, true) == OK);
if (*skipwhite(p) != NUL) {
n = false; // Trailing garbage.
}
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 3e67571053..91be41751e 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -4201,3 +4201,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
return res != NULL ? res : "";
}
+
+/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
+/// list, etc. Mostly like what JavaScript does, except that empty list and
+/// empty dictionary are false.
+bool tv2bool(const typval_T *const tv)
+{
+ switch (tv->v_type) {
+ case VAR_NUMBER:
+ return tv->vval.v_number != 0;
+ case VAR_FLOAT:
+ return tv->vval.v_float != 0.0;
+ case VAR_PARTIAL:
+ return tv->vval.v_partial != NULL;
+ case VAR_FUNC:
+ case VAR_STRING:
+ return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
+ case VAR_LIST:
+ return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
+ case VAR_DICT:
+ return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
+ case VAR_BOOL:
+ return tv->vval.v_bool == kBoolVarTrue;
+ case VAR_SPECIAL:
+ return tv->vval.v_special == kSpecialVarNull;
+ case VAR_BLOB:
+ return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
+ case VAR_UNKNOWN:
+ break;
+ }
+ return false;
+}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f09da6b79b..178f9fd6b6 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -71,6 +71,8 @@ static const char *e_funcref = N_("E718: Funcref required");
static const char *e_nofunc = N_("E130: Unknown function: %s");
static const char e_no_white_space_allowed_before_str_str[]
= N_("E1068: No white space allowed before '%s': %s");
+static const char e_missing_heredoc_end_marker_str[]
+ = N_("E1145: Missing heredoc end marker: %s");
void func_init(void)
{
@@ -152,7 +154,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
p = skipwhite(p) + 1;
p = skipwhite(p);
char *expr = p;
- if (eval1(&p, &rettv, false) != FAIL) {
+ if (eval1(&p, &rettv, NULL) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
@@ -252,22 +254,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 +293,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 +326,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 +368,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,7 +467,8 @@ 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;
@@ -463,7 +483,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], funcexe->fe_evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) {
ret = FAIL;
break;
}
@@ -972,7 +992,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
default_expr = ((char **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1(&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) {
default_arg_err = true;
break;
}
@@ -1109,7 +1129,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1(&p, rettv, true);
+ (void)eval1(&p, rettv, &EVALARG_EVALUATE);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -2301,7 +2321,11 @@ void ex_function(exarg_T *eap)
lines_left = Rows - 1;
}
if (theline == NULL) {
- emsg(_("E126: Missing :endfunction"));
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
goto erret;
}
if (show_block) {
@@ -2947,13 +2971,15 @@ void ex_return(exarg_T *eap)
return;
}
+ evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE };
+
if (eap->skip) {
emsg_skip++;
}
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
- && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) {
+ && eval0(arg, &rettv, eap, &evalarg) != FAIL) {
if (!eap->skip) {
returning = do_return(eap, false, true, &rettv);
} else {
@@ -2982,6 +3008,7 @@ void ex_return(exarg_T *eap)
if (eap->skip) {
emsg_skip--;
}
+ clear_evalarg(&evalarg, eap);
}
/// ":1,25call func(arg1, arg2)" function call.
@@ -2998,16 +3025,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->nextcmd, false) != FAIL) {
+ if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
tv_clear(&rettv);
}
emsg_skip--;
+ clear_evalarg(&evalarg, eap);
return;
}
@@ -3065,14 +3095,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, true, true)
- == FAIL) {
+ if (handle_subscript((const char **)&arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
failed = true;
break;
}
@@ -3104,6 +3133,7 @@ void ex_call(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg);
}
}
+ clear_evalarg(&evalarg, eap);
end:
tv_dict_unref(fudi.fd_dict);
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index c8583f232c..f0e1f5dca0 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -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"
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index 701f190a06..b8a8f39437 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -175,25 +175,16 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
/// ":let var ..= expr" assignment command.
/// ":let [var1, var2] = expr" unpack list.
/// ":let [name, ..., ; lastname] = expr" unpack list.
-void ex_let(exarg_T *eap)
-{
- ex_let_const(eap, false);
-}
-
+///
/// ":cons[t] var = expr1" define constant
/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list
/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list
-void ex_const(exarg_T *eap)
-{
- ex_let_const(eap, true);
-}
-
-static void ex_let_const(exarg_T *eap, const bool is_const)
+void ex_let(exarg_T *eap)
{
+ const bool is_const = eap->cmdidx == CMD_const;
char *arg = eap->arg;
char *expr = NULL;
typval_T rettv;
- int i;
int var_count = 0;
int semicolon = 0;
char op[2];
@@ -208,8 +199,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
argend--;
}
expr = skipwhite(argend);
- if (*expr != '=' && !((vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
- && expr[1] == '=') || strncmp(expr, "..=", 3) == 0)) {
+ bool concat = strncmp(expr, "..=", 3) == 0;
+ bool has_assign = *expr == '=' || (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL
+ && expr[1] == '=');
+ if (!has_assign && !concat) {
// ":let" without "=": list variables
if (*arg == '[') {
emsg(_(e_invarg));
@@ -227,7 +220,10 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
list_vim_vars(&first);
}
eap->nextcmd = check_nextcmd(arg);
- } else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
+ return;
+ }
+
+ if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<') {
// HERE document
list_T *l = heredoc_get(eap, expr + 3);
if (l != NULL) {
@@ -239,36 +235,43 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
}
tv_clear(&rettv);
}
- } else {
- op[0] = '=';
- op[1] = NUL;
- if (*expr != '=') {
- if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
- op[0] = *expr; // +=, -=, *=, /=, %= or .=
- if (expr[0] == '.' && expr[1] == '.') { // ..=
- expr++;
- }
- }
- expr += 2;
- } else {
- expr += 1;
- }
+ return;
+ }
- expr = skipwhite(expr);
+ rettv.v_type = VAR_UNKNOWN;
- if (eap->skip) {
- emsg_skip++;
- }
- i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
- if (eap->skip) {
- if (i != FAIL) {
- tv_clear(&rettv);
+ op[0] = '=';
+ op[1] = NUL;
+ if (*expr != '=') {
+ if (vim_strchr("+-*/%.", (uint8_t)(*expr)) != NULL) {
+ op[0] = *expr; // +=, -=, *=, /=, %= or .=
+ if (expr[0] == '.' && expr[1] == '.') { // ..=
+ expr++;
}
- emsg_skip--;
- } else if (i != FAIL) {
- (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
- tv_clear(&rettv);
}
+ expr += 2;
+ } else {
+ expr += 1;
+ }
+
+ expr = skipwhite(expr);
+
+ if (eap->skip) {
+ emsg_skip++;
+ }
+ 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);
+ }
+ if (eval_res != FAIL) {
+ tv_clear(&rettv);
}
}
@@ -500,13 +503,12 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
if (tofree != NULL) {
name = tofree;
}
- if (get_var_tv(name, len, &tv, NULL, true, false)
- == FAIL) {
+ if (eval_variable(name, len, &tv, NULL, true, false) == FAIL) {
error = true;
} else {
// handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg;
- if (handle_subscript(&arg, &tv, true, true) == FAIL) {
+ if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
error = true;
} else {
if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -1073,8 +1075,8 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
/// @param dip non-NULL when typval's dict item is needed
/// @param verbose may give error message
/// @param no_autoload do not use script autoloading
-int get_var_tv(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
- bool no_autoload)
+int eval_variable(const char *name, int len, typval_T *rettv, dictitem_T **dip, bool verbose,
+ bool no_autoload)
{
int ret = OK;
typval_T *tv = NULL;
@@ -1149,7 +1151,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val)
}
}
hash_clear(ht);
- ht->ht_used = 0;
+ hash_init(ht);
}
/// Delete a variable from hashtab "ht" at item "hi".
@@ -1561,7 +1563,7 @@ static void get_var_from(const char *varname, typval_T *rettv, typval_T *deftv,
tv_dict_set_ret(rettv, opts);
done = true;
}
- } else if (get_option_tv(&varname, rettv, true) == OK) {
+ } else if (eval_option(&varname, rettv, true) == OK) {
// Local option
done = true;
}
@@ -1710,10 +1712,10 @@ bool var_exists(const char *var)
if (tofree != NULL) {
name = tofree;
}
- n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
+ n = eval_variable(name, len, &tv, NULL, false, true) == OK;
if (n) {
// Handle d.key, l[idx], f(expr).
- n = handle_subscript(&var, &tv, true, false) == OK;
+ n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
if (n) {
tv_clear(&tv);
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 2fd50a18d3..be6299db0e 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -197,12 +197,6 @@ module.cmds = {
func='ex_bunload',
},
{
- command='behave',
- flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN, LOCK_OK),
- addr_type='ADDR_NONE',
- func='ex_behave',
- },
- {
command='belowright',
flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM),
addr_type='ADDR_NONE',
@@ -640,7 +634,7 @@ module.cmds = {
command='const',
flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN, LOCK_OK),
addr_type='ADDR_NONE',
- func='ex_const',
+ func='ex_let',
},
{
command='copen',
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 629aaf14cf..7932649114 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -185,6 +185,7 @@ struct exarg {
char *nextcmd; ///< next command (NULL if none)
char *cmd; ///< the name of the command (except for :make)
char **cmdlinep; ///< pointer to pointer of allocated cmdline
+ char *cmdline_tofree; ///< free later
cmdidx_T cmdidx; ///< the index for the command
uint32_t argt; ///< flags for the command
int skip; ///< don't execute the command, only parse it
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 699c1da3a7..8e55672615 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -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;
}
}
@@ -2328,6 +2332,7 @@ doend:
}
ex_nesting_level--;
+ xfree(ea.cmdline_tofree);
return ea.nextcmd;
}
@@ -3750,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++;
}
@@ -3969,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;
}
@@ -4432,7 +4437,7 @@ static void ex_colorscheme(exarg_T *eap)
char *expr = xstrdup("g:colors_name");
emsg_off++;
- char *p = eval_to_string(expr, NULL, false);
+ char *p = eval_to_string(expr, false);
emsg_off--;
xfree(expr);
@@ -7058,24 +7063,6 @@ void dialog_msg(char *buff, char *format, char *fname)
vim_snprintf(buff, DIALOG_MSG_SIZE, format, fname);
}
-/// ":behave {mswin,xterm}"
-static void ex_behave(exarg_T *eap)
-{
- if (strcmp(eap->arg, "mswin") == 0) {
- set_option_value_give_err("selection", 0L, "exclusive", 0);
- set_option_value_give_err("selectmode", 0L, "mouse,key", 0);
- set_option_value_give_err("mousemodel", 0L, "popup", 0);
- set_option_value_give_err("keymodel", 0L, "startsel,stopsel", 0);
- } else if (strcmp(eap->arg, "xterm") == 0) {
- set_option_value_give_err("selection", 0L, "inclusive", 0);
- set_option_value_give_err("selectmode", 0L, "", 0);
- set_option_value_give_err("mousemodel", 0L, "extend", 0);
- set_option_value_give_err("keymodel", 0L, "", 0);
- } else {
- semsg(_(e_invarg2), eap->arg);
- }
-}
-
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index fed8f549e6..5404ae6731 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -792,10 +792,15 @@ void report_discard_pending(int pending, void *value)
void ex_eval(exarg_T *eap)
{
typval_T tv;
+ evalarg_T evalarg;
- if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK) {
+ 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".
@@ -812,7 +817,7 @@ void ex_if(exarg_T *eap)
int skip = CHECK_SKIP;
bool error;
- int result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ int result = eval_to_bool(eap->arg, &error, eap, skip);
if (!skip && !error) {
if (result) {
@@ -907,7 +912,7 @@ void ex_else(exarg_T *eap)
if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) {
semsg(_(e_invexpr2), eap->arg);
} else {
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
+ result = eval_to_bool(eap->arg, &error, eap, skip);
}
// When throwing error exceptions, we want to throw always the first
@@ -952,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"
- result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
- } else {
+ if (eap->cmdidx == CMD_while) { // ":while bool-expr"
+ result = eval_to_bool(eap->arg, &error, eap, skip);
+ } 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.
@@ -966,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->nextcmd, skip);
+ fi = eval_for_line(eap->arg, &error, eap, &evalarg);
cstack->cs_forinfo[cstack->cs_idx] = fi;
}
@@ -981,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
@@ -1125,12 +1130,11 @@ void ex_endwhile(exarg_T *eap)
/// Handle ":throw expr"
void ex_throw(exarg_T *eap)
{
- const char *arg = eap->arg;
+ char *arg = eap->arg;
char *value;
if (*arg != NUL && *arg != '|' && *arg != '\n') {
- value = eval_to_string_skip(arg, (const char **)&eap->nextcmd,
- (bool)eap->skip);
+ value = eval_to_string_skip(arg, eap, eap->skip);
} else {
emsg(_(e_argreq));
value = NULL;
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ba403e3dd9..f3afbdcaaf 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4209,7 +4209,8 @@ void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
- rettv->vval.v_number = set_cmdline_str(argvars[0].vval.v_string, pos);
+ // Use tv_get_string() to handle a NULL string like an empty string.
+ rettv->vval.v_number = set_cmdline_str(tv_get_string(&argvars[0]), pos);
}
/// "setcmdpos()" function
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 71984e806d..a0869b54c9 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1749,7 +1749,8 @@ char *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo
curbuf = wp->w_buffer;
emsg_silent++; // handle exceptions, but don't display errors
- text = eval_to_string_safe(wp->w_p_fdt, NULL, was_set_insecurely(wp, "foldtext", OPT_LOCAL));
+ text = eval_to_string_safe(wp->w_p_fdt,
+ was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
if (text == NULL || did_emsg) {
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 6493408a58..3ab8a3bb88 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -1530,6 +1530,10 @@ static bool hlgroup2dict(Dictionary *hl, NS ns_id, int hl_id, Arena *arena)
if (link == -1) {
return false;
}
+ if (ns_id == 0 && sgp->sg_cleared && sgp->sg_set == 0) {
+ // table entry was created but not ever set
+ return false;
+ }
HlAttrs attr =
syn_attr2entry(ns_id == 0 ? sgp->sg_attr : ns_get_hl(&ns_id, hl_id, false, sgp->sg_set));
*hl = arena_dict(arena, HLATTRS_DICT_SIZE + 1);
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 19a2aca75e..c1565a84f5 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -1632,7 +1632,7 @@ char *eval_map_expr(mapblock_T *mp, int c)
api_clear_error(&err);
}
} else {
- p = eval_to_string(expr, NULL, false);
+ p = eval_to_string(expr, false);
xfree(expr);
}
expr_map_lock--;
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 855fcb33ae..59ff3026b3 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -84,7 +84,7 @@ void clear_fmark(fmark_T *fm)
FUNC_ATTR_NONNULL_ALL
{
free_fmark(*fm);
- CLEAR_POINTER(fm);
+ *fm = (fmark_T)INIT_FMARK;
}
// Set named mark "c" to position "pos".
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index af0abba864..8c72579d0f 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -36,7 +36,7 @@
/// Clear given fmark
#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T) { 0 }))
+ RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T)INIT_FMARKV))
/// Set given extended mark (regular mark + file name)
#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index b11d5a3c32..718c51deb0 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1173,7 +1173,7 @@ static int normal_execute(VimState *state, int key)
msg_col = 0;
}
- s->old_pos = curwin->w_cursor; // remember where cursor was
+ s->old_pos = curwin->w_cursor; // remember where the cursor was
// When 'keymodel' contains "startsel" some keys start Select/Visual
// mode.
@@ -1997,13 +1997,21 @@ static void display_showcmd(void)
showcmd_is_clear = (len == 0);
if (*p_sloc == 's') {
- win_redr_status(curwin);
- setcursor(); // put cursor back where it belongs
+ if (showcmd_is_clear) {
+ curwin->w_redr_status = true;
+ } else {
+ win_redr_status(curwin);
+ setcursor(); // put cursor back where it belongs
+ }
return;
}
if (*p_sloc == 't') {
- draw_tabline();
- setcursor(); // put cursor back where it belongs
+ if (showcmd_is_clear) {
+ redraw_tabline = true;
+ } else {
+ draw_tabline();
+ setcursor(); // put cursor back where it belongs
+ }
return;
}
// 'showcmdloc' is "last" or empty
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 9aacfcad30..b2c0dd6c01 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -763,7 +763,7 @@ char *get_expr_line(void)
}
nested++;
- rv = eval_to_string(expr_copy, NULL, true);
+ rv = eval_to_string(expr_copy, true);
nested--;
xfree(expr_copy);
return rv;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 386c6d88d9..6d4e7de1a3 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -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;
}
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 0b06877f3c..26707fd6ca 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -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++;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 368f3feb27..8bd3303166 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1371,7 +1371,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags)
char *cmd = xstrnsave(pat + 1, strlen(pat) - 2);
if (*cmd == '=') { // `={expr}`: Expand expression
- buffer = eval_to_string(cmd + 1, &p, true);
+ buffer = eval_to_string(cmd + 1, true);
} else {
buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
}
@@ -1662,7 +1662,7 @@ void simplify_filename(char *filename)
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- char *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
+ char *res = eval_to_string_safe(curbuf->b_p_inex,
was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index c8db9a64b5..ab6acf685f 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -5540,7 +5540,7 @@ msgstr "tekijät Bram Moolenaar et al."
#~ msgstr "kirjoita :help iccf<Enter> lisätietoa varten "
#, fuzzy
-#~ msgid "type :CheckHealth<Enter> to optimize Nvim"
+#~ msgid "type :checkhealth<Enter> to optimize Nvim"
#~ msgstr "kirjoita :help iccf<Enter> lisätietoa varten "
msgid "type :q<Enter> to exit "
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 262d87ba61..62eb14342c 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -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->nextcmd, true) == 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"
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 94796f0ed3..3ed32bf8af 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1812,7 +1812,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
}
tv_clear(&rettv);
} else {
- eval_result[nested] = eval_to_string(source + 2, NULL, true);
+ eval_result[nested] = eval_to_string(source + 2, true);
}
nesting--;
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 2d5eee7eec..78499922bf 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1307,6 +1307,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
.mark = cur_entry.data.filemark.mark,
.fnum = (buf == NULL ? 0 : buf->b_fnum),
.timestamp = cur_entry.timestamp,
+ .view = INIT_FMARKV,
.additional_data = cur_entry.data.filemark.additional_data,
},
};
@@ -1388,6 +1389,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
.mark = cur_entry.data.filemark.mark,
.fnum = 0,
.timestamp = cur_entry.timestamp,
+ .view = INIT_FMARKV,
.additional_data = cur_entry.data.filemark.additional_data,
};
if (cur_entry.type == kSDItemLocalMark) {
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 6c698f45be..05649e9b7f 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -985,7 +985,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
};
set_var(S_LEN("g:statusline_winid"), &tv, false);
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
+ usefmt = eval_to_string_safe(fmt + 2, use_sandbox);
if (usefmt == NULL) {
usefmt = fmt;
}
@@ -1457,7 +1457,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
}
// Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
+ str = eval_to_string_safe(out_p, use_sandbox);
curwin = save_curwin;
curbuf = save_curbuf;
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 401b43204e..cebc7d5d89 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -3322,11 +3322,10 @@ int get_tags(list_T *list, char *pat, char *buf_fname)
}
for (i = 0; i < num_matches; i++) {
- int parse_result = parse_match(matches[i], &tp);
-
- // Avoid an unused variable warning in release builds.
- (void)parse_result;
- assert(parse_result == OK);
+ if (parse_match(matches[i], &tp) == FAIL) {
+ xfree(matches[i]);
+ continue;
+ }
bool is_static = test_for_static(&tp);
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 4cd58bb91b..e859a96552 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -56,7 +56,6 @@ static const char e_no_such_user_defined_command_in_current_buffer_str[]
static const char *command_complete[] = {
[EXPAND_ARGLIST] = "arglist",
[EXPAND_AUGROUP] = "augroup",
- [EXPAND_BEHAVE] = "behave",
[EXPAND_BUFFERS] = "buffer",
[EXPAND_CHECKHEALTH] = "checkhealth",
[EXPAND_COLORS] = "color",
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 00e28e3e68..44e3e9d86a 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -140,7 +140,6 @@ enum {
EXPAND_SHELLCMD,
EXPAND_SIGN,
EXPAND_PROFILE,
- EXPAND_BEHAVE,
EXPAND_FILETYPE,
EXPAND_FILES_IN_PATH,
EXPAND_OWNSYNTAX,