aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2021-10-09 22:13:11 +0100
committerSean Dewar <seandewar@users.noreply.github.com>2021-10-14 01:27:10 +0100
commitd4ed51eb4492d4358eb4ab0c939802f2a5fdc636 (patch)
tree6bdebbb39ba5a5c64175439a94d688314f089814
parent6b9cb665fa101299d8a6f67d6d7e4705da13abee (diff)
downloadrneovim-d4ed51eb4492d4358eb4ab0c939802f2a5fdc636.tar.gz
rneovim-d4ed51eb4492d4358eb4ab0c939802f2a5fdc636.tar.bz2
rneovim-d4ed51eb4492d4358eb4ab0c939802f2a5fdc636.zip
feat(:source, nvim_exec): support script-local variables
Based on #13143 (and #11507) with changes: - Omit script_type_E. Use sn_name == NULL to determine anon items. - Keep SID_STR. Used by anon :source for .lua files (no item). - Show SID in get_scriptname output (:verbose set). - Factor item creation into new_script_item. - Leave sc_seq = 0 (anon scripts don't re-use the same item when re-sourced). - Add tests for anon :source. Co-authored-by: Vikram Pal <vikrampal659@gmail.com> Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
-rw-r--r--src/nvim/ex_cmds2.c56
-rw-r--r--src/nvim/globals.h2
-rw-r--r--test/functional/api/vim_spec.lua25
-rw-r--r--test/functional/ex_cmds/source_spec.lua16
4 files changed, 75 insertions, 24 deletions
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index c3cde2f15a..c421d3991c 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1939,6 +1939,29 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
return ga.ga_data;
}
+/// Create a new script item and allocate script-local vars. @see new_script_vars
+///
+/// @param name File name of the script. NULL for anonymous :source.
+/// @param[out] sid_out SID of the new item.
+/// @return pointer to the created script item.
+static scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out)
+{
+ static scid_T last_current_SID = 0;
+ const scid_T sid = ++last_current_SID;
+ if (sid_out != NULL) {
+ *sid_out = sid;
+ }
+ ga_grow(&script_items, (int)(sid - script_items.ga_len));
+ while (script_items.ga_len < sid) {
+ script_items.ga_len++;
+ SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
+ SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
+ }
+ SCRIPT_ITEM(sid).sn_name = name;
+ new_script_vars(sid); // Allocate the local script variables to use for this script.
+ return &SCRIPT_ITEM(sid);
+}
+
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
{
char_u *save_sourcing_name = sourcing_name;
@@ -1955,7 +1978,7 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char
sourcing_lnum = 0;
const sctx_T save_current_sctx = current_sctx;
- current_sctx.sc_sid = SID_STR;
+ new_script_item(NULL, &current_sctx.sc_sid);
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = save_sourcing_lnum;
funccal_entry_T entry;
@@ -2036,7 +2059,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
char_u *fname_exp;
char_u *firstline = NULL;
int retval = FAIL;
- static scid_T last_current_SID = 0;
static int last_current_SID_seq = 0;
int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL;
@@ -2183,15 +2205,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
}
}
if (current_sctx.sc_sid == 0) {
- current_sctx.sc_sid = ++last_current_SID;
- ga_grow(&script_items, (int)(current_sctx.sc_sid - script_items.ga_len));
- while (script_items.ga_len < current_sctx.sc_sid) {
- script_items.ga_len++;
- SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
- SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
- }
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- si->sn_name = fname_exp;
+ si = new_script_item(fname_exp, &current_sctx.sc_sid);
fname_exp = vim_strsave(si->sn_name); // used for autocmd
if (file_id_ok) {
si->file_id_valid = true;
@@ -2199,9 +2213,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
} else {
si->file_id_valid = false;
}
-
- // Allocate the local script variables to use for this script.
- new_script_vars(current_sctx.sc_sid);
}
if (l_do_profiling == PROF_YES) {
@@ -2375,16 +2386,21 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
case SID_LUA:
return (char_u *)_("Lua");
case SID_API_CLIENT:
- vim_snprintf((char *)IObuff, IOSIZE,
- _("API client (channel id %" PRIu64 ")"),
- last_set.channel_id);
+ snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
return IObuff;
case SID_STR:
return (char_u *)_("anonymous :source");
- default:
+ default: {
+ char_u *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
+ if (sname == NULL) {
+ snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"),
+ last_set.script_ctx.sc_sid);
+ return IObuff;
+ }
+
*should_free = true;
- return home_replace_save(NULL,
- SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name);
+ return home_replace_save(NULL, sname);
+ }
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 182e88083a..435949379b 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -335,7 +335,7 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_WINLAYOUT -7 // changing window size
#define SID_LUA -8 // for Lua scripts/chunks
#define SID_API_CLIENT -9 // for API clients
-#define SID_STR -10 // for sourcing a string
+#define SID_STR -10 // for sourcing a string with no script item
// Script CTX being sourced or was sourced to define the current function.
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 6b644ed519..f878a50a26 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -89,7 +89,7 @@ describe('API', function()
it(':verbose set {option}?', function()
nvim('exec', 'set nowrap', false)
- eq('nowrap\n\tLast set from anonymous :source',
+ eq('nowrap\n\tLast set from anonymous :source (script id 1)',
nvim('exec', 'verbose set wrap?', true))
end)
@@ -132,6 +132,29 @@ describe('API', function()
-- try no spaces before continuations to catch off-by-one error
nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false)
eq({a = 98}, request('nvim_eval', 'g:ab'))
+
+ -- Script scope (s:)
+ eq('ahoy! script-scoped varrrrr', nvim('exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ function! s:avast_ye_hades(s) abort
+ return a:s .. ' ' .. s:pirate
+ endfunction
+ echo <sid>avast_ye_hades('ahoy!')
+ ]], true))
+
+ eq('ahoy! script-scoped varrrrr', nvim('exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ function! Avast_ye_hades(s) abort
+ return a:s .. ' ' .. s:pirate
+ endfunction
+ echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1)
+ ]], true))
+
+ eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate',
+ pcall_err(request, 'nvim_exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ call nvim_exec('echo s:pirate', 1)
+ ]], false))
end)
it('non-ASCII input', function()
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index bdf6ae76d1..8077b22e56 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -25,11 +25,14 @@ describe(':source', function()
let b = #{
\ k: "v"
"\ (o_o)
- \ }]])
+ \ }
+ let s:s = 0zbeef.cafe
+ let c = s:s]])
command('source')
eq('2', meths.exec('echo a', true))
eq("{'k': 'v'}", meths.exec('echo b', true))
+ eq("0zBEEFCAFE", meths.exec('echo c', true))
exec('set cpoptions+=C')
eq('Vim(let):E15: Invalid expression: #{', exc_exec('source'))
@@ -43,7 +46,11 @@ describe(':source', function()
let b = #{
"\ (>_<)
\ K: "V"
- \ }]])
+ \ }
+ function! s:C() abort
+ return expand("<SID>") .. "C()"
+ endfunction
+ let D = {-> s:C()}]])
-- Source the 2nd line only
feed('ggjV')
@@ -55,6 +62,11 @@ describe(':source', function()
feed_command(':source')
eq('4', meths.exec('echo a', true))
eq("{'K': 'V'}", meths.exec('echo b', true))
+ eq("<SNR>3_C()", meths.exec('echo D()', true))
+
+ -- Source last line only
+ feed_command(':$source')
+ eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()'))
exec('set cpoptions+=C')
eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source"))