aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-08-14 05:18:21 +0800
committerGitHub <noreply@github.com>2022-08-14 05:18:21 +0800
commit8cd116729fe2a15d62cd10e5ba7d3dcf1f677337 (patch)
treec3697b80da96e6e1165742979deb121ca491b1d0 /src
parentc1cbe3fb3d2ec1dbcfdc14ee2d9a5e8049d494ae (diff)
parent1ca2247639424994890ef70ab34f2bffa23ddd9f (diff)
downloadrneovim-8cd116729fe2a15d62cd10e5ba7d3dcf1f677337.tar.gz
rneovim-8cd116729fe2a15d62cd10e5ba7d3dcf1f677337.tar.bz2
rneovim-8cd116729fe2a15d62cd10e5ba7d3dcf1f677337.zip
Merge pull request #19752 from zeertzjq/vim-8.2.0056
vim-patch:8.2.{0056,0061,0078,0097,0823}: execution stack
Diffstat (limited to 'src')
-rw-r--r--src/nvim/autocmd.c24
-rw-r--r--src/nvim/autocmd.h21
-rw-r--r--src/nvim/buffer.c15
-rw-r--r--src/nvim/debugger.c36
-rw-r--r--src/nvim/eval.c25
-rw-r--r--src/nvim/eval/funcs.c13
-rw-r--r--src/nvim/eval/typval.h2
-rw-r--r--src/nvim/eval/userfunc.c162
-rw-r--r--src/nvim/ex_docmd.c46
-rw-r--r--src/nvim/ex_eval.c20
-rw-r--r--src/nvim/ex_eval_defs.h6
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/highlight_group.c14
-rw-r--r--src/nvim/lua/executor.c6
-rw-r--r--src/nvim/main.c19
-rw-r--r--src/nvim/mapping.c5
-rw-r--r--src/nvim/message.c57
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/profile.c11
-rw-r--r--src/nvim/runtime.c140
-rw-r--r--src/nvim/runtime.h37
-rw-r--r--src/nvim/spellfile.c13
-rw-r--r--src/nvim/testdir/test_autocmd.vim22
-rw-r--r--src/nvim/testdir/test_vimscript.vim17
-rw-r--r--src/nvim/testing.c15
-rw-r--r--src/nvim/usercmd.c5
26 files changed, 463 insertions, 278 deletions
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 4ffd7a4850..0d8efbb830 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -27,6 +27,7 @@
#include "nvim/os/input.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/ui_compositor.h"
@@ -1143,7 +1144,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
ac->id = id;
ac->exec = aucmd_exec_copy(aucmd);
ac->script_ctx = current_sctx;
- ac->script_ctx.sc_lnum += sourcing_lnum;
+ ac->script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&ac->script_ctx);
ac->next = NULL;
ac->once = once;
@@ -1771,10 +1772,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Don't redraw while doing autocommands.
RedrawingDisabled++;
- char *save_sourcing_name = sourcing_name;
- sourcing_name = NULL; // don't free this one
- linenr_T save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 0; // no line number here
+
+ // name and lnum are filled in later
+ estack_push(ETYPE_AUCMD, NULL, 0);
const sctx_T save_current_sctx = current_sctx;
@@ -1878,9 +1878,8 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
autocmd_busy = save_autocmd_busy;
filechangeshell_busy = false;
autocmd_nested = save_autocmd_nested;
- xfree(sourcing_name);
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ xfree(SOURCING_NAME);
+ estack_pop();
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
autocmd_bufnr = save_autocmd_bufnr;
@@ -1983,8 +1982,9 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
AutoPat *ap;
AutoCmd *cp;
char *s;
+ char **const sourcing_namep = &SOURCING_NAME;
- XFREE_CLEAR(sourcing_name);
+ XFREE_CLEAR(*sourcing_namep);
for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) {
apc->curpat = NULL;
@@ -2009,11 +2009,11 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
const size_t sourcing_name_len
= (STRLEN(s) + strlen(name) + (size_t)ap->patlen + 1);
- sourcing_name = xmalloc(sourcing_name_len);
- snprintf(sourcing_name, sourcing_name_len, s, name, ap->pat);
+ *sourcing_namep = xmalloc(sourcing_name_len);
+ snprintf(*sourcing_namep, sourcing_name_len, s, name, ap->pat);
if (p_verbose >= 8) {
verbose_enter();
- smsg(_("Executing %s"), sourcing_name);
+ smsg(_("Executing %s"), *sourcing_namep);
verbose_leave();
}
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index a085a03455..d559d8c3d2 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -22,7 +22,8 @@ typedef struct {
bool save_VIsual_active; ///< saved VIsual_active
} aco_save_T;
-typedef struct AutoCmd {
+typedef struct AutoCmd_S AutoCmd;
+struct AutoCmd_S {
AucmdExecutable exec;
bool once; // "One shot": removed after execution
bool nested; // If autocommands nest here
@@ -30,11 +31,12 @@ typedef struct AutoCmd {
int64_t id; // ID used for uniquely tracking an autocmd.
sctx_T script_ctx; // script context where defined
char *desc; // Description for the autocmd.
- struct AutoCmd *next; // Next AutoCmd in list
-} AutoCmd;
+ AutoCmd *next; // Next AutoCmd in list
+};
-typedef struct AutoPat {
- struct AutoPat *next; // next AutoPat in AutoPat list; MUST
+typedef struct AutoPat_S AutoPat;
+struct AutoPat_S {
+ AutoPat *next; // next AutoPat in AutoPat list; MUST
// be the first entry
char *pat; // pattern as typed (NULL when pattern
// has been removed)
@@ -45,10 +47,11 @@ typedef struct AutoPat {
int buflocal_nr; // !=0 for buffer-local AutoPat
char allow_dirs; // Pattern may match whole path
char last; // last pattern for apply_autocmds()
-} AutoPat;
+};
/// Struct used to keep status while executing autocommands for an event.
-typedef struct AutoPatCmd {
+typedef struct AutoPatCmd_S AutoPatCmd;
+struct AutoPatCmd_S {
AutoPat *curpat; // next AutoPat to examine
AutoCmd *nextcmd; // next AutoCmd to execute
int group; // group being used
@@ -58,8 +61,8 @@ typedef struct AutoPatCmd {
event_T event; // current event
int arg_bufnr; // initially equal to <abuf>, set to zero when buf is deleted
Object *data; // arbitrary data
- struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation
-} AutoPatCmd;
+ AutoPatCmd *next; // chain of active apc-s for auto-invalidation
+};
// Set by the apply_autocmds_group function if the given event is equal to
// EVENT_FILETYPE. Used by the readfile function in order to determine if
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index be376c2109..6262f2d61c 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -70,6 +70,7 @@
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
@@ -5149,8 +5150,6 @@ static int chk_modeline(linenr_T lnum, int flags)
intmax_t vers;
int end;
int retval = OK;
- char *save_sourcing_name;
- linenr_T save_sourcing_lnum;
prev = -1;
for (s = (char *)ml_get(lnum); *s != NUL; s++) {
@@ -5195,10 +5194,8 @@ static int chk_modeline(linenr_T lnum, int flags)
s = linecopy = xstrdup(s); // copy the line, it will change
- save_sourcing_lnum = sourcing_lnum;
- save_sourcing_name = sourcing_name;
- sourcing_lnum = lnum; // prepare for emsg()
- sourcing_name = "modelines";
+ // prepare for emsg()
+ estack_push(ETYPE_MODELINE, "modelines", lnum);
end = false;
while (end == false) {
@@ -5238,7 +5235,7 @@ static int chk_modeline(linenr_T lnum, int flags)
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_MODELINE;
current_sctx.sc_seq = 0;
- current_sctx.sc_lnum = 0;
+ current_sctx.sc_lnum = lnum;
// Make sure no risky things are executed as a side effect.
secure = 1;
@@ -5253,9 +5250,7 @@ static int chk_modeline(linenr_T lnum, int flags)
s = e + 1; // advance to next part
}
- sourcing_lnum = save_sourcing_lnum;
- sourcing_name = save_sourcing_name;
-
+ estack_pop();
xfree(linecopy);
return retval;
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 0eaff06833..76e1caff49 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -17,6 +17,7 @@
#include "nvim/os/os.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/types.h"
#include "nvim/vim.h"
@@ -98,14 +99,17 @@ void do_debug(char_u *cmd)
xfree(debug_newval);
debug_newval = NULL;
}
- if (sourcing_name != NULL) {
- msg(sourcing_name);
+ char *sname = estack_sfile();
+ if (sname != NULL) {
+ msg(sname);
}
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
+ xfree(sname);
+ if (SOURCING_LNUM != 0) {
+ smsg(_("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
} else {
smsg(_("cmd: %s"), cmd);
}
+
// Repeat getting a command and executing it.
for (;;) {
msg_scroll = true;
@@ -287,12 +291,12 @@ void do_debug(char_u *cmd)
debug_did_msg = true;
}
-static int get_maxbacktrace_level(void)
+static int get_maxbacktrace_level(char *sname)
{
int maxbacktrace = 0;
- if (sourcing_name != NULL) {
- char *p = sourcing_name;
+ if (sname != NULL) {
+ char *p = sname;
char *q;
while ((q = strstr(p, "..")) != NULL) {
p = q + 2;
@@ -320,20 +324,24 @@ static void do_checkbacktracelevel(void)
debug_backtrace_level = 0;
msg(_("frame is zero"));
} else {
- int max = get_maxbacktrace_level();
+ char *sname = estack_sfile();
+ int max = get_maxbacktrace_level(sname);
+
if (debug_backtrace_level > max) {
debug_backtrace_level = max;
smsg(_("frame at highest level: %d"), max);
}
+ xfree(sname);
}
}
static void do_showbacktrace(char_u *cmd)
{
- if (sourcing_name != NULL) {
+ char *sname = estack_sfile();
+ int max = get_maxbacktrace_level(sname);
+ if (sname != NULL) {
int i = 0;
- int max = get_maxbacktrace_level();
- char *cur = sourcing_name;
+ char *cur = sname;
while (!got_int) {
char *next = strstr(cur, "..");
if (next != NULL) {
@@ -351,9 +359,11 @@ static void do_showbacktrace(char_u *cmd)
*next = '.';
cur = next + 2;
}
+ xfree(sname);
}
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
+
+ if (SOURCING_LNUM != 0) {
+ smsg(_("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
} else {
smsg(_("cmd: %s"), cmd);
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0cd2c7314d..ff94cf5944 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -49,6 +49,7 @@
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sign.h"
@@ -4187,6 +4188,23 @@ bool garbage_collect(bool testing)
garbage_collect_at_exit = false;
}
+ // The execution stack can grow big, limit the size.
+ if (exestack.ga_maxlen - exestack.ga_len > 500) {
+ // Keep 150% of the current size, with a minimum of the growth size.
+ int n = exestack.ga_len / 2;
+ if (n < exestack.ga_growsize) {
+ n = exestack.ga_growsize;
+ }
+
+ // Don't make it bigger though.
+ if (exestack.ga_len + n < exestack.ga_maxlen) {
+ size_t new_len = (size_t)exestack.ga_itemsize * (size_t)(exestack.ga_len + n);
+ char *pp = xrealloc(exestack.ga_data, new_len);
+ exestack.ga_maxlen = exestack.ga_len + n;
+ exestack.ga_data = pp;
+ }
+ }
+
// We advance by two (COPYID_INC) because we add one for items referenced
// through previous_funccal.
const int copyID = get_copyID();
@@ -8613,8 +8631,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
struct caller_scope saved_provider_caller_scope = provider_caller_scope;
provider_caller_scope = (struct caller_scope) {
.script_ctx = current_sctx,
- .sourcing_name = sourcing_name,
- .sourcing_lnum = sourcing_lnum,
+ .es_entry = ((estack_T *)exestack.ga_data)[exestack.ga_len - 1],
.autocmd_fname = autocmd_fname,
.autocmd_match = autocmd_match,
.autocmd_bufnr = autocmd_bufnr,
@@ -8713,8 +8730,8 @@ bool eval_has_provider(const char *feat)
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
void eval_fmt_source_name_line(char *buf, size_t bufsize)
{
- if (sourcing_name) {
- snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum);
+ if (SOURCING_NAME) {
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR, SOURCING_NAME, SOURCING_LNUM);
} else {
snprintf(buf, bufsize, "?");
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index e13046ef82..fe3c0f825e 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -62,6 +62,7 @@
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
@@ -7317,8 +7318,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
sctx_T save_current_sctx;
- char *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
- linenr_T save_sourcing_lnum;
+ char *save_autocmd_fname, *save_autocmd_match;
int save_autocmd_bufnr;
funccal_entry_T funccal_entry;
@@ -7326,16 +7326,14 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// If this is called from a provider function, restore the scope
// information of the caller.
save_current_sctx = current_sctx;
- save_sourcing_name = sourcing_name;
- save_sourcing_lnum = sourcing_lnum;
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
save_autocmd_bufnr = autocmd_bufnr;
save_funccal(&funccal_entry);
current_sctx = provider_caller_scope.script_ctx;
- sourcing_name = provider_caller_scope.sourcing_name;
- sourcing_lnum = provider_caller_scope.sourcing_lnum;
+ ga_grow(&exestack, 1);
+ ((estack_T *)exestack.ga_data)[exestack.ga_len++] = provider_caller_scope.es_entry;
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
@@ -7352,8 +7350,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (l_provider_call_nesting) {
current_sctx = save_current_sctx;
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ exestack.ga_len--;
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
autocmd_bufnr = save_autocmd_bufnr;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index e411419a07..c4bc9f603b 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -355,6 +355,8 @@ struct ufunc {
///< used for s: variables
int uf_refcount; ///< reference count, see func_name_refcount()
funccall_T *uf_scoped; ///< l: local variables for closure
+ char_u *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
+ ///< "<SNR>" as a string, otherwise NULL
char_u uf_name[]; ///< Name of function (actual size equals name);
///< can start with <SNR>123_
///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 3a69e40acb..c527c70be0 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -23,6 +23,7 @@
#include "nvim/os/input.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -219,6 +220,17 @@ char_u *get_lambda_name(void)
return name;
}
+static void set_ufunc_name(ufunc_T *fp, char_u *name)
+{
+ STRCPY(fp->uf_name, name);
+
+ if (name[0] == K_SPECIAL) {
+ fp->uf_name_exp = xmalloc(STRLEN(name) + 3);
+ STRCPY(fp->uf_name_exp, "<SNR>");
+ STRCAT(fp->uf_name_exp, fp->uf_name + 3);
+ }
+}
+
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
@@ -297,7 +309,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
}
fp->uf_refcount = 1;
- STRCPY(fp->uf_name, name);
+ set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
@@ -319,7 +331,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
- fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len;
+ fp->uf_script_ctx.sc_lnum += SOURCING_LNUM - newlines.ga_len;
pt->pt_func = fp;
pt->pt_refcount = 1;
@@ -744,6 +756,7 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_def_args));
ga_clear_strings(&(fp->uf_lines));
+ XFREE_CLEAR(fp->uf_name_exp);
if (fp->uf_cb_free != NULL) {
fp->uf_cb_free(fp->uf_cb_state);
@@ -807,8 +820,6 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
linenr_T firstline, linenr_T lastline, dict_T *selfdict)
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
- char_u *save_sourcing_name;
- linenr_T save_sourcing_lnum;
bool using_sandbox = false;
funccall_T *fc;
int save_did_emsg;
@@ -998,73 +1009,49 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Don't redraw while executing the function.
RedrawingDisabled++;
- save_sourcing_name = (char_u *)sourcing_name;
- save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 1;
if (fp->uf_flags & FC_SANDBOX) {
using_sandbox = true;
sandbox++;
}
- // need space for new sourcing_name:
- // * save_sourcing_name
- // * "["number"].." or "function "
- // * "<SNR>" + fp->uf_name - 3
- // * terminating NUL
- size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name))
- + STRLEN(fp->uf_name) + 27;
- sourcing_name = xmalloc(len);
- {
- if (save_sourcing_name != NULL
- && STRNCMP(save_sourcing_name, "function ", 9) == 0) {
- vim_snprintf(sourcing_name,
- len,
- "%s[%" PRId64 "]..",
- save_sourcing_name,
- (int64_t)save_sourcing_lnum);
- } else {
- STRCPY(sourcing_name, "function ");
- }
- cat_func_name((char_u *)sourcing_name + STRLEN(sourcing_name), fp);
-
- if (p_verbose >= 12) {
- ++no_wait_return;
- verbose_enter_scroll();
+ estack_push_ufunc(fp, 1);
+ if (p_verbose >= 12) {
+ ++no_wait_return;
+ verbose_enter_scroll();
- smsg(_("calling %s"), sourcing_name);
- if (p_verbose >= 14) {
- msg_puts("(");
- for (int i = 0; i < argcount; i++) {
- if (i > 0) {
- msg_puts(", ");
- }
- 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++;
- char *tofree = encode_tv2string(&argvars[i], NULL);
- emsg_off--;
- if (tofree != NULL) {
- char *s = tofree;
- char buf[MSG_BUF_LEN];
- if (vim_strsize(s) > MSG_BUF_CLEN) {
- trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
- s = buf;
- }
- msg_puts(s);
- xfree(tofree);
+ smsg(_("calling %s"), SOURCING_NAME);
+ if (p_verbose >= 14) {
+ msg_puts("(");
+ for (int i = 0; i < argcount; i++) {
+ if (i > 0) {
+ msg_puts(", ");
+ }
+ 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++;
+ char *tofree = encode_tv2string(&argvars[i], NULL);
+ emsg_off--;
+ if (tofree != NULL) {
+ char *s = tofree;
+ char buf[MSG_BUF_LEN];
+ if (vim_strsize(s) > MSG_BUF_CLEN) {
+ trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
+ s = buf;
}
+ msg_puts(s);
+ xfree(tofree);
}
}
- msg_puts(")");
}
- msg_puts("\n"); // don't overwrite this either
-
- verbose_leave_scroll();
- --no_wait_return;
+ msg_puts(")");
}
+ msg_puts("\n"); // don't overwrite this either
+
+ verbose_leave_scroll();
+ --no_wait_return;
}
const bool do_profiling_yes = do_profiling == PROF_YES;
@@ -1148,10 +1135,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
verbose_enter_scroll();
if (aborting()) {
- smsg(_("%s aborted"), sourcing_name);
+ smsg(_("%s aborted"), SOURCING_NAME);
} else if (fc->rettv->v_type == VAR_NUMBER) {
smsg(_("%s returning #%" PRId64 ""),
- sourcing_name, (int64_t)fc->rettv->vval.v_number);
+ SOURCING_NAME, (int64_t)fc->rettv->vval.v_number);
} else {
char buf[MSG_BUF_LEN];
@@ -1167,7 +1154,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
s = buf;
}
- smsg(_("%s returning %s"), sourcing_name, s);
+ smsg(_("%s returning %s"), SOURCING_NAME, s);
xfree(tofree);
}
}
@@ -1177,9 +1164,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
--no_wait_return;
}
- xfree(sourcing_name);
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
current_sctx = save_current_sctx;
if (do_profiling_yes) {
script_prof_restore(&wait_start);
@@ -1188,15 +1173,15 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
sandbox--;
}
- if (p_verbose >= 12 && sourcing_name != NULL) {
- ++no_wait_return;
+ if (p_verbose >= 12 && SOURCING_NAME != NULL) {
+ no_wait_return++;
verbose_enter_scroll();
- smsg(_("continuing in %s"), sourcing_name);
+ smsg(_("continuing in %s"), SOURCING_NAME);
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- --no_wait_return;
+ no_wait_return--;
}
did_emsg |= save_did_emsg;
@@ -1637,9 +1622,8 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
msg_puts(" ");
}
msg_puts(force ? "function! " : "function ");
- if (fp->uf_name[0] == K_SPECIAL) {
- msg_puts_attr("<SNR>", HL_ATTR(HLF_8));
- msg_puts((const char *)fp->uf_name + 3);
+ if (fp->uf_name_exp != NULL) {
+ msg_puts((const char *)fp->uf_name_exp);
} else {
msg_puts((const char *)fp->uf_name);
}
@@ -2196,7 +2180,7 @@ void ex_function(exarg_T *eap)
}
// Save the starting line number.
- sourcing_lnum_top = sourcing_lnum;
+ sourcing_lnum_top = SOURCING_LNUM;
indent = 2;
nesting = 0;
@@ -2238,10 +2222,10 @@ void ex_function(exarg_T *eap)
ui_ext_cmdline_block_append((size_t)indent, (const char *)theline);
}
- // Detect line continuation: sourcing_lnum increased more than one.
+ // Detect line continuation: SOURCING_LNUM increased more than one.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
- if (sourcing_lnum < sourcing_lnum_off) {
- sourcing_lnum_off -= sourcing_lnum;
+ if (SOURCING_LNUM < sourcing_lnum_off) {
+ sourcing_lnum_off -= SOURCING_LNUM;
} else {
sourcing_lnum_off = 0;
}
@@ -2458,9 +2442,12 @@ void ex_function(exarg_T *eap)
fp = NULL;
overwrite = true;
} else {
- // redefine existing function
+ char_u *exp_name = fp->uf_name_exp;
+ // redefine existing function, keep the expanded name
XFREE_CLEAR(name);
+ fp->uf_name_exp = NULL;
func_clear_items(fp);
+ fp->uf_name_exp = exp_name;
fp->uf_profiling = false;
fp->uf_prof_initialized = false;
}
@@ -2499,13 +2486,12 @@ void ex_function(exarg_T *eap)
// Check that the autoload name matches the script name.
int j = FAIL;
- if (sourcing_name != NULL) {
+ if (SOURCING_NAME != NULL) {
scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
p = (char_u *)vim_strchr((char *)scriptname, '/');
plen = (int)STRLEN(p);
- slen = (int)STRLEN(sourcing_name);
- if (slen > plen && FNAMECMP(p,
- sourcing_name + slen - plen) == 0) {
+ slen = (int)STRLEN(SOURCING_NAME);
+ if (slen > plen && FNAMECMP(p, SOURCING_NAME + slen - plen) == 0) {
j = OK;
}
xfree(scriptname);
@@ -2540,7 +2526,7 @@ void ex_function(exarg_T *eap)
}
// insert the new function in the function list
- STRCPY(fp->uf_name, name);
+ set_ufunc_name(fp, name);
if (overwrite) {
hi = hash_find(&func_hashtab, (char *)name);
hi->hi_key = UF2HIKEY(fp);
@@ -3148,8 +3134,7 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
// If breakpoints have been added/deleted need to check for it.
if (fcp->dbg_tick != debug_tick) {
- fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name,
- sourcing_lnum);
+ fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -3170,7 +3155,7 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
retval = NULL;
} else {
retval = (char_u *)xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
- sourcing_lnum = fcp->linenr;
+ SOURCING_LNUM = fcp->linenr;
if (do_profiling == PROF_YES) {
func_line_start(cookie);
}
@@ -3178,11 +3163,10 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
}
// Did we encounter a breakpoint?
- if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) {
- dbg_breakpoint(fp->uf_name, sourcing_lnum);
+ if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint(fp->uf_name, SOURCING_LNUM);
// Find next breakpoint.
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name,
- sourcing_lnum);
+ fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index a681b3c693..5ea6a224d6 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -373,7 +373,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
breakpoint = func_breakpoint(real_cookie);
dbg_tick = func_dbg_tick(real_cookie);
} else if (getline_equal(fgetline, cookie, getsourceline)) {
- fname = sourcing_name;
+ fname = SOURCING_NAME;
breakpoint = source_breakpoint(real_cookie);
dbg_tick = source_dbg_tick(real_cookie);
}
@@ -466,20 +466,19 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (breakpoint != NULL && dbg_tick != NULL
&& *dbg_tick != debug_tick) {
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname, sourcing_lnum);
+ (char_u *)fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line;
- sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
+ SOURCING_LNUM = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
// Did we encounter a breakpoint?
- if (breakpoint != NULL && *breakpoint != 0
- && *breakpoint <= sourcing_lnum) {
- dbg_breakpoint((char_u *)fname, sourcing_lnum);
+ if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint((char_u *)fname, SOURCING_LNUM);
// Find next breakpoint.
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname, sourcing_lnum);
+ (char_u *)fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -582,8 +581,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
}
- if ((p_verbose >= 15 && sourcing_name != NULL) || p_verbose >= 16) {
- msg_verbose_cmd(sourcing_lnum, cmdline_copy);
+ if ((p_verbose >= 15 && SOURCING_NAME != NULL) || p_verbose >= 16) {
+ msg_verbose_cmd(SOURCING_LNUM, cmdline_copy);
}
/*
@@ -683,7 +682,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
*/
if (cstack.cs_looplevel == 0) {
if (!GA_EMPTY(&lines_ga)) {
- sourcing_lnum = ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
+ SOURCING_LNUM = ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD);
}
current_line = 0;
@@ -800,8 +799,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// commands are executed.
if (current_exception) {
char *p = NULL;
- char *saved_sourcing_name;
- linenr_T saved_sourcing_lnum;
msglist_T *messages = NULL;
msglist_T *next;
@@ -826,10 +823,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
break;
}
- saved_sourcing_name = sourcing_name;
- saved_sourcing_lnum = sourcing_lnum;
- sourcing_name = current_exception->throw_name;
- sourcing_lnum = current_exception->throw_lnum;
+ estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
current_exception->throw_name = NULL;
discard_current_exception(); // uses IObuff if 'verbose'
@@ -849,9 +843,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
emsg(p);
xfree(p);
}
- xfree(sourcing_name);
- sourcing_name = saved_sourcing_name;
- sourcing_lnum = saved_sourcing_lnum;
+ xfree(SOURCING_NAME);
+ estack_pop();
} else if (got_int || (did_emsg && force_abort)) {
// On an interrupt or an aborting error not converted to an exception,
// disable the conversion of errors to exceptions. (Interrupts are not
@@ -979,7 +972,7 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
KeyTyped = false;
cp->current_line++;
wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
- sourcing_lnum = wp->lnum;
+ SOURCING_LNUM = wp->lnum;
return xstrdup(wp->line);
}
@@ -988,7 +981,7 @@ static void store_loop_line(garray_T *gap, char *line)
{
wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap);
p->line = xstrdup(line);
- p->lnum = sourcing_lnum;
+ p->lnum = SOURCING_LNUM;
}
/// If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals
@@ -7919,29 +7912,30 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
break;
case SPEC_SFILE: // file name for ":so" command
- result = sourcing_name;
+ result = estack_sfile();
if (result == NULL) {
*errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
return NULL;
}
+ resultbuf = result; // remember allocated string
break;
case SPEC_SLNUM: // line in file for ":so" command
- if (sourcing_name == NULL || sourcing_lnum == 0) {
+ if (SOURCING_NAME == NULL || SOURCING_LNUM == 0) {
*errormsg = _("E842: no line number to use for \"<slnum>\"");
return NULL;
}
- snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum);
+ snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, SOURCING_LNUM);
result = strbuf;
break;
case SPEC_SFLNUM: // line in script file
- if (current_sctx.sc_lnum + sourcing_lnum == 0) {
+ if (current_sctx.sc_lnum + SOURCING_LNUM == 0) {
*errormsg = _("E961: no line number to use for \"<sflnum>\"");
return NULL;
}
snprintf((char *)strbuf, sizeof(strbuf), "%" PRIdLINENR,
- current_sctx.sc_lnum + sourcing_lnum);
+ current_sctx.sc_lnum + SOURCING_LNUM);
result = strbuf;
break;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 9e029aa68a..66c782d12e 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -275,6 +275,11 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
(*msg_list)->throw_msg = tmsg;
}
}
+
+ // Get the source name and lnum now, it may change before
+ // reaching do_errthrow().
+ elem->sfile = estack_sfile();
+ elem->slnum = SOURCING_LNUM;
}
return true;
}
@@ -289,6 +294,7 @@ static void free_msglist(msglist_T *l)
while (messages != NULL) {
next = messages->next;
xfree(messages->msg);
+ xfree(messages->sfile);
xfree(messages);
messages = next;
}
@@ -478,8 +484,18 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
}
excp->type = type;
- excp->throw_name = xstrdup(sourcing_name == NULL ? "" : sourcing_name);
- excp->throw_lnum = sourcing_lnum;
+ if (type == ET_ERROR && ((msglist_T *)value)->sfile != NULL) {
+ msglist_T *entry = (msglist_T *)value;
+ excp->throw_name = entry->sfile;
+ entry->sfile = NULL;
+ excp->throw_lnum = entry->slnum;
+ } else {
+ excp->throw_name = estack_sfile();
+ if (excp->throw_name == NULL) {
+ excp->throw_name = xstrdup("");
+ }
+ excp->throw_lnum = SOURCING_LNUM;
+ }
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 75150d6da4..9da0c9ad12 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -38,11 +38,13 @@ enum {
/// A list of error messages that can be converted to an exception. "throw_msg"
/// is only set in the first element of the list. Usually, it points to the
/// original message stored in that element, but sometimes it points to a later
-/// message in the list. See cause_errthrow() below.
+/// message in the list. See cause_errthrow().
typedef struct msglist msglist_T;
struct msglist {
- char *msg; ///< original message
+ char *msg; ///< original message, allocated
char *throw_msg; ///< msg to throw: usually original one
+ char *sfile; ///< value from estack_sfile(), allocated
+ linenr_T slnum; ///< line number for "sfile"
msglist_T *next; ///< next of several messages in a row
};
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 199dd7e8e2..a3ef859276 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -12,6 +12,7 @@
#include "nvim/mbyte.h"
#include "nvim/menu.h"
#include "nvim/os/os_defs.h"
+#include "nvim/runtime.h"
#include "nvim/syntax_defs.h"
#include "nvim/types.h"
@@ -248,9 +249,6 @@ EXTERN int lines_left INIT(= -1); // lines left for listing
EXTERN int msg_no_more INIT(= false); // don't use more prompt, truncate
// messages
-EXTERN char *sourcing_name INIT(= NULL); // name of error message source
-EXTERN linenr_T sourcing_lnum INIT(= 0); // line number of the source file
-
EXTERN int ex_nesting_level INIT(= 0); // nesting level
EXTERN int debug_break_level INIT(= -1); // break below this level
EXTERN bool debug_did_msg INIT(= false); // did "debug mode" message
@@ -348,8 +346,8 @@ EXTERN bool did_source_packages INIT(= false);
// provider function call
EXTERN struct caller_scope {
sctx_T script_ctx;
- char *sourcing_name, *autocmd_fname, *autocmd_match;
- linenr_T sourcing_lnum;
+ estack_T es_entry;
+ char *autocmd_fname, *autocmd_match;
int autocmd_bufnr;
void *funccalp;
} provider_caller_scope;
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index 42db85b3eb..d79283d3e3 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -691,12 +691,12 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_cleared = false;
g->sg_link = link_id;
g->sg_script_ctx = current_sctx;
- g->sg_script_ctx.sc_lnum += sourcing_lnum;
+ g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
g->sg_set |= SG_LINK;
if (is_default) {
g->sg_deflink = link_id;
g->sg_deflink_sctx = current_sctx;
- g->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
}
return;
}
@@ -735,7 +735,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_blend = attrs.hl_blend;
g->sg_script_ctx = current_sctx;
- g->sg_script_ctx.sc_lnum += sourcing_lnum;
+ g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
g->sg_attr = hl_get_syn_attr(0, id, attrs);
@@ -863,7 +863,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
hlgroup->sg_deflink = to_id;
hlgroup->sg_deflink_sctx = current_sctx;
- hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hlgroup->sg_deflink_sctx);
}
}
@@ -873,7 +873,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// for the group, unless '!' is used
if (to_id > 0 && !forceit && !init
&& hl_has_settings(from_id - 1, dodefault)) {
- if (sourcing_name == NULL && !dodefault) {
+ if (SOURCING_NAME == NULL && !dodefault) {
emsg(_("E414: group has settings, highlight link ignored"));
}
} else if (hlgroup->sg_link != to_id
@@ -884,7 +884,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
hlgroup->sg_link = to_id;
hlgroup->sg_script_ctx = current_sctx;
- hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum;
+ hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hlgroup->sg_script_ctx);
hlgroup->sg_cleared = false;
redraw_all_later(SOME_VALID);
@@ -1274,7 +1274,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
set_hl_attr(idx);
}
hl_table[idx].sg_script_ctx = current_sctx;
- hl_table[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
+ hl_table[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hl_table[idx].sg_script_ctx);
// Only call highlight_changed() once, after a sequence of highlight
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 2ce564c011..2e5b411fad 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -37,6 +37,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
#include "nvim/profile.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
@@ -1313,12 +1314,11 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name
int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
{
- const linenr_T save_sourcing_lnum = sourcing_lnum;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, NULL, 0);
garray_T ga;
char_u *line = NULL;
@@ -1331,7 +1331,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
size_t len = strlen(code);
nlua_typval_exec(code, len, name, NULL, 0, false, NULL);
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
current_sctx = save_current_sctx;
ga_clear_strings(&ga);
xfree(code);
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 378ca6ba71..4dacf6948f 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -60,6 +60,7 @@
#include "nvim/popupmnu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
@@ -159,6 +160,7 @@ bool event_teardown(void)
void early_init(mparm_T *paramp)
{
env_init();
+ estack_init();
cmdline_init();
eval_init(); // init global variables
init_path(argv0 ? argv0 : "nvim");
@@ -1814,12 +1816,12 @@ static void exe_pre_commands(mparm_T *parmp)
if (cnt > 0) {
curwin->w_cursor.lnum = 0; // just in case..
- sourcing_name = _("pre-vimrc command line");
+ estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
current_sctx.sc_sid = SID_CMDARG;
for (i = 0; i < cnt; i++) {
do_cmdline_cmd(cmds[i]);
}
- sourcing_name = NULL;
+ estack_pop();
current_sctx.sc_sid = 0;
TIME_MSG("--cmd commands");
}
@@ -1841,7 +1843,7 @@ static void exe_commands(mparm_T *parmp)
if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) {
curwin->w_cursor.lnum = 0;
}
- sourcing_name = "command line";
+ estack_push(ETYPE_ARGS, "command line", 0);
current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) {
@@ -1850,7 +1852,7 @@ static void exe_commands(mparm_T *parmp)
xfree(parmp->commands[i]);
}
}
- sourcing_name = NULL;
+ estack_pop();
current_sctx.sc_sid = 0;
if (curwin->w_cursor.lnum == 0) {
curwin->w_cursor.lnum = 1;
@@ -2059,17 +2061,14 @@ static int execute_env(char *env)
{
const char *initstr = os_getenv(env);
if (initstr != NULL) {
- char_u *save_sourcing_name = (char_u *)sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
- sourcing_name = env;
- sourcing_lnum = 0;
+ estack_push(ETYPE_ENV, env, 0);
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd((char *)initstr);
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+
+ estack_pop();
current_sctx = save_current_sctx;
return OK;
}
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index a3d8587a97..64a798a27b 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -29,6 +29,7 @@
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -469,7 +470,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
mp->m_script_ctx.sc_lnum = lnum;
} else {
mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&mp->m_script_ctx);
}
mp->m_desc = NULL;
@@ -776,7 +777,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mp->m_expr = args->expr;
mp->m_replace_keycodes = args->replace_keycodes;
mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&mp->m_script_ctx);
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
diff --git a/src/nvim/message.c b/src/nvim/message.c
index adab7c1c7b..cdbd7369b3 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -38,6 +38,7 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -524,7 +525,7 @@ int smsg_attr_keep(int attr, const char *s, ...)
* isn't printed each time when it didn't change.
*/
static int last_sourcing_lnum = 0;
-static char_u *last_sourcing_name = NULL;
+static char *last_sourcing_name = NULL;
/// Reset the last used sourcing name/lnum. Makes sure it is displayed again
/// for the next error message;
@@ -534,16 +535,16 @@ void reset_last_sourcing(void)
last_sourcing_lnum = 0;
}
-/// @return TRUE if "sourcing_name" differs from "last_sourcing_name".
-static int other_sourcing_name(void)
+/// @return true if "SOURCING_NAME" differs from "last_sourcing_name".
+static bool other_sourcing_name(void)
{
- if (sourcing_name != NULL) {
+ if (SOURCING_NAME != NULL) {
if (last_sourcing_name != NULL) {
- return STRCMP(sourcing_name, last_sourcing_name) != 0;
+ return strcmp(SOURCING_NAME, last_sourcing_name) != 0;
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Get the message about the source, as used for an error message
@@ -553,11 +554,19 @@ static int other_sourcing_name(void)
static char *get_emsg_source(void)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (sourcing_name != NULL && other_sourcing_name()) {
+ if (SOURCING_NAME != NULL && other_sourcing_name()) {
+ char *sname = estack_sfile();
+ char *tofree = sname;
+
+ if (sname == NULL) {
+ sname = SOURCING_NAME;
+ }
+
const char *const p = _("Error detected while processing %s:");
- const size_t buf_len = STRLEN(sourcing_name) + strlen(p) + 1;
+ const size_t buf_len = STRLEN(sname) + strlen(p) + 1;
char *const buf = xmalloc(buf_len);
- snprintf(buf, buf_len, p, sourcing_name);
+ snprintf(buf, buf_len, p, sname);
+ xfree(tofree);
return buf;
}
return NULL;
@@ -572,13 +581,13 @@ static char *get_emsg_lnum(void)
{
// lnum is 0 when executing a command from the command line
// argument, we don't want a line number then
- if (sourcing_name != NULL
- && (other_sourcing_name() || sourcing_lnum != last_sourcing_lnum)
- && sourcing_lnum != 0) {
+ if (SOURCING_NAME != NULL
+ && (other_sourcing_name() || SOURCING_LNUM != last_sourcing_lnum)
+ && SOURCING_LNUM != 0) {
const char *const p = _("line %4ld:");
const size_t buf_len = 20 + strlen(p);
char *const buf = xmalloc(buf_len);
- snprintf(buf, buf_len, p, (long)sourcing_lnum);
+ snprintf(buf, buf_len, p, (long)SOURCING_LNUM);
return buf;
}
return NULL;
@@ -599,16 +608,16 @@ void msg_source(int attr)
if (p != NULL) {
msg_attr(p, HL_ATTR(HLF_N));
xfree(p);
- last_sourcing_lnum = sourcing_lnum; // only once for each line
+ last_sourcing_lnum = SOURCING_LNUM; // only once for each line
}
// remember the last sourcing name printed, also when it's empty
- if (sourcing_name == NULL || other_sourcing_name()) {
+ if (SOURCING_NAME == NULL || other_sourcing_name()) {
xfree(last_sourcing_name);
- if (sourcing_name == NULL) {
+ if (SOURCING_NAME == NULL) {
last_sourcing_name = NULL;
} else {
- last_sourcing_name = vim_strsave((char_u *)sourcing_name);
+ last_sourcing_name = xstrdup(SOURCING_NAME);
}
}
--no_wait_return;
@@ -686,9 +695,9 @@ static bool emsg_multiline(const char *s, bool multiline)
}
// Log (silent) errors as debug messages.
- if (sourcing_name != NULL && sourcing_lnum != 0) {
+ if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) {
DLOG("(:silent) %s (%s (line %ld))",
- s, sourcing_name, (long)sourcing_lnum);
+ s, SOURCING_NAME, (long)SOURCING_LNUM);
} else {
DLOG("(:silent) %s", s);
}
@@ -697,8 +706,8 @@ static bool emsg_multiline(const char *s, bool multiline)
}
// Log editor errors as INFO.
- if (sourcing_name != NULL && sourcing_lnum != 0) {
- ILOG("%s (%s (line %ld))", s, sourcing_name, (long)sourcing_lnum);
+ if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) {
+ ILOG("%s (%s (line %ld))", s, SOURCING_NAME, (long)SOURCING_LNUM);
} else {
ILOG("%s", s);
}
@@ -2457,7 +2466,7 @@ void msg_reset_scroll(void)
static void inc_msg_scrolled(void)
{
if (*get_vim_var_str(VV_SCROLLSTART) == NUL) {
- char *p = sourcing_name;
+ char *p = SOURCING_NAME;
char *tofree = NULL;
// v:scrollstart is empty, set it to the script/function name and line
@@ -2468,7 +2477,7 @@ static void inc_msg_scrolled(void)
size_t len = strlen(p) + 40;
tofree = xmalloc(len);
vim_snprintf(tofree, len, _("%s line %" PRId64),
- p, (int64_t)sourcing_lnum);
+ p, (int64_t)SOURCING_LNUM);
p = tofree;
}
set_vim_var_string(VV_SCROLLSTART, p, -1);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 916c460ed6..5e88ed98e8 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -3996,7 +3996,7 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
.script_ctx = {
script_ctx.sc_sid,
script_ctx.sc_seq,
- script_ctx.sc_lnum + sourcing_lnum
+ script_ctx.sc_lnum + SOURCING_LNUM
},
current_channel_id
};
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index b05ba0d8f8..d4f3756f4d 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -532,9 +532,8 @@ void func_line_start(void *cookie)
funccall_T *fcp = (funccall_T *)cookie;
ufunc_T *fp = fcp->func;
- if (fp->uf_profiling && sourcing_lnum >= 1
- && sourcing_lnum <= fp->uf_lines.ga_len) {
- fp->uf_tml_idx = sourcing_lnum - 1;
+ if (fp->uf_profiling && SOURCING_LNUM >= 1 && SOURCING_LNUM <= fp->uf_lines.ga_len) {
+ fp->uf_tml_idx = SOURCING_LNUM - 1;
// Skip continuation lines.
while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
fp->uf_tml_idx--;
@@ -798,11 +797,11 @@ void script_line_start(void)
return;
}
si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && sourcing_lnum >= 1) {
+ if (si->sn_prof_on && SOURCING_LNUM >= 1) {
// Grow the array before starting the timer, so that the time spent
// here isn't counted.
- (void)ga_grow(&si->sn_prl_ga, sourcing_lnum - si->sn_prl_ga.ga_len);
- si->sn_prl_idx = sourcing_lnum - 1;
+ (void)ga_grow(&si->sn_prl_ga, SOURCING_LNUM - si->sn_prl_ga.ga_len);
+ si->sn_prl_idx = SOURCING_LNUM - 1;
while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
// Zero counters for a line that was not used before.
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 89848b43bd..8299f8ec45 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -50,8 +50,94 @@ struct source_cookie {
# include "runtime.c.generated.h"
#endif
+garray_T exestack = { 0, 0, sizeof(estack_T), 50, NULL };
garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
+/// Initialize the execution stack.
+void estack_init(void)
+{
+ ga_grow(&exestack, 10);
+ estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = ETYPE_TOP;
+ entry->es_name = NULL;
+ entry->es_lnum = 0;
+ entry->es_info.ufunc = NULL;
+ exestack.ga_len++;
+}
+
+/// Add an item to the execution stack.
+/// @return the new entry
+estack_T *estack_push(etype_T type, char *name, linenr_T lnum)
+{
+ ga_grow(&exestack, 1);
+ estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = type;
+ entry->es_name = name;
+ entry->es_lnum = lnum;
+ entry->es_info.ufunc = NULL;
+ exestack.ga_len++;
+ return entry;
+}
+
+/// Add a user function to the execution stack.
+void estack_push_ufunc(ufunc_T *ufunc, linenr_T lnum)
+{
+ estack_T *entry = estack_push(ETYPE_UFUNC,
+ (char *)(ufunc->uf_name_exp != NULL
+ ? ufunc->uf_name_exp : ufunc->uf_name),
+ lnum);
+ if (entry != NULL) {
+ entry->es_info.ufunc = ufunc;
+ }
+}
+
+/// Take an item off of the execution stack.
+void estack_pop(void)
+{
+ if (exestack.ga_len > 1) {
+ exestack.ga_len--;
+ }
+}
+
+/// Get the current value for <sfile> in allocated memory.
+char *estack_sfile(void)
+{
+ estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+ if (entry->es_name == NULL) {
+ return NULL;
+ }
+ if (entry->es_info.ufunc == NULL) {
+ return xstrdup(entry->es_name);
+ }
+
+ // For a function we compose the call stack, as it was done in the past:
+ // "function One[123]..Two[456]..Three"
+ size_t len = STRLEN(entry->es_name) + 10;
+ int idx;
+ for (idx = exestack.ga_len - 2; idx >= 0; idx--) {
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ if (entry->es_name == NULL || entry->es_info.ufunc == NULL) {
+ idx++;
+ break;
+ }
+ len += STRLEN(entry->es_name) + 15;
+ }
+
+ char *res = (char *)xmalloc(len);
+ STRCPY(res, "function ");
+ size_t done;
+ while (idx < exestack.ga_len - 1) {
+ done = STRLEN(res);
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ vim_snprintf(res + done, len - done, "%s[%" PRIdLINENR "]..", entry->es_name, entry->es_lnum);
+ idx++;
+ }
+ done = STRLEN(res);
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ vim_snprintf(res + done, len - done, "%s", entry->es_name);
+ return res;
+}
+
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;
@@ -1645,18 +1731,19 @@ scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
{
- char *save_sourcing_name = sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
+ char *save_sourcing_name = SOURCING_NAME;
+ linenr_T save_sourcing_lnum = SOURCING_LNUM;
char sourcing_name_buf[256];
+ char *sname;
if (save_sourcing_name == NULL) {
- sourcing_name = (char *)traceback_name;
+ sname = (char *)traceback_name;
} else {
snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf),
"%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
save_sourcing_lnum);
- sourcing_name = sourcing_name_buf; // -V507 reassigned below, before return.
+ sname = sourcing_name_buf;
}
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, sname, 0);
const sctx_T save_current_sctx = current_sctx;
if (current_sctx.sc_sid != SID_LUA) {
@@ -1668,8 +1755,7 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char
save_funccal(&entry);
int retval = do_cmdline(NULL, fgetline, cookie,
DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
- sourcing_lnum = save_sourcing_lnum;
- sourcing_name = save_sourcing_name;
+ estack_pop();
current_sctx = save_current_sctx;
restore_funccal();
return retval;
@@ -1736,8 +1822,6 @@ int do_source_str(const char *cmd, const char *traceback_name)
int do_source(char *fname, int check_other, int is_vimrc)
{
struct source_cookie cookie;
- char *save_sourcing_name;
- linenr_T save_sourcing_lnum;
char *p;
char *fname_exp;
uint8_t *firstline = NULL;
@@ -1791,11 +1875,11 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (cookie.fp == NULL) {
if (p_verbose > 1) {
verbose_enter();
- if (sourcing_name == NULL) {
+ if (SOURCING_NAME == NULL) {
smsg(_("could not source \"%s\""), fname);
} else {
smsg(_("line %" PRId64 ": could not source \"%s\""),
- (int64_t)sourcing_lnum, fname);
+ (int64_t)SOURCING_LNUM, fname);
}
verbose_leave();
}
@@ -1807,11 +1891,10 @@ int do_source(char *fname, int check_other, int is_vimrc)
// - For a vimrc file, may want to call vimrc_found().
if (p_verbose > 1) {
verbose_enter();
- if (sourcing_name == NULL) {
+ if (SOURCING_NAME == NULL) {
smsg(_("sourcing \"%s\""), fname);
} else {
- smsg(_("line %" PRId64 ": sourcing \"%s\""),
- (int64_t)sourcing_lnum, fname);
+ smsg(_("line %" PRId64 ": sourcing \"%s\""), (int64_t)SOURCING_LNUM, fname);
}
verbose_leave();
}
@@ -1841,10 +1924,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.level = ex_nesting_level;
// Keep the sourcing name/lnum, for recursive calls.
- save_sourcing_name = sourcing_name;
- sourcing_name = fname_exp;
- save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, fname_exp, 0);
// start measuring script load time if --startuptime was passed and
// time_fd was successfully opened afterwards.
@@ -1901,14 +1981,13 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (path_with_extension((const char *)fname_exp, "lua")) {
const sctx_T current_sctx_backup = current_sctx;
- const linenr_T sourcing_lnum_backup = sourcing_lnum;
current_sctx.sc_sid = SID_LUA;
current_sctx.sc_lnum = 0;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, NULL, 0);
// Source the file as lua
nlua_exec_file((const char *)fname_exp);
current_sctx = current_sctx_backup;
- sourcing_lnum = sourcing_lnum_backup;
+ estack_pop();
} else {
// Call do_cmdline, which will call getsourceline() to get the lines.
do_cmdline((char *)firstline, getsourceline, (void *)&cookie,
@@ -1931,13 +2010,12 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (got_int) {
emsg(_(e_interr));
}
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
if (p_verbose > 1) {
verbose_enter();
smsg(_("finished sourcing %s"), fname);
- if (sourcing_name != NULL) {
- smsg(_("continuing in %s"), sourcing_name);
+ if (SOURCING_NAME != NULL) {
+ smsg(_("continuing in %s"), SOURCING_NAME);
}
verbose_leave();
}
@@ -2110,7 +2188,7 @@ linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie)
{
return fgetline == getsourceline
? ((struct source_cookie *)cookie)->sourcing_lnum
- : sourcing_lnum;
+ : SOURCING_LNUM;
}
/// Get one full line from a sourced file.
@@ -2126,14 +2204,14 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
// If breakpoints have been added/deleted need to check for it.
if (sp->dbg_tick < debug_tick) {
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
script_line_end();
}
// Set the current sourcing line number.
- sourcing_lnum = sp->sourcing_lnum + 1;
+ SOURCING_LNUM = sp->sourcing_lnum + 1;
// Get current line. If there is a read-ahead line, use it, otherwise get
// one now.
if (sp->finished) {
@@ -2191,10 +2269,10 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
}
// Did we encounter a breakpoint?
- if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) {
- dbg_breakpoint((char_u *)sp->fname, sourcing_lnum);
+ if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint((char_u *)sp->fname, SOURCING_LNUM);
// Find next breakpoint.
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
+ sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick;
}
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index fb12029693..6f9f31c9c4 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -3,7 +3,44 @@
#include <stdbool.h>
+#include "nvim/autocmd.h"
+#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_eval_defs.h"
+
+typedef enum {
+ ETYPE_TOP, ///< toplevel
+ ETYPE_SCRIPT, ///< sourcing script, use es_info.sctx
+ ETYPE_UFUNC, ///< user function, use es_info.ufunc
+ ETYPE_AUCMD, ///< autocomand, use es_info.aucmd
+ ETYPE_MODELINE, ///< modeline, use es_info.sctx
+ ETYPE_EXCEPT, ///< exception, use es_info.exception
+ ETYPE_ARGS, ///< command line argument
+ ETYPE_ENV, ///< environment variable
+ ETYPE_INTERNAL, ///< internal operation
+ ETYPE_SPELL, ///< loading spell file
+} etype_T;
+
+/// Entry in the execution stack "exestack".
+typedef struct {
+ linenr_T es_lnum; ///< replaces "sourcing_lnum"
+ char *es_name; ///< replaces "sourcing_name"
+ etype_T es_type;
+ union {
+ sctx_T *sctx; ///< script and modeline info
+ ufunc_T *ufunc; ///< function info
+ AutoPatCmd *aucmd; ///< autocommand info
+ except_T *except; ///< exception info
+ } es_info;
+} estack_T;
+
+/// Stack of execution contexts. Each entry is an estack_T.
+/// Current context is at ga_len - 1.
+extern garray_T exestack;
+/// name of error message source
+#define SOURCING_NAME (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_name)
+/// line number in the message source or zero
+#define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum)
typedef struct scriptitem_S {
char_u *sn_name;
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 36df7695ff..58a66f0635 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -242,6 +242,7 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/spell_defs.h"
@@ -576,11 +577,10 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
char_u *p;
int n;
int len;
- char_u *save_sourcing_name = (char_u *)sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
slang_T *lp = NULL;
int c = 0;
int res;
+ bool did_estack_push = false;
fd = os_fopen((char *)fname, "r");
if (fd == NULL) {
@@ -612,8 +612,8 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
}
// Set sourcing_name, so that error messages mention the file name.
- sourcing_name = (char *)fname;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SPELL, (char *)fname, 0);
+ did_estack_push = true;
// <HEADER>: <fileID>
const int scms_ret = spell_check_magic_string(fd);
@@ -809,8 +809,9 @@ endOK:
if (fd != NULL) {
fclose(fd);
}
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ if (did_estack_push) {
+ estack_pop();
+ }
return lp;
}
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1c2f86a584..89e3c9927b 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -2614,6 +2614,28 @@ func Test_BufWrite_lockmarks()
call delete('Xtest2')
endfunc
+func Test_FileType_spell()
+ if !isdirectory('/tmp')
+ throw "Skipped: requires /tmp directory"
+ endif
+
+ " this was crashing with an invalid free()
+ setglobal spellfile=/tmp/en.utf-8.add
+ augroup crash
+ autocmd!
+ autocmd BufNewFile,BufReadPost crashfile setf somefiletype
+ autocmd BufNewFile,BufReadPost crashfile set ft=anotherfiletype
+ autocmd FileType anotherfiletype setlocal spell
+ augroup END
+ func! NoCrash() abort
+ edit /tmp/crashfile
+ endfunc
+ call NoCrash()
+
+ au! crash
+ setglobal spellfile=
+endfunc
+
" Test closing a window or editing another buffer from a FileChangedRO handler
" in a readonly buffer
func Test_FileChangedRO_winclose()
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index d16783cf31..0f204cdd0c 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1573,6 +1573,23 @@ func Test_script_local_func()
enew! | close
endfunc
+func Test_script_expand_sfile()
+ let lines =<< trim END
+ func s:snr()
+ return expand('<sfile>')
+ endfunc
+ let g:result = s:snr()
+ END
+ call writefile(lines, 'Xexpand')
+ source Xexpand
+ call assert_match('<SNR>\d\+_snr', g:result)
+ source Xexpand
+ call assert_match('<SNR>\d\+_snr', g:result)
+
+ call delete('Xexpand')
+ unlet g:result
+endfunc
+
func Test_compound_assignment_operators()
" Test for number
let x = 1
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 69b687e44f..de6d445ba3 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -7,6 +7,7 @@
#include "nvim/eval/encode.h"
#include "nvim/ex_docmd.h"
#include "nvim/os/os.h"
+#include "nvim/runtime.h"
#include "nvim/testing.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -17,21 +18,23 @@
static void prepare_assert_error(garray_T *gap)
{
char buf[NUMBUFLEN];
+ char *sname = estack_sfile();
ga_init(gap, 1, 100);
- if (sourcing_name != NULL) {
- ga_concat(gap, (char *)sourcing_name);
- if (sourcing_lnum > 0) {
+ if (sname != NULL) {
+ ga_concat(gap, sname);
+ if (SOURCING_LNUM > 0) {
ga_concat(gap, " ");
}
}
- if (sourcing_lnum > 0) {
- vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
+ if (SOURCING_LNUM > 0) {
+ vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)SOURCING_LNUM);
ga_concat(gap, buf);
}
- if (sourcing_name != NULL || sourcing_lnum > 0) {
+ if (sname != NULL || SOURCING_LNUM > 0) {
ga_concat(gap, ": ");
}
+ xfree(sname);
}
/// Append "p[clen]" to "gap", escaping unprintable characters.
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index bf5faf7bc9..59b8d10200 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -17,6 +17,7 @@
#include "nvim/garray.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
+#include "nvim/runtime.h"
#include "nvim/usercmd.h"
#include "nvim/window.h"
@@ -168,7 +169,7 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
xp->xp_luaref = uc->uc_compl_luaref;
xp->xp_arg = (char *)uc->uc_compl_arg;
xp->xp_script_ctx = uc->uc_script_ctx;
- xp->xp_script_ctx.sc_lnum += sourcing_lnum;
+ xp->xp_script_ctx.sc_lnum += SOURCING_LNUM;
}
// Do not search for further abbreviations
// if this is an exact match.
@@ -890,7 +891,7 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt,
cmd->uc_def = def;
cmd->uc_compl = compl;
cmd->uc_script_ctx = current_sctx;
- cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
+ cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&cmd->uc_script_ctx);
cmd->uc_compl_arg = (char_u *)compl_arg;
cmd->uc_compl_luaref = compl_luaref;