aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt1
-rw-r--r--runtime/doc/cmdline.txt16
-rw-r--r--src/nvim/debugger.c6
-rw-r--r--src/nvim/ex_docmd.c8
-rw-r--r--src/nvim/ex_eval.c4
-rw-r--r--src/nvim/lua/executor.c2
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/runtime.c81
-rw-r--r--src/nvim/runtime.h7
-rw-r--r--src/nvim/testdir/test_expand_func.vim20
-rw-r--r--src/nvim/testing.c2
-rw-r--r--test/functional/ex_cmds/source_spec.lua28
12 files changed, 127 insertions, 50 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 291eea7263..0fc8a30c20 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -2010,6 +2010,7 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
a function
<SID> "<SNR>123_" where "123" is the
current script ID |<SID>|
+ <stack> call stack
<cword> word under the cursor
<cWORD> WORD under the cursor
<client> the {clientid} of the last received
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 87f1589ea1..29eff75bfa 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -879,12 +879,18 @@ Note: these are typed literally, they are not special keys!
*:<sfile>* *<sfile>*
<sfile> When executing a `:source` command, is replaced with the
file name of the sourced file. *E498*
- When executing a function, is replaced with:
- "function {function-name}[{lnum}]"
- function call nesting is indicated like this:
- "function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
+ When executing a function, is replaced with the call stack,
+ as with <stack> (this is for backwards compatibility, using
+ <stack> is preferred).
Note that filename-modifiers are useless when <sfile> is
- used inside a function.
+ not used inside a script.
+ *:<stack>* *<stack>*
+ <stack> is replaced with the call stack, using
+ "function {function-name}[{lnum}]" for a function line
+ and "script {file-name}[{lnum}]" for a script line, and
+ ".." in between items. E.g.:
+ "function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
+ If there is no call stack you get error *E489* .
*:<slnum>* *<slnum>*
<slnum> When executing a `:source` command, is replaced with the
line number. *E842*
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 76e1caff49..72d776d1e4 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -99,7 +99,7 @@ void do_debug(char_u *cmd)
xfree(debug_newval);
debug_newval = NULL;
}
- char *sname = estack_sfile();
+ char *sname = estack_sfile(ESTACK_NONE);
if (sname != NULL) {
msg(sname);
}
@@ -324,7 +324,7 @@ static void do_checkbacktracelevel(void)
debug_backtrace_level = 0;
msg(_("frame is zero"));
} else {
- char *sname = estack_sfile();
+ char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname);
if (debug_backtrace_level > max) {
@@ -337,7 +337,7 @@ static void do_checkbacktracelevel(void)
static void do_showbacktrace(char_u *cmd)
{
- char *sname = estack_sfile();
+ char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname);
if (sname != NULL) {
int i = 0;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5ea6a224d6..de62d90610 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7719,6 +7719,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
/// '<cexpr>' to C-expression under the cursor
/// '<cfile>' to path name under the cursor
/// '<sfile>' to sourced file name
+/// '<stack>' to call stack
/// '<slnum>' to sourced file line number
/// '<afile>' to file name for autocommand
/// '<abuf>' to buffer number for autocommand
@@ -7912,9 +7913,12 @@ 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 = estack_sfile();
+ case SPEC_STACK: // call stack
+ result = estack_sfile(spec_idx == SPEC_SFILE ? ESTACK_SFILE : ESTACK_STACK);
if (result == NULL) {
- *errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
+ *errormsg = spec_idx == SPEC_SFILE
+ ? _("E498: no :source file name to substitute for \"<sfile>\"")
+ : _("E489: no call stack to substitute for \"<stack>\"");
return NULL;
}
resultbuf = result; // remember allocated string
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 66c782d12e..c39bb16498 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -278,7 +278,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
// Get the source name and lnum now, it may change before
// reaching do_errthrow().
- elem->sfile = estack_sfile();
+ elem->sfile = estack_sfile(ESTACK_NONE);
elem->slnum = SOURCING_LNUM;
}
return true;
@@ -490,7 +490,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
entry->sfile = NULL;
excp->throw_lnum = entry->slnum;
} else {
- excp->throw_name = estack_sfile();
+ excp->throw_name = estack_sfile(ESTACK_NONE);
if (excp->throw_name == NULL) {
excp->throw_name = xstrdup("");
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 2e5b411fad..a86f23db8e 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1906,7 +1906,7 @@ void nlua_set_sctx(sctx_T *current)
break;
}
char *source_path = fix_fname(info->source + 1);
- get_current_script_id((char_u *)source_path, current);
+ get_current_script_id(&source_path, current);
xfree(source_path);
current->sc_lnum = info->currentline;
current->sc_seq = -1;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index c97e68207b..6910fc16ae 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -555,7 +555,7 @@ static char *get_emsg_source(void)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
if (SOURCING_NAME != NULL && other_sourcing_name()) {
- char *sname = estack_sfile();
+ char *sname = estack_sfile(ESTACK_NONE);
char *tofree = sname;
if (sname == NULL) {
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 8299f8ec45..4eb38c2c9e 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -100,42 +100,60 @@ void estack_pop(void)
}
/// Get the current value for <sfile> in allocated memory.
-char *estack_sfile(void)
+/// @param which ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>.
+char *estack_sfile(estack_arg_T which)
{
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) {
+ if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) {
+ if (entry->es_name == NULL) {
+ return NULL;
+ }
return xstrdup(entry->es_name);
}
+ // Give information about each stack entry up to the root.
// 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--) {
+ garray_T ga;
+ ga_init(&ga, sizeof(char), 100);
+ etype_T last_type = ETYPE_SCRIPT;
+ for (int idx = 0; idx < exestack.ga_len; idx++) {
entry = ((estack_T *)exestack.ga_data) + idx;
- if (entry->es_name == NULL || entry->es_info.ufunc == NULL) {
- idx++;
- break;
+ if (entry->es_name != NULL) {
+ size_t len = strlen(entry->es_name) + 15;
+ char *type_name = "";
+ if (entry->es_type != last_type) {
+ switch (entry->es_type) {
+ case ETYPE_SCRIPT:
+ type_name = "script "; break;
+ case ETYPE_UFUNC:
+ type_name = "function "; break;
+ default:
+ type_name = ""; break;
+ }
+ last_type = entry->es_type;
+ }
+ len += strlen(type_name);
+ ga_grow(&ga, (int)len);
+ linenr_T lnum = idx == exestack.ga_len - 1
+ ? which == ESTACK_STACK ? SOURCING_LNUM : 0
+ : entry->es_lnum;
+ char *dots = idx == exestack.ga_len - 1 ? "" : "..";
+ if (lnum == 0) {
+ // For the bottom entry of <sfile>: do not add the line number,
+ // it is used in <slnum>. Also leave it out when the number is
+ // not set.
+ vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s",
+ type_name, entry->es_name, dots);
+ } else {
+ vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%" PRIdLINENR "]%s",
+ type_name, entry->es_name, lnum, dots);
+ }
+ ga.ga_len += (int)strlen((char *)ga.ga_data + ga.ga_len);
}
- 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;
+ return (char *)ga.ga_data;
}
static bool runtime_search_path_valid = false;
@@ -1946,7 +1964,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
save_funccal(&funccalp_entry);
const sctx_T save_current_sctx = current_sctx;
- si = get_current_script_id((char_u *)fname_exp, &current_sctx);
+ si = get_current_script_id(&fname_exp, &current_sctx);
if (l_do_profiling == PROF_YES) {
bool forceit = false;
@@ -2059,9 +2077,9 @@ theend:
/// Check if fname was sourced before to finds its SID.
/// If it's new, generate a new SID.
///
-/// @param[in] fname file path of script
-/// @param[out] ret_sctx sctx of this script
-scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
+/// @param[in,out] fnamep pointer to file path of script
+/// @param[out] ret_sctx sctx of this script
+scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
{
static int last_current_SID_seq = 0;
@@ -2078,13 +2096,14 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
// - If a script is deleted and another script is written, with a
// different name, the inode may be re-used.
si = &SCRIPT_ITEM(script_sctx.sc_sid);
- if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) {
+ if (si->sn_name != NULL && FNAMECMP(si->sn_name, *fnamep) == 0) {
// Found it!
break;
}
}
if (script_sctx.sc_sid == 0) {
- si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid);
+ si = new_script_item(*fnamep, &script_sctx.sc_sid);
+ *fnamep = xstrdup((char *)si->sn_name);
}
if (ret_sctx != NULL) {
*ret_sctx = script_sctx;
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index 6f9f31c9c4..a255c6c096 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -42,6 +42,13 @@ extern garray_T exestack;
/// line number in the message source or zero
#define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum)
+/// Argument for estack_sfile().
+typedef enum {
+ ESTACK_NONE,
+ ESTACK_SFILE,
+ ESTACK_STACK,
+} estack_arg_T;
+
typedef struct scriptitem_S {
char_u *sn_name;
bool sn_prof_on; ///< true when script is/was profiled
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index b48c2e8a19..fc0f7619c4 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -37,17 +37,29 @@ func Test_expand_sflnum()
delcommand Flnum
endfunc
-func Test_expand_sfile()
+func Test_expand_sfile_and_stack()
call assert_match('test_expand_func\.vim$', s:sfile)
- call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
+ let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack'
+ call assert_match(expected .. '$', expand('<sfile>'))
+ call assert_match(expected .. '\[4\]' , expand('<stack>'))
" Call in script-local function
- call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
+ call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
" Call in command
command Sfile echo expand('<sfile>')
- call assert_match('^function .*\.\.Test_expand_sfile$', trim(execute('Sfile')))
+ call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$', trim(execute('Sfile')))
delcommand Sfile
+
+ " Use <stack> from sourced script.
+ let lines =<< trim END
+ " comment here
+ let g:stack_value = expand('<stack>')
+ END
+ call writefile(lines, 'Xstack')
+ source Xstack
+ call assert_match('\<Xstack\[2\]$', g:stack_value)
+ call delete('Xstack')
endfunc
func Test_expand_slnum()
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index de6d445ba3..e70e9f2cbd 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -18,7 +18,7 @@
static void prepare_assert_error(garray_T *gap)
{
char buf[NUMBUFLEN];
- char *sname = estack_sfile();
+ char *sname = estack_sfile(ESTACK_NONE);
ga_init(gap, 1, 100);
if (sname != NULL) {
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index 13a40fcc53..4bc3355e9e 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -13,6 +13,10 @@ local exec_lua = helpers.exec_lua
local eval = helpers.eval
local exec_capture = helpers.exec_capture
local neq = helpers.neq
+local matches = helpers.matches
+local iswin = helpers.iswin
+local mkdir = helpers.mkdir
+local rmdir = helpers.rmdir
describe(':source', function()
before_each(function()
@@ -39,6 +43,30 @@ describe(':source', function()
os.remove(test_file)
end)
+ it("changing 'shellslash' changes the result of expand()", function()
+ if not iswin() then
+ pending("'shellslash' only works on Windows")
+ return
+ end
+ mkdir('Xshellslash')
+ local script = [[
+ let g:result1 = expand('<stack>')
+ set shellslash
+ let g:result2 = expand('<stack>')
+ set noshellslash
+ let g:result3 = expand('<stack>')
+ ]]
+ write_file([[Xshellslash/Xexpand.vim]], script)
+
+ meths.set_option('shellslash', false)
+ command([[source Xshellslash/Xexpand.vim]])
+ matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result1'))
+ matches([[Xshellslash/Xexpand%.vim]], meths.get_var('result2'))
+ matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result3'))
+
+ rmdir('Xshellslash')
+ end)
+
it('current buffer', function()
insert([[
let a = 2