aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/builtin.txt2
-rw-r--r--runtime/doc/cmdline.txt8
-rw-r--r--src/nvim/ex_docmd.c29
-rw-r--r--src/nvim/runtime.c24
-rw-r--r--src/nvim/runtime.h1
-rw-r--r--src/nvim/testdir/test_expand.vim50
-rw-r--r--test/functional/ex_cmds/source_spec.lua4
7 files changed, 112 insertions, 6 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index a47eae8274..2bd0415dab 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -2013,6 +2013,8 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
a function
<SID> "<SNR>123_" where "123" is the
current script ID |<SID>|
+ <script> sourced script file, or script file
+ where the current function was defined
<stack> call stack
<cword> word under the cursor
<cWORD> WORD under the cursor
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 29eff75bfa..f19671e713 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -881,7 +881,7 @@ Note: these are typed literally, they are not special keys!
file name of the sourced file. *E498*
When executing a function, is replaced with the call stack,
as with <stack> (this is for backwards compatibility, using
- <stack> is preferred).
+ <stack> or <script> is preferred).
Note that filename-modifiers are useless when <sfile> is
not used inside a script.
*:<stack>* *<stack>*
@@ -891,6 +891,12 @@ Note: these are typed literally, they are not special keys!
".." in between items. E.g.:
"function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
If there is no call stack you get error *E489* .
+ *:<script>* *<script>*
+ <script> When executing a `:source` command, is replaced with the file
+ name of the sourced file. When executing a function, is
+ replaced with the file name of the script where it is
+ defined.
+ If the file name cannot be determined you get error *E1274* .
*:<slnum>* *<slnum>*
<slnum> When executing a `:source` command, is replaced with the
line number. *E842*
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5e8c7dbdda..ac2a65844f 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -94,6 +94,12 @@ static char e_ambiguous_use_of_user_defined_command[]
= N_("E464: Ambiguous use of user-defined command");
static char e_not_an_editor_command[]
= N_("E492: Not an editor command");
+static char e_no_source_file_name_to_substitute_for_sfile[]
+ = N_("E498: no :source file name to substitute for \"<sfile>\"");
+static char e_no_call_stack_to_substitute_for_stack[]
+ = N_("E489: no call stack to substitute for \"<stack>\"");
+static char e_no_script_file_name_to_substitute_for_script[]
+ = N_("E1274: No script file name to substitute for \"<script>\"");
static int quitmore = 0;
static bool ex_pressedreturn = false;
@@ -6564,6 +6570,7 @@ enum {
SPEC_SFILE,
SPEC_SLNUM,
SPEC_STACK,
+ SPEC_SCRIPT,
SPEC_AFILE,
SPEC_ABUF,
SPEC_AMATCH,
@@ -6588,6 +6595,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
[SPEC_SFILE] = "<sfile>", // ":so" file name
[SPEC_SLNUM] = "<slnum>", // ":so" file line number
[SPEC_STACK] = "<stack>", // call stack
+ [SPEC_SCRIPT] = "<script>", // script file name
[SPEC_AFILE] = "<afile>", // autocommand file name
[SPEC_ABUF] = "<abuf>", // autocommand buffer number
[SPEC_AMATCH] = "<amatch>", // autocommand match name
@@ -6801,12 +6809,25 @@ 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(ESTACK_SFILE);
+ if (result == NULL) {
+ *errormsg = _(e_no_source_file_name_to_substitute_for_sfile);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
+ break;
case SPEC_STACK: // call stack
- result = estack_sfile(spec_idx == SPEC_SFILE ? ESTACK_SFILE : ESTACK_STACK);
+ result = estack_sfile(ESTACK_STACK);
+ if (result == NULL) {
+ *errormsg = _(e_no_call_stack_to_substitute_for_stack);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
+ break;
+ case SPEC_SCRIPT: // script file name
+ result = estack_sfile(ESTACK_SCRIPT);
if (result == NULL) {
- *errormsg = spec_idx == SPEC_SFILE
- ? _("E498: no :source file name to substitute for \"<sfile>\"")
- : _("E489: no call stack to substitute for \"<stack>\"");
+ *errormsg = _(e_no_script_file_name_to_substitute_for_script);
return NULL;
}
resultbuf = result; // remember allocated string
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 537c496ae2..412b78728e 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -100,7 +100,8 @@ void estack_pop(void)
}
/// Get the current value for <sfile> in allocated memory.
-/// @param which ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>.
+/// @param which ESTACK_SFILE for <sfile>, ESTACK_STACK for <stack> or
+/// ESTACK_SCRIPT for <script>.
char *estack_sfile(estack_arg_T which)
{
estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
@@ -111,6 +112,27 @@ char *estack_sfile(estack_arg_T which)
return xstrdup(entry->es_name);
}
+ // If evaluated in a function return the path of the script where the
+ // function is defined, at script level the current script path is returned
+ // instead.
+ if (which == ESTACK_SCRIPT) {
+ if (entry->es_type == ETYPE_UFUNC) {
+ sctx_T *def_ctx = &entry->es_info.ufunc->uf_script_ctx;
+ if (def_ctx->sc_sid > 0) {
+ return xstrdup((char *)(SCRIPT_ITEM(def_ctx->sc_sid).sn_name));
+ }
+ } else if (exestack.ga_len > 0) {
+ // Walk the stack backwards, starting from the current frame.
+ for (int idx = exestack.ga_len - 1; idx; idx--) {
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ if (entry->es_type == ETYPE_SCRIPT) {
+ return xstrdup(entry->es_name);
+ }
+ }
+ }
+ return NULL;
+ }
+
// 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"
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index a255c6c096..03c426c79b 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -47,6 +47,7 @@ typedef enum {
ESTACK_NONE,
ESTACK_SFILE,
ESTACK_STACK,
+ ESTACK_SCRIPT,
} estack_arg_T;
typedef struct scriptitem_S {
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index ce414e4b11..4b9140ef50 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -147,4 +147,54 @@ func Test_expandcmd_shell_nonomatch()
call assert_equal('$*', expandcmd('$*'))
endfunc
+func Test_expand_script_source()
+ let lines0 =<< trim [SCRIPT]
+ let g:script_level[0] = expand('<script>:t')
+ so Xscript1
+ func F0()
+ let g:func_level[0] = expand('<script>:t')
+ endfunc
+ [SCRIPT]
+
+ let lines1 =<< trim [SCRIPT]
+ let g:script_level[1] = expand('<script>:t')
+ so Xscript2
+ func F1()
+ let g:func_level[1] = expand('<script>:t')
+ endfunc
+ [SCRIPT]
+
+ let lines2 =<< trim [SCRIPT]
+ let g:script_level[2] = expand('<script>:t')
+ func F2()
+ let g:func_level[2] = expand('<script>:t')
+ endfunc
+ [SCRIPT]
+
+ call writefile(lines0, 'Xscript0')
+ call writefile(lines1, 'Xscript1')
+ call writefile(lines2, 'Xscript2')
+
+ " Check the expansion of <script> at script and function level.
+ let g:script_level = ['', '', '']
+ let g:func_level = ['', '', '']
+
+ so Xscript0
+ call F0()
+ call F1()
+ call F2()
+
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level)
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level)
+
+ unlet g:script_level g:func_level
+ delfunc F0
+ delfunc F1
+ delfunc F2
+
+ call delete('Xscript0')
+ call delete('Xscript1')
+ call delete('Xscript2')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index 163ded43f9..cd1f43f9fd 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -166,6 +166,7 @@ describe(':source', function()
vim.g.sourced_lua = 1
vim.g.sfile_value = vim.fn.expand('<sfile>')
vim.g.stack_value = vim.fn.expand('<stack>')
+ vim.g.script_value = vim.fn.expand('<script>')
]])
command('set shellslash')
@@ -173,6 +174,7 @@ describe(':source', function()
eq(1, eval('g:sourced_lua'))
matches([[/test%.lua$]], meths.get_var('sfile_value'))
matches([[/test%.lua$]], meths.get_var('stack_value'))
+ matches([[/test%.lua$]], meths.get_var('script_value'))
os.remove(test_file)
end)
@@ -214,6 +216,7 @@ describe(':source', function()
"\ 2]=]
vim.g.sfile_value = vim.fn.expand('<sfile>')
vim.g.stack_value = vim.fn.expand('<stack>')
+ vim.g.script_value = vim.fn.expand('<script>')
]])
command('edit '..test_file)
@@ -223,6 +226,7 @@ describe(':source', function()
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
eq(':source (no file)', meths.get_var('sfile_value'))
eq(':source (no file)', meths.get_var('stack_value'))
+ eq(':source (no file)', meths.get_var('script_value'))
os.remove(test_file)
end)