aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c125
1 files changed, 76 insertions, 49 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index fd2c3a6dd8..1cef91785c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -83,7 +83,7 @@
#include "nvim/os/time.h"
#include "nvim/os/channel.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/private/defs.h"
+#include "nvim/api/vim.h"
#include "nvim/os/msgpack_rpc_helpers.h"
#include "nvim/os/dl.h"
#include "nvim/os/provider.h"
@@ -193,6 +193,9 @@ static int current_copyID = 0;
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
+/// Abort conversion to string after a recursion error.
+static bool did_echo_string_emsg = false;
+
/*
* Array to hold the hashtab with variables local to each sourced script.
* Each item holds a variable (nameless) that points to the dict_T.
@@ -5322,6 +5325,9 @@ list_join_inner (
}
line_breakcheck();
+ if (did_echo_string_emsg) { // recursion error, bail out
+ break;
+ }
}
/* Allocate result buffer with its total size, avoid re-allocation and
@@ -5945,8 +5951,10 @@ static char_u *dict2string(typval_T *tv, int copyID)
if (s != NULL)
ga_concat(&ga, s);
free(tofree);
- if (s == NULL)
+ if (s == NULL || did_echo_string_emsg) {
break;
+ }
+ line_breakcheck();
}
}
if (todo > 0) {
@@ -6077,9 +6085,15 @@ static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int co
char_u *r = NULL;
if (recurse >= DICT_MAXNEST) {
- EMSG(_("E724: variable nested too deep for displaying"));
+ if (!did_echo_string_emsg) {
+ // Only give this message once for a recursive call to avoid
+ // flooding the user with errors. And stop iterating over lists
+ // and dicts.
+ did_echo_string_emsg = true;
+ EMSG(_("E724: variable nested too deep for displaying"));
+ }
*tofree = NULL;
- return NULL;
+ return (char_u *)"{E724}";
}
++recurse;
@@ -6134,7 +6148,9 @@ static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int co
*tofree = NULL;
}
- --recurse;
+ if (--recurse == 0) {
+ did_echo_string_emsg = false;
+ }
return r;
}
@@ -6392,7 +6408,7 @@ static struct fst {
{"getwinposy", 0, 0, f_getwinposy},
{"getwinvar", 2, 3, f_getwinvar},
{"glob", 1, 3, f_glob},
- {"globpath", 2, 3, f_globpath},
+ {"globpath", 2, 4, f_globpath},
{"has", 1, 1, f_has},
{"has_key", 2, 2, f_has_key},
{"haslocaldir", 0, 0, f_haslocaldir},
@@ -8283,11 +8299,8 @@ static void f_extend(typval_T *argvars, typval_T *rettv)
*/
static void f_feedkeys(typval_T *argvars, typval_T *rettv)
{
- int remap = TRUE;
- char_u *keys, *flags;
+ char_u *keys, *flags = NULL;
char_u nbuf[NUMBUFLEN];
- int typed = FALSE;
- char_u *keys_esc;
/* This is not allowed in the sandbox. If the commands would still be
* executed in the sandbox it would be OK, but it probably happens later,
@@ -8299,23 +8312,10 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv)
if (*keys != NUL) {
if (argvars[1].v_type != VAR_UNKNOWN) {
flags = get_tv_string_buf(&argvars[1], nbuf);
- for (; *flags != NUL; ++flags) {
- switch (*flags) {
- case 'n': remap = FALSE; break;
- case 'm': remap = TRUE; break;
- case 't': typed = TRUE; break;
- }
- }
}
- /* Need to escape K_SPECIAL and CSI before putting the string in the
- * typeahead buffer. */
- keys_esc = vim_strsave_escape_csi(keys);
- ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE),
- typebuf.tb_len, !typed, FALSE);
- free(keys_esc);
- if (vgetc_busy)
- typebuf_was_filled = TRUE;
+ vim_feedkeys(cstr_as_string((char *)keys),
+ cstr_as_string((char *)flags));
}
}
@@ -9608,27 +9608,50 @@ static void f_glob(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;
}
-/*
- * "globpath()" function
- */
+/// "globpath()" function
static void f_globpath(typval_T *argvars, typval_T *rettv)
{
- int flags = 0;
- char_u buf1[NUMBUFLEN];
- char_u *file = get_tv_string_buf_chk(&argvars[1], buf1);
- int error = FALSE;
+ int flags = 0; // Flags for globpath.
+ int error = false;
- /* When the optional second argument is non-zero, don't remove matches
- * for 'wildignore' and don't put matches for 'suffixes' at the end. */
- if (argvars[2].v_type != VAR_UNKNOWN
- && get_tv_number_chk(&argvars[2], &error))
- flags |= WILD_KEEP_ALL;
+ // Return a string, or a list if the optional third argument is non-zero.
rettv->v_type = VAR_STRING;
- if (file == NULL || error)
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ // When the optional second argument is non-zero, don't remove matches
+ // for 'wildignore' and don't put matches for 'suffixes' at the end.
+ if (get_tv_number_chk(&argvars[2], &error)) {
+ flags |= WILD_KEEP_ALL;
+ }
+
+ if (argvars[3].v_type != VAR_UNKNOWN
+ && get_tv_number_chk(&argvars[3], &error)) {
+ rettv->v_type = VAR_LIST;
+ rettv->vval.v_list = NULL;
+ }
+ }
+
+ char_u buf1[NUMBUFLEN];
+ char_u *file = get_tv_string_buf_chk(&argvars[1], buf1);
+ if (file != NULL && !error) {
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char_u *), 10);
+ globpath(get_tv_string(&argvars[0]), file, &ga, flags);
+
+ if (rettv->v_type == VAR_STRING) {
+ rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
+ } else {
+ rettv_list_alloc(rettv);
+ for (int i = 0; i < ga.ga_len; i++) {
+ list_append_string(rettv->vval.v_list,
+ ((char_u **)(ga.ga_data))[i], -1);
+ }
+ }
+
+ ga_clear_strings(&ga);
+ } else {
rettv->vval.v_string = NULL;
- else
- rettv->vval.v_string = globpath(get_tv_string(&argvars[0]), file,
- flags);
+ }
}
/*
@@ -9745,9 +9768,6 @@ static void f_has(typval_T *argvars, typval_T *rettv)
#endif
"tag_binary",
"tag_old_static",
-#ifdef FEAT_TAG_ANYWHITE
- "tag_any_white",
-#endif
#ifdef TERMINFO
"terminfo",
#endif
@@ -18032,7 +18052,10 @@ call_user_func (
if (argvars[i].v_type == VAR_NUMBER)
msg_outnum((long)argvars[i].vval.v_number);
else {
+ // Do not want errors such as E724 here.
+ ++emsg_off;
s = tv2string(&argvars[i], &tofree, numbuf2, 0);
+ --emsg_off;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -18114,10 +18137,12 @@ call_user_func (
char_u *tofree;
char_u *s;
- /* The value may be very long. Skip the middle part, so that we
- * have some idea how it starts and ends. smsg() would always
- * truncate it at the end. */
+ // The value may be very long. Skip the middle part, so that we
+ // have some idea how it starts and ends. smsg() would always
+ // truncate it at the end. Don't want errors such as E724 here.
+ ++emsg_off;
s = tv2string(fc->rettv, &tofree, numbuf2, 0);
+ --emsg_off;
if (s != NULL) {
if (vim_strsize(s) > MSG_BUF_CLEN) {
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
@@ -19019,8 +19044,10 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
if (regmatch.startp[0] == regmatch.endp[0]) {
if (zero_width == regmatch.startp[0]) {
/* avoid getting stuck on a match with an empty string */
- *((char_u *)ga.ga_data + ga.ga_len) = *tail++;
- ++ga.ga_len;
+ int i = MB_PTR2LEN(tail);
+ memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
+ ga.ga_len += i;
+ tail += i;
continue;
}
zero_width = regmatch.startp[0];