mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge #15994 feat(:source, nvim_exec): script-local scope
This commit is contained in:
commit
30af69509d
@ -9191,8 +9191,12 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons
|
|||||||
} else if (*name == 'l' && funccal != NULL) { // local variable
|
} else if (*name == 'l' && funccal != NULL) { // local variable
|
||||||
*d = &funccal->l_vars;
|
*d = &funccal->l_vars;
|
||||||
} else if (*name == 's' // script variable
|
} else if (*name == 's' // script variable
|
||||||
&& current_sctx.sc_sid > 0
|
&& (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR)
|
||||||
&& current_sctx.sc_sid <= ga_scripts.ga_len) {
|
&& current_sctx.sc_sid <= ga_scripts.ga_len) {
|
||||||
|
// For anonymous scripts without a script item, create one now so script vars can be used
|
||||||
|
if (current_sctx.sc_sid == SID_STR) {
|
||||||
|
new_script_item(NULL, ¤t_sctx.sc_sid);
|
||||||
|
}
|
||||||
*d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
|
*d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,33 +52,7 @@
|
|||||||
#include "nvim/version.h"
|
#include "nvim/version.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
|
|
||||||
|
|
||||||
/// Growarray to store info about already sourced scripts.
|
/// Growarray to store info about already sourced scripts.
|
||||||
/// Also store the dev/ino, so that we don't have to stat() each
|
|
||||||
/// script when going through the list.
|
|
||||||
typedef struct scriptitem_S {
|
|
||||||
char_u *sn_name;
|
|
||||||
bool file_id_valid;
|
|
||||||
FileID file_id;
|
|
||||||
bool sn_prof_on; ///< true when script is/was profiled
|
|
||||||
bool sn_pr_force; ///< forceit: profile functions in this script
|
|
||||||
proftime_T sn_pr_child; ///< time set when going into first child
|
|
||||||
int sn_pr_nest; ///< nesting for sn_pr_child
|
|
||||||
// profiling the script as a whole
|
|
||||||
int sn_pr_count; ///< nr of times sourced
|
|
||||||
proftime_T sn_pr_total; ///< time spent in script + children
|
|
||||||
proftime_T sn_pr_self; ///< time spent in script itself
|
|
||||||
proftime_T sn_pr_start; ///< time at script start
|
|
||||||
proftime_T sn_pr_children; ///< time in children after script start
|
|
||||||
// profiling the script per line
|
|
||||||
garray_T sn_prl_ga; ///< things stored for every line
|
|
||||||
proftime_T sn_prl_start; ///< start time for current line
|
|
||||||
proftime_T sn_prl_children; ///< time spent in children for this line
|
|
||||||
proftime_T sn_prl_wait; ///< wait start time for current line
|
|
||||||
linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
|
|
||||||
int sn_prl_execed; ///< line being timed was executed
|
|
||||||
} scriptitem_T;
|
|
||||||
|
|
||||||
static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
|
static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
|
||||||
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
|
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
|
||||||
|
|
||||||
@ -1939,6 +1913,29 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
|
|||||||
return ga.ga_data;
|
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.
|
||||||
|
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)
|
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
|
||||||
{
|
{
|
||||||
char_u *save_sourcing_name = sourcing_name;
|
char_u *save_sourcing_name = sourcing_name;
|
||||||
@ -2036,7 +2033,6 @@ int do_source(char *fname, int check_other, int is_vimrc)
|
|||||||
char_u *fname_exp;
|
char_u *fname_exp;
|
||||||
char_u *firstline = NULL;
|
char_u *firstline = NULL;
|
||||||
int retval = FAIL;
|
int retval = FAIL;
|
||||||
static scid_T last_current_SID = 0;
|
|
||||||
static int last_current_SID_seq = 0;
|
static int last_current_SID_seq = 0;
|
||||||
int save_debug_break_level = debug_break_level;
|
int save_debug_break_level = debug_break_level;
|
||||||
scriptitem_T *si = NULL;
|
scriptitem_T *si = NULL;
|
||||||
@ -2183,15 +2179,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current_sctx.sc_sid == 0) {
|
if (current_sctx.sc_sid == 0) {
|
||||||
current_sctx.sc_sid = ++last_current_SID;
|
si = new_script_item(fname_exp, ¤t_sctx.sc_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;
|
|
||||||
fname_exp = vim_strsave(si->sn_name); // used for autocmd
|
fname_exp = vim_strsave(si->sn_name); // used for autocmd
|
||||||
if (file_id_ok) {
|
if (file_id_ok) {
|
||||||
si->file_id_valid = true;
|
si->file_id_valid = true;
|
||||||
@ -2199,9 +2187,6 @@ int do_source(char *fname, int check_other, int is_vimrc)
|
|||||||
} else {
|
} else {
|
||||||
si->file_id_valid = false;
|
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) {
|
if (l_do_profiling == PROF_YES) {
|
||||||
@ -2375,16 +2360,21 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
|
|||||||
case SID_LUA:
|
case SID_LUA:
|
||||||
return (char_u *)_("Lua");
|
return (char_u *)_("Lua");
|
||||||
case SID_API_CLIENT:
|
case SID_API_CLIENT:
|
||||||
vim_snprintf((char *)IObuff, IOSIZE,
|
snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
|
||||||
_("API client (channel id %" PRIu64 ")"),
|
|
||||||
last_set.channel_id);
|
|
||||||
return IObuff;
|
return IObuff;
|
||||||
case SID_STR:
|
case SID_STR:
|
||||||
return (char_u *)_("anonymous :source");
|
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;
|
*should_free = true;
|
||||||
return home_replace_save(NULL,
|
return home_replace_save(NULL, sname);
|
||||||
SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,31 @@
|
|||||||
#define CCGD_ALLBUF 8 // may write all buffers
|
#define CCGD_ALLBUF 8 // may write all buffers
|
||||||
#define CCGD_EXCMD 16 // may suggest using !
|
#define CCGD_EXCMD 16 // may suggest using !
|
||||||
|
|
||||||
|
/// Also store the dev/ino, so that we don't have to stat() each
|
||||||
|
/// script when going through the list.
|
||||||
|
typedef struct scriptitem_S {
|
||||||
|
char_u *sn_name;
|
||||||
|
bool file_id_valid;
|
||||||
|
FileID file_id;
|
||||||
|
bool sn_prof_on; ///< true when script is/was profiled
|
||||||
|
bool sn_pr_force; ///< forceit: profile functions in this script
|
||||||
|
proftime_T sn_pr_child; ///< time set when going into first child
|
||||||
|
int sn_pr_nest; ///< nesting for sn_pr_child
|
||||||
|
// profiling the script as a whole
|
||||||
|
int sn_pr_count; ///< nr of times sourced
|
||||||
|
proftime_T sn_pr_total; ///< time spent in script + children
|
||||||
|
proftime_T sn_pr_self; ///< time spent in script itself
|
||||||
|
proftime_T sn_pr_start; ///< time at script start
|
||||||
|
proftime_T sn_pr_children; ///< time in children after script start
|
||||||
|
// profiling the script per line
|
||||||
|
garray_T sn_prl_ga; ///< things stored for every line
|
||||||
|
proftime_T sn_prl_start; ///< start time for current line
|
||||||
|
proftime_T sn_prl_children; ///< time spent in children for this line
|
||||||
|
proftime_T sn_prl_wait; ///< wait start time for current line
|
||||||
|
linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
|
||||||
|
int sn_prl_execed; ///< line being timed was executed
|
||||||
|
} scriptitem_T;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_cmds2.h.generated.h"
|
# include "ex_cmds2.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -335,7 +335,7 @@ EXTERN int garbage_collect_at_exit INIT(= false);
|
|||||||
#define SID_WINLAYOUT -7 // changing window size
|
#define SID_WINLAYOUT -7 // changing window size
|
||||||
#define SID_LUA -8 // for Lua scripts/chunks
|
#define SID_LUA -8 // for Lua scripts/chunks
|
||||||
#define SID_API_CLIENT -9 // for API clients
|
#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.
|
// Script CTX being sourced or was sourced to define the current function.
|
||||||
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
|
EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
|
||||||
|
@ -91,6 +91,14 @@ describe('API', function()
|
|||||||
nvim('exec', 'set nowrap', false)
|
nvim('exec', 'set nowrap', false)
|
||||||
eq('nowrap\n\tLast set from anonymous :source',
|
eq('nowrap\n\tLast set from anonymous :source',
|
||||||
nvim('exec', 'verbose set wrap?', true))
|
nvim('exec', 'verbose set wrap?', true))
|
||||||
|
|
||||||
|
-- Using script var to force creation of a script item
|
||||||
|
nvim('exec', [[
|
||||||
|
let s:a = 1
|
||||||
|
set nowrap
|
||||||
|
]], false)
|
||||||
|
eq('nowrap\n\tLast set from anonymous :source (script id 1)',
|
||||||
|
nvim('exec', 'verbose set wrap?', true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('multiline input', function()
|
it('multiline input', function()
|
||||||
@ -132,6 +140,43 @@ describe('API', function()
|
|||||||
-- try no spaces before continuations to catch off-by-one error
|
-- try no spaces before continuations to catch off-by-one error
|
||||||
nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false)
|
nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false)
|
||||||
eq({a = 98}, request('nvim_eval', 'g:ab'))
|
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))
|
||||||
|
|
||||||
|
-- Script items are created only on script var access
|
||||||
|
eq('1\n0', nvim('exec', [[
|
||||||
|
echo expand("<SID>")->empty()
|
||||||
|
let s:a = 123
|
||||||
|
echo expand("<SID>")->empty()
|
||||||
|
]], true))
|
||||||
|
|
||||||
|
eq('1\n0', nvim('exec', [[
|
||||||
|
echo expand("<SID>")->empty()
|
||||||
|
function s:a() abort
|
||||||
|
endfunction
|
||||||
|
echo expand("<SID>")->empty()
|
||||||
|
]], true))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('non-ASCII input', function()
|
it('non-ASCII input', function()
|
||||||
|
@ -25,12 +25,19 @@ describe(':source', function()
|
|||||||
let b = #{
|
let b = #{
|
||||||
\ k: "v"
|
\ k: "v"
|
||||||
"\ (o_o)
|
"\ (o_o)
|
||||||
\ }]])
|
\ }
|
||||||
|
let c = expand("<SID>")->empty()
|
||||||
|
let s:s = 0zbeef.cafe
|
||||||
|
let d = s:s]])
|
||||||
|
|
||||||
command('source')
|
command('source')
|
||||||
eq('2', meths.exec('echo a', true))
|
eq('2', meths.exec('echo a', true))
|
||||||
eq("{'k': 'v'}", meths.exec('echo b', true))
|
eq("{'k': 'v'}", meths.exec('echo b', true))
|
||||||
|
|
||||||
|
-- Script items are created only on script var access
|
||||||
|
eq("1", meths.exec('echo c', true))
|
||||||
|
eq("0zBEEFCAFE", meths.exec('echo d', true))
|
||||||
|
|
||||||
exec('set cpoptions+=C')
|
exec('set cpoptions+=C')
|
||||||
eq('Vim(let):E15: Invalid expression: #{', exc_exec('source'))
|
eq('Vim(let):E15: Invalid expression: #{', exc_exec('source'))
|
||||||
end)
|
end)
|
||||||
@ -43,7 +50,11 @@ describe(':source', function()
|
|||||||
let b = #{
|
let b = #{
|
||||||
"\ (>_<)
|
"\ (>_<)
|
||||||
\ K: "V"
|
\ K: "V"
|
||||||
\ }]])
|
\ }
|
||||||
|
function! s:C() abort
|
||||||
|
return expand("<SID>") .. "C()"
|
||||||
|
endfunction
|
||||||
|
let D = {-> s:C()}]])
|
||||||
|
|
||||||
-- Source the 2nd line only
|
-- Source the 2nd line only
|
||||||
feed('ggjV')
|
feed('ggjV')
|
||||||
@ -55,6 +66,11 @@ describe(':source', function()
|
|||||||
feed_command(':source')
|
feed_command(':source')
|
||||||
eq('4', meths.exec('echo a', true))
|
eq('4', meths.exec('echo a', true))
|
||||||
eq("{'K': 'V'}", meths.exec('echo b', true))
|
eq("{'K': 'V'}", meths.exec('echo b', true))
|
||||||
|
eq("<SNR>1_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')
|
exec('set cpoptions+=C')
|
||||||
eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source"))
|
eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source"))
|
||||||
|
Loading…
Reference in New Issue
Block a user