diff options
Diffstat (limited to 'src/nvim')
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, |