Merge #10869 'vim-patch:8.1.{0309,0362,0365,0515,1946}'

This commit is contained in:
Justin M. Keyes 2019-09-05 14:10:32 -07:00
commit 8b06231612
31 changed files with 770 additions and 280 deletions

View File

@ -816,11 +816,12 @@ it, no matter how many backslashes.
\\# \# \\# \#
Also see |`=|. Also see |`=|.
*:<cword>* *:<cWORD>* *:<cfile>* *<cfile>* *:<cword>* *<cword>* *:<cWORD>* *<cWORD>*
*:<sfile>* *<sfile>* *:<afile>* *<afile>* *:<cexpr>* *<cexpr>* *:<cfile>* *<cfile>*
*:<abuf>* *<abuf>* *:<amatch>* *<amatch>* *:<afile>* *<afile>* *:<abuf>* *<abuf>*
*:<cexpr>* *<cexpr>* *:<amatch>* *<amatch>*
*<slnum>* *E495* *E496* *E497* *E499* *E500* *:<sfile>* *<sfile>* *:<slnum>* *<slnum>*
*:<sflnum>* *<sflnum>* *E499* *E500*
Note: these are typed literally, they are not special keys! Note: these are typed literally, they are not special keys!
<cword> is replaced with the word under the cursor (like |star|) <cword> is replaced with the word under the cursor (like |star|)
<cWORD> is replaced with the WORD under the cursor (see |WORD|) <cWORD> is replaced with the WORD under the cursor (see |WORD|)
@ -833,15 +834,16 @@ Note: these are typed literally, they are not special keys!
|gf| uses) |gf| uses)
<afile> When executing autocommands, is replaced with the file name <afile> When executing autocommands, is replaced with the file name
of the buffer being manipulated, or the file for a read or of the buffer being manipulated, or the file for a read or
write. write. *E495*
<abuf> When executing autocommands, is replaced with the currently <abuf> When executing autocommands, is replaced with the currently
effective buffer number (for ":r file" and ":so file" it is effective buffer number (for ":r file" and ":so file" it is
the current buffer, the file being read/sourced is not in a the current buffer, the file being read/sourced is not in a
buffer). buffer). *E496*
<amatch> When executing autocommands, is replaced with the match for <amatch> When executing autocommands, is replaced with the match for
which this autocommand was executed. It differs from which this autocommand was executed. *E497*
<afile> only when the file name isn't used to match with It differs from <afile> only when the file name isn't used
(for FileType, Syntax and SpellFileMissing events). to match with (for FileType, Syntax and SpellFileMissing
events).
<sfile> When executing a ":source" command, is replaced with the <sfile> When executing a ":source" command, is replaced with the
file name of the sourced file. *E498* file name of the sourced file. *E498*
When executing a function, is replaced with: When executing a function, is replaced with:
@ -851,9 +853,12 @@ Note: these are typed literally, they are not special keys!
Note that filename-modifiers are useless when <sfile> is Note that filename-modifiers are useless when <sfile> is
used inside a function. used inside a function.
<slnum> When executing a ":source" command, is replaced with the <slnum> When executing a ":source" command, is replaced with the
line number. *E842* line number. *E842*
When executing a function it's the line number relative to When executing a function it's the line number relative to
the start of the function. the start of the function.
<sflnum> When executing a script, is replaced with the line number.
It differs from <slnum> in that <sflnum> is replaced with
the script line number in any situation. *E961*
*filename-modifiers* *filename-modifiers*
*:_%:* *::8* *::p* *::.* *::~* *::h* *::t* *::r* *::e* *::s* *::gs* *::S* *:_%:* *::8* *::p* *::.* *::~* *::h* *::t* *::r* *::e* *::s* *::gs* *::S*

View File

@ -3595,7 +3595,10 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()*
<abuf> autocmd buffer number (as a String!) <abuf> autocmd buffer number (as a String!)
<amatch> autocmd matched name <amatch> autocmd matched name
<sfile> sourced script file or function name <sfile> sourced script file or function name
<slnum> sourced script file line number <slnum> sourced script line number or function
line number
<sflnum> script file line number, also when in
a function
<cword> word under the cursor <cword> word under the cursor
<cWORD> WORD under the cursor <cWORD> WORD under the cursor
<client> the {clientid} of the last received <client> the {clientid} of the last received
@ -5705,6 +5708,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()*
(|mapmode-ic|) (|mapmode-ic|)
"sid" The script local ID, used for <sid> mappings "sid" The script local ID, used for <sid> mappings
(|<SID>|). (|<SID>|).
"lnum" The line number in "sid", zero if unknown.
"nowait" Do not wait for other, longer mappings. "nowait" Do not wait for other, longer mappings.
(|:map-<nowait>|). (|:map-<nowait>|).
@ -9255,9 +9259,13 @@ See |:verbose-cmd| for more information.
deleted if there are no more references to it. deleted if there are no more references to it.
*E127* *E122* *E127* *E122*
When a function by this name already exists and [!] is When a function by this name already exists and [!] is
not used an error message is given. When [!] is used, not used an error message is given. There is one
an existing function is silently replaced. Unless it exception: When sourcing a script again, a function
is currently being executed, that is an error. that was previously defined in that script will be
silently replaced.
When [!] is used, an existing function is silently
replaced. Unless it is currently being executed, that
is an error.
NOTE: Use ! wisely. If used without care it can cause NOTE: Use ! wisely. If used without care it can cause
an existing function to be replaced unexpectedly, an existing function to be replaced unexpectedly,
which is hard to debug. which is hard to debug.

View File

@ -391,8 +391,10 @@ void set_option_to(uint64_t channel_id, void *to, int type,
stringval = (char *)value.data.string.data; stringval = (char *)value.data.string.data;
} }
const scid_T save_current_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
current_SID = channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; current_sctx.sc_sid =
channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT;
current_sctx.sc_lnum = 0;
current_channel_id = channel_id; current_channel_id = channel_id;
const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL))
@ -401,7 +403,7 @@ void set_option_to(uint64_t channel_id, void *to, int type,
set_option_value_for(name.data, numval, stringval, set_option_value_for(name.data, numval, stringval,
opt_flags, type, to, err); opt_flags, type, to, err);
current_SID = save_current_SID; current_sctx = save_current_sctx;
} }
#define TYPVAL_ENCODE_ALLOW_SPECIALS false #define TYPVAL_ENCODE_ALLOW_SPECIALS false

View File

@ -5064,7 +5064,6 @@ chk_modeline(
int retval = OK; int retval = OK;
char_u *save_sourcing_name; char_u *save_sourcing_name;
linenr_T save_sourcing_lnum; linenr_T save_sourcing_lnum;
scid_T save_SID;
prev = -1; prev = -1;
for (s = ml_get(lnum); *s != NUL; s++) { for (s = ml_get(lnum); *s != NUL; s++) {
@ -5152,15 +5151,17 @@ chk_modeline(
if (*s != NUL) { // skip over an empty "::" if (*s != NUL) { // skip over an empty "::"
const int secure_save = secure; const int secure_save = secure;
save_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
current_SID = SID_MODELINE; current_sctx.sc_sid = SID_MODELINE;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
// Make sure no risky things are executed as a side effect. // Make sure no risky things are executed as a side effect.
secure = 1; secure = 1;
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
secure = secure_save; secure = secure_save;
current_SID = save_SID; current_sctx = save_current_sctx;
if (retval == FAIL) { // stop if error found if (retval == FAIL) { // stop if error found
break; break;
} }

View File

@ -255,8 +255,8 @@ typedef struct {
long wo_winbl; long wo_winbl;
# define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend' # define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend'
LastSet wo_scriptID[WV_COUNT]; // SIDs for window-local options LastSet wo_script_ctx[WV_COUNT]; // SCTXs for window-local options
# define w_p_scriptID w_onebuf_opt.wo_scriptID # define w_p_script_ctx w_onebuf_opt.wo_script_ctx
} winopt_T; } winopt_T;
/* /*
@ -344,17 +344,17 @@ typedef struct {
*/ */
typedef struct mapblock mapblock_T; typedef struct mapblock mapblock_T;
struct mapblock { struct mapblock {
mapblock_T *m_next; /* next mapblock in list */ mapblock_T *m_next; // next mapblock in list
char_u *m_keys; /* mapped from, lhs */ char_u *m_keys; // mapped from, lhs
char_u *m_str; /* mapped to, rhs */ char_u *m_str; // mapped to, rhs
char_u *m_orig_str; /* rhs as entered by the user */ char_u *m_orig_str; // rhs as entered by the user
int m_keylen; /* strlen(m_keys) */ int m_keylen; // strlen(m_keys)
int m_mode; /* valid mode */ int m_mode; // valid mode
int m_noremap; /* if non-zero no re-mapping for m_str */ int m_noremap; // if non-zero no re-mapping for m_str
char m_silent; /* <silent> used, don't echo commands */ char m_silent; // <silent> used, don't echo commands
char m_nowait; /* <nowait> used */ char m_nowait; // <nowait> used
char m_expr; /* <expr> used, m_str is an expression */ char m_expr; // <expr> used, m_str is an expression
scid_T m_script_ID; /* ID of script where map was defined */ sctx_T m_script_ctx; // SCTX where map was defined
}; };
/* /*
@ -622,9 +622,9 @@ struct file_buffer {
* They are here because their value depends on the type of file * They are here because their value depends on the type of file
* or contents of the file being edited. * or contents of the file being edited.
*/ */
bool b_p_initialized; /* set when options initialized */ bool b_p_initialized; // set when options initialized
LastSet b_p_scriptID[BV_COUNT]; // SIDs for buffer-local options LastSet b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options
int b_p_ai; ///< 'autoindent' int b_p_ai; ///< 'autoindent'
int b_p_ai_nopaste; ///< b_p_ai saved for paste mode int b_p_ai_nopaste; ///< b_p_ai saved for paste mode

View File

@ -1815,13 +1815,11 @@ static void list_vim_vars(int *first)
list_hashtable_vars(&vimvarht, "v:", false, first); list_hashtable_vars(&vimvarht, "v:", false, first);
} }
/* // List script-local variables, if there is a script.
* List script-local variables, if there is a script.
*/
static void list_script_vars(int *first) static void list_script_vars(int *first)
{ {
if (current_SID > 0 && current_SID <= ga_scripts.ga_len) { if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) {
list_hashtable_vars(&SCRIPT_VARS(current_SID), "s:", false, first); list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first);
} }
} }
@ -5981,7 +5979,8 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
fp->uf_varargs = true; fp->uf_varargs = true;
fp->uf_flags = flags; fp->uf_flags = flags;
fp->uf_calls = 0; fp->uf_calls = 0;
fp->uf_script_ID = current_SID; fp->uf_script_ctx = current_sctx;
fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len;
pt->pt_func = fp; pt->pt_func = fp;
pt->pt_refcount = 1; pt->pt_refcount = 1;
@ -6329,11 +6328,11 @@ static char_u *fname_trans_sid(const char_u *const name,
fname_buf[2] = (int)KE_SNR; fname_buf[2] = (int)KE_SNR;
int i = 3; int i = 3;
if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:" if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
if (current_SID <= 0) { if (current_sctx.sc_sid <= 0) {
*error = ERROR_SCRIPT; *error = ERROR_SCRIPT;
} else { } else {
snprintf((char *)fname_buf + 3, FLEN_FIXED + 1, "%" PRId64 "_", snprintf((char *)fname_buf + 3, FLEN_FIXED + 1, "%" PRId64 "_",
(int64_t)current_SID); (int64_t)current_sctx.sc_sid);
i = (int)STRLEN(fname_buf); i = (int)STRLEN(fname_buf);
} }
} }
@ -9433,7 +9432,7 @@ static void common_function(typval_T *argvars, typval_T *rettv,
// would also work, but some plugins depend on the name being // would also work, but some plugins depend on the name being
// printable text. // printable text.
snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
(int64_t)current_SID); (int64_t)current_sctx.sc_sid);
name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1);
STRCPY(name, sid_buf); STRCPY(name, sid_buf);
STRCAT(name, s + off); STRCAT(name, s + off);
@ -12874,7 +12873,8 @@ void mapblock_fill_dict(dict_T *const dict,
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ctx.sc_sid);
tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)mp->m_script_ctx.sc_lnum);
tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
@ -14549,7 +14549,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ADD(args, vim_to_object(tv)); ADD(args, vim_to_object(tv));
} }
scid_T save_current_SID; sctx_T save_current_sctx;
uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match; uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
linenr_T save_sourcing_lnum; linenr_T save_sourcing_lnum;
int save_autocmd_bufnr; int save_autocmd_bufnr;
@ -14558,7 +14558,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (l_provider_call_nesting) { if (l_provider_call_nesting) {
// If this is called from a provider function, restore the scope // If this is called from a provider function, restore the scope
// information of the caller. // information of the caller.
save_current_SID = current_SID; save_current_sctx = current_sctx;
save_sourcing_name = sourcing_name; save_sourcing_name = sourcing_name;
save_sourcing_lnum = sourcing_lnum; save_sourcing_lnum = sourcing_lnum;
save_autocmd_fname = autocmd_fname; save_autocmd_fname = autocmd_fname;
@ -14566,7 +14566,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
save_autocmd_bufnr = autocmd_bufnr; save_autocmd_bufnr = autocmd_bufnr;
save_funccalp = save_funccal(); save_funccalp = save_funccal();
current_SID = provider_caller_scope.SID; current_sctx = provider_caller_scope.script_ctx;
sourcing_name = provider_caller_scope.sourcing_name; sourcing_name = provider_caller_scope.sourcing_name;
sourcing_lnum = provider_caller_scope.sourcing_lnum; sourcing_lnum = provider_caller_scope.sourcing_lnum;
autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_fname = provider_caller_scope.autocmd_fname;
@ -14584,7 +14584,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Object result = rpc_send_call(chan_id, method, args, &err); Object result = rpc_send_call(chan_id, method, args, &err);
if (l_provider_call_nesting) { if (l_provider_call_nesting) {
current_SID = save_current_SID; current_sctx = save_current_sctx;
sourcing_name = save_sourcing_name; sourcing_name = save_sourcing_name;
sourcing_lnum = save_sourcing_lnum; sourcing_lnum = save_sourcing_lnum;
autocmd_fname = save_autocmd_fname; autocmd_fname = save_autocmd_fname;
@ -20145,7 +20145,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht,
if (varname_len == 0) { if (varname_len == 0) {
// Must be something like "s:", otherwise "ht" would be NULL. // Must be something like "s:", otherwise "ht" would be NULL.
switch (htname) { switch (htname) {
case 's': return (dictitem_T *)&SCRIPT_SV(current_SID)->sv_var; case 's': return (dictitem_T *)&SCRIPT_SV(current_sctx.sc_sid)->sv_var;
case 'g': return (dictitem_T *)&globvars_var; case 'g': return (dictitem_T *)&globvars_var;
case 'v': return (dictitem_T *)&vimvars_var; case 'v': return (dictitem_T *)&vimvars_var;
case 'b': return (dictitem_T *)&curbuf->b_bufvar; case 'b': return (dictitem_T *)&curbuf->b_bufvar;
@ -20282,8 +20282,9 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len,
} else if (*name == 'l' && current_funccal != NULL) { // local variable } else if (*name == 'l' && current_funccal != NULL) { // local variable
*d = &get_funccal()->l_vars; *d = &get_funccal()->l_vars;
} else if (*name == 's' // script variable } else if (*name == 's' // script variable
&& current_SID > 0 && current_SID <= ga_scripts.ga_len) { && current_sctx.sc_sid > 0
*d = &SCRIPT_SV(current_SID)->sv_dict; && current_sctx.sc_sid <= ga_scripts.ga_len) {
*d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
} }
end: end:
@ -21529,7 +21530,11 @@ void ex_function(exarg_T *eap)
fp = find_func(name); fp = find_func(name);
if (fp != NULL) { if (fp != NULL) {
if (!eap->forceit) { // Function can be replaced with "function!" and when sourcing the
// same script again, but only once.
if (!eap->forceit
&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
|| fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) {
emsg_funcname(e_funcexts, name); emsg_funcname(e_funcexts, name);
goto erret; goto erret;
} }
@ -21654,7 +21659,8 @@ void ex_function(exarg_T *eap)
} }
fp->uf_flags = flags; fp->uf_flags = flags;
fp->uf_calls = 0; fp->uf_calls = 0;
fp->uf_script_ID = current_SID; fp->uf_script_ctx = current_sctx;
fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len - 1;
goto ret_free; goto ret_free;
erret: erret:
@ -21841,12 +21847,12 @@ trans_function_name(
if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
|| eval_fname_sid((const char *)(*pp))) { || eval_fname_sid((const char *)(*pp))) {
// It's "s:" or "<SID>". // It's "s:" or "<SID>".
if (current_SID <= 0) { if (current_sctx.sc_sid <= 0) {
EMSG(_(e_usingsid)); EMSG(_(e_usingsid));
goto theend; goto theend;
} }
sid_buf_len = snprintf(sid_buf, sizeof(sid_buf), sid_buf_len = snprintf(sid_buf, sizeof(sid_buf),
"%" PRIdSCID "_", current_SID); "%" PRIdSCID "_", current_sctx.sc_sid);
lead += sid_buf_len; lead += sid_buf_len;
} }
} else if (!(flags & TFN_INT) } else if (!(flags & TFN_INT)
@ -21963,8 +21969,9 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
msg_puts(" closure"); msg_puts(" closure");
} }
msg_clr_eos(); msg_clr_eos();
if (p_verbose > 0) if (p_verbose > 0) {
last_set_msg(fp->uf_script_ID); last_set_msg(fp->uf_script_ctx);
}
} }
/// Find a function by name, return pointer to it in ufuncs. /// Find a function by name, return pointer to it in ufuncs.
@ -22161,14 +22168,29 @@ void func_dump_profile(FILE *fd)
if (fp->uf_prof_initialized) { if (fp->uf_prof_initialized) {
sorttab[st_len++] = fp; sorttab[st_len++] = fp;
if (fp->uf_name[0] == K_SPECIAL) if (fp->uf_name[0] == K_SPECIAL) {
fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3); fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3);
else } else {
fprintf(fd, "FUNCTION %s()\n", fp->uf_name); fprintf(fd, "FUNCTION %s()\n", fp->uf_name);
if (fp->uf_tm_count == 1) }
if (fp->uf_script_ctx.sc_sid != 0) {
bool should_free;
const LastSet last_set = (LastSet){
.script_ctx = fp->uf_script_ctx,
.channel_id = 0,
};
char_u *p = get_scriptname(last_set, &should_free);
fprintf(fd, " Defined: %s line %" PRIdLINENR "\n",
p, fp->uf_script_ctx.sc_lnum);
if (should_free) {
xfree(p);
}
}
if (fp->uf_tm_count == 1) {
fprintf(fd, "Called 1 time\n"); fprintf(fd, "Called 1 time\n");
else } else {
fprintf(fd, "Called %d times\n", fp->uf_tm_count); fprintf(fd, "Called %d times\n", fp->uf_tm_count);
}
fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total)); fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total));
fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self)); fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self));
fprintf(fd, "\n"); fprintf(fd, "\n");
@ -22654,7 +22676,6 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
{ {
char_u *save_sourcing_name; char_u *save_sourcing_name;
linenr_T save_sourcing_lnum; linenr_T save_sourcing_lnum;
scid_T save_current_SID;
bool using_sandbox = false; bool using_sandbox = false;
funccall_T *fc; funccall_T *fc;
int save_did_emsg; int save_did_emsg;
@ -22908,8 +22929,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
script_prof_save(&wait_start); script_prof_save(&wait_start);
} }
save_current_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
current_SID = fp->uf_script_ID; current_sctx = fp->uf_script_ctx;
save_did_emsg = did_emsg; save_did_emsg = did_emsg;
did_emsg = FALSE; did_emsg = FALSE;
@ -22983,7 +23004,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,
xfree(sourcing_name); xfree(sourcing_name);
sourcing_name = save_sourcing_name; sourcing_name = save_sourcing_name;
sourcing_lnum = save_sourcing_lnum; sourcing_lnum = save_sourcing_lnum;
current_SID = save_current_SID; current_sctx = save_current_sctx;
if (do_profiling_yes) { if (do_profiling_yes) {
script_prof_restore(&wait_start); script_prof_restore(&wait_start);
} }
@ -23603,10 +23624,10 @@ int store_session_globals(FILE *fd)
* Display script name where an item was last set. * Display script name where an item was last set.
* Should only be invoked when 'verbose' is non-zero. * Should only be invoked when 'verbose' is non-zero.
*/ */
void last_set_msg(scid_T scriptID) void last_set_msg(sctx_T script_ctx)
{ {
const LastSet last_set = (LastSet){ const LastSet last_set = (LastSet){
.script_id = scriptID, .script_ctx = script_ctx,
.channel_id = 0, .channel_id = 0,
}; };
option_last_set_msg(last_set); option_last_set_msg(last_set);
@ -23617,12 +23638,16 @@ void last_set_msg(scid_T scriptID)
/// Should only be invoked when 'verbose' is non-zero. /// Should only be invoked when 'verbose' is non-zero.
void option_last_set_msg(LastSet last_set) void option_last_set_msg(LastSet last_set)
{ {
if (last_set.script_id != 0) { if (last_set.script_ctx.sc_sid != 0) {
bool should_free; bool should_free;
char_u *p = get_scriptname(last_set, &should_free); char_u *p = get_scriptname(last_set, &should_free);
verbose_enter(); verbose_enter();
MSG_PUTS(_("\n\tLast set from ")); MSG_PUTS(_("\n\tLast set from "));
MSG_PUTS(p); MSG_PUTS(p);
if (last_set.script_ctx.sc_lnum > 0) {
MSG_PUTS(_(" line "));
msg_outnum((long)last_set.script_ctx.sc_lnum);
}
if (should_free) { if (should_free) {
xfree(p); xfree(p);
} }
@ -24068,7 +24093,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
// Save caller scope information // Save caller scope information
struct caller_scope saved_provider_caller_scope = provider_caller_scope; struct caller_scope saved_provider_caller_scope = provider_caller_scope;
provider_caller_scope = (struct caller_scope) { provider_caller_scope = (struct caller_scope) {
.SID = current_SID, .script_ctx = current_sctx,
.sourcing_name = sourcing_name, .sourcing_name = sourcing_name,
.sourcing_lnum = sourcing_lnum, .sourcing_lnum = sourcing_lnum,
.autocmd_fname = autocmd_fname, .autocmd_fname = autocmd_fname,

View File

@ -248,6 +248,18 @@ typedef int scid_T;
/// Format argument for scid_T /// Format argument for scid_T
#define PRIdSCID "d" #define PRIdSCID "d"
// SCript ConteXt (SCTX): identifies a script script line.
// When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current
// line number. When executing a user function "sc_lnum" is the line where the
// function was defined, "sourcing_lnum" is the line number inside the
// function. When stored with a function, mapping, option, etc. "sc_lnum" is
// the line number in the script "sc_sid".
typedef struct {
scid_T sc_sid; // script ID
int sc_seq; // sourcing sequence number
linenr_T sc_lnum; // line number
} sctx_T;
// Structure to hold info for a function that is currently being executed. // Structure to hold info for a function that is currently being executed.
typedef struct funccall_S funccall_T; typedef struct funccall_S funccall_T;
@ -275,7 +287,7 @@ struct ufunc {
proftime_T uf_tml_wait; ///< start wait time for current line proftime_T uf_tml_wait; ///< start wait time for current line
int uf_tml_idx; ///< index of line being timed; -1 if none int uf_tml_idx; ///< index of line being timed; -1 if none
int uf_tml_execed; ///< line being timed was executed int uf_tml_execed; ///< line being timed was executed
scid_T uf_script_ID; ///< ID of script where function was defined, sctx_T uf_script_ctx; ///< SCTX where function was defined,
///< used for s: variables ///< used for s: variables
int uf_refcount; ///< reference count, see func_name_refcount() int uf_refcount; ///< reference count, see func_name_refcount()
funccall_T *uf_scoped; ///< l: local variables for closure funccall_T *uf_scoped; ///< l: local variables for closure

View File

@ -1080,8 +1080,8 @@ void script_prof_save(
{ {
scriptitem_T *si; scriptitem_T *si;
if (current_SID > 0 && current_SID <= script_items.ga_len) { if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on && si->sn_pr_nest++ == 0) { if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
si->sn_pr_child = profile_start(); si->sn_pr_child = profile_start();
} }
@ -1094,8 +1094,8 @@ void script_prof_restore(proftime_T *tm)
{ {
scriptitem_T *si; scriptitem_T *si;
if (current_SID > 0 && current_SID <= script_items.ga_len) { if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on && --si->sn_pr_nest == 0) { if (si->sn_prof_on && --si->sn_pr_nest == 0) {
si->sn_pr_child = profile_end(si->sn_pr_child); si->sn_pr_child = profile_end(si->sn_pr_child);
// don't count wait time // don't count wait time
@ -1192,8 +1192,8 @@ static void script_dump_profile(FILE *fd)
/// profiled. /// profiled.
bool prof_def_func(void) bool prof_def_func(void)
{ {
if (current_SID > 0) { if (current_sctx.sc_sid > 0) {
return SCRIPT_ITEM(current_SID).sn_pr_force; return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
} }
return false; return false;
} }
@ -3033,8 +3033,8 @@ int do_source(char_u *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;
scid_T save_current_SID;
static scid_T last_current_SID = 0; static scid_T last_current_SID = 0;
static int last_current_SID_seq = 0;
void *save_funccalp; void *save_funccalp;
int save_debug_break_level = debug_break_level; int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL; scriptitem_T *si = NULL;
@ -3161,12 +3161,16 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
// Check if this script was sourced before to finds its SID. // Check if this script was sourced before to finds its SID.
// If it's new, generate a new SID. // If it's new, generate a new SID.
save_current_SID = current_SID; // Always use a new sequence number.
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_seq = ++last_current_SID_seq;
current_sctx.sc_lnum = 0;
FileID file_id; FileID file_id;
bool file_id_ok = os_fileid((char *)fname_exp, &file_id); bool file_id_ok = os_fileid((char *)fname_exp, &file_id);
assert(script_items.ga_len >= 0); assert(script_items.ga_len >= 0);
for (current_SID = script_items.ga_len; current_SID > 0; current_SID--) { for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
si = &SCRIPT_ITEM(current_SID); current_sctx.sc_sid--) {
si = &SCRIPT_ITEM(current_sctx.sc_sid);
// Compare dev/ino when possible, it catches symbolic links. // Compare dev/ino when possible, it catches symbolic links.
// Also compare file names, the inode may change when the file was edited. // Also compare file names, the inode may change when the file was edited.
bool file_id_equal = file_id_ok && si->file_id_valid bool file_id_equal = file_id_ok && si->file_id_valid
@ -3176,15 +3180,15 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
break; break;
} }
} }
if (current_SID == 0) { if (current_sctx.sc_sid == 0) {
current_SID = ++last_current_SID; current_sctx.sc_sid = ++last_current_SID;
ga_grow(&script_items, (int)(current_SID - script_items.ga_len)); ga_grow(&script_items, (int)(current_sctx.sc_sid - script_items.ga_len));
while (script_items.ga_len < current_SID) { while (script_items.ga_len < current_sctx.sc_sid) {
script_items.ga_len++; script_items.ga_len++;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
} }
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
si->sn_name = fname_exp; 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) {
@ -3195,7 +3199,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
} }
// Allocate the local script variables to use for this script. // Allocate the local script variables to use for this script.
new_script_vars(current_SID); new_script_vars(current_sctx.sc_sid);
} }
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES) {
@ -3236,7 +3240,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES) {
// Get "si" again, "script_items" may have been reallocated. // Get "si" again, "script_items" may have been reallocated.
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on) { if (si->sn_prof_on) {
si->sn_pr_start = profile_end(si->sn_pr_start); si->sn_pr_start = profile_end(si->sn_pr_start);
si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start); si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start);
@ -3277,7 +3281,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
debug_break_level++; debug_break_level++;
} }
current_SID = save_current_SID; current_sctx = save_current_sctx;
restore_funccal(save_funccalp); restore_funccal(save_funccalp);
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES) {
prof_child_exit(&wait_start); // leaving a child now prof_child_exit(&wait_start); // leaving a child now
@ -3338,7 +3342,7 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
{ {
*should_free = false; *should_free = false;
switch (last_set.script_id) { switch (last_set.script_ctx.sc_sid) {
case SID_MODELINE: case SID_MODELINE:
return (char_u *)_("modeline"); return (char_u *)_("modeline");
case SID_CMDARG: case SID_CMDARG:
@ -3358,7 +3362,8 @@ char_u *get_scriptname(LastSet last_set, bool *should_free)
return IObuff; return IObuff;
default: default:
*should_free = true; *should_free = true;
return home_replace_save(NULL, SCRIPT_ITEM(last_set.script_id).sn_name); return home_replace_save(NULL,
SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name);
} }
} }
@ -3585,10 +3590,10 @@ void script_line_start(void)
scriptitem_T *si; scriptitem_T *si;
sn_prl_T *pp; sn_prl_T *pp;
if (current_SID <= 0 || current_SID > script_items.ga_len) { if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
return; return;
} }
si = &SCRIPT_ITEM(current_SID); 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 // Grow the array before starting the timer, so that the time spent
// here isn't counted. // here isn't counted.
@ -3616,10 +3621,10 @@ void script_line_exec(void)
{ {
scriptitem_T *si; scriptitem_T *si;
if (current_SID <= 0 || current_SID > script_items.ga_len) { if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
return; return;
} }
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on && si->sn_prl_idx >= 0) { if (si->sn_prof_on && si->sn_prl_idx >= 0) {
si->sn_prl_execed = true; si->sn_prl_execed = true;
} }
@ -3631,10 +3636,10 @@ void script_line_end(void)
scriptitem_T *si; scriptitem_T *si;
sn_prl_T *pp; sn_prl_T *pp;
if (current_SID <= 0 || current_SID > script_items.ga_len) { if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
return; return;
} }
si = &SCRIPT_ITEM(current_SID); si = &SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on && si->sn_prl_idx >= 0 if (si->sn_prof_on && si->sn_prl_idx >= 0
&& si->sn_prl_idx < si->sn_prl_ga.ga_len) { && si->sn_prl_idx < si->sn_prl_ga.ga_len) {
if (si->sn_prl_execed) { if (si->sn_prl_execed) {

View File

@ -143,9 +143,9 @@ struct exarg {
struct expand { struct expand {
int xp_context; // type of expansion int xp_context; // type of expansion
char_u *xp_pattern; // start of item to expand char_u *xp_pattern; // start of item to expand
size_t xp_pattern_len; // bytes in xp_pattern before cursor size_t xp_pattern_len; // bytes in xp_pattern before cursor
char_u *xp_arg; // completion function char_u *xp_arg; // completion function
int xp_scriptID; // SID for completion function sctx_T xp_script_ctx; // SCTX for completion function
int xp_backslash; // one of the XP_BS_ values int xp_backslash; // one of the XP_BS_ values
#ifndef BACKSLASH_IN_FILENAME #ifndef BACKSLASH_IN_FILENAME
int xp_shell; // TRUE for a shell command, more int xp_shell; // TRUE for a shell command, more

View File

@ -84,14 +84,14 @@ static int ex_pressedreturn = FALSE;
static int did_lcd; static int did_lcd;
typedef struct ucmd { typedef struct ucmd {
char_u *uc_name; /* The command name */ char_u *uc_name; // The command name
uint32_t uc_argt; /* The argument type */ uint32_t uc_argt; // The argument type
char_u *uc_rep; /* The command's replacement string */ char_u *uc_rep; // The command's replacement string
long uc_def; /* The default value for a range/count */ long uc_def; // The default value for a range/count
int uc_compl; /* completion type */ int uc_compl; // completion type
int uc_addr_type; /* The command's address type */ int uc_addr_type; // The command's address type
scid_T uc_scriptID; /* SID where the command was defined */ sctx_T uc_script_ctx; // SCTX where the command was defined
char_u *uc_compl_arg; /* completion argument if any */ char_u *uc_compl_arg; // completion argument if any
} ucmd_T; } ucmd_T;
#define UC_BUFFER 1 /* -buffer: local to current buffer */ #define UC_BUFFER 1 /* -buffer: local to current buffer */
@ -1471,22 +1471,6 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|| (cstack->cs_idx >= 0 || (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE))); && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
/* Count this line for profiling if ea.skip is FALSE. */
if (do_profiling == PROF_YES && !ea.skip) {
if (getline_equal(fgetline, cookie, get_func_line))
func_line_exec(getline_cookie(fgetline, cookie));
else if (getline_equal(fgetline, cookie, getsourceline))
script_line_exec();
}
/* May go to debug mode. If this happens and the ">quit" debug command is
* used, throw an interrupt exception and skip the next command. */
dbg_check_breakpoint(&ea);
if (!ea.skip && got_int) {
ea.skip = TRUE;
(void)do_intthrow(cstack);
}
// 3. Skip over the range to find the command. Let "p" point to after it. // 3. Skip over the range to find the command. Let "p" point to after it.
// //
// We need the command to know what kind of range it uses. // We need the command to know what kind of range it uses.
@ -1498,22 +1482,61 @@ static char_u * do_one_cmd(char_u **cmdlinep,
} }
p = find_command(&ea, NULL); p = find_command(&ea, NULL);
/* // Count this line for profiling if skip is TRUE.
* 4. Parse a range specifier of the form: addr [,addr] [;addr] .. if (do_profiling == PROF_YES
* && (!ea.skip || cstack->cs_idx == 0
* where 'addr' is: || (cstack->cs_idx > 0
* && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) {
* % (entire file) int skip = did_emsg || got_int || current_exception;
* $ [+-NUM]
* 'x [+-NUM] (where x denotes a currently defined mark) if (ea.cmdidx == CMD_catch) {
* . [+-NUM] skip = !skip && !(cstack->cs_idx >= 0
* [+-NUM].. && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN)
* NUM && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT));
* } else if (ea.cmdidx == CMD_else || ea.cmdidx == CMD_elseif) {
* The ea.cmd pointer is updated to point to the first character following the skip = skip || !(cstack->cs_idx >= 0
* range spec. If an initial address is found, but no second, the upper bound && !(cstack->cs_flags[cstack->cs_idx]
* is equal to the lower. & (CSF_ACTIVE | CSF_TRUE)));
*/ } else if (ea.cmdidx == CMD_finally) {
skip = false;
} else if (ea.cmdidx != CMD_endif
&& ea.cmdidx != CMD_endfor
&& ea.cmdidx != CMD_endtry
&& ea.cmdidx != CMD_endwhile) {
skip = ea.skip;
}
if (!skip) {
if (getline_equal(fgetline, cookie, get_func_line)) {
func_line_exec(getline_cookie(fgetline, cookie));
} else if (getline_equal(fgetline, cookie, getsourceline)) {
script_line_exec();
}
}
}
// May go to debug mode. If this happens and the ">quit" debug command is
// used, throw an interrupt exception and skip the next command.
dbg_check_breakpoint(&ea);
if (!ea.skip && got_int) {
ea.skip = TRUE;
(void)do_intthrow(cstack);
}
// 4. Parse a range specifier of the form: addr [,addr] [;addr] ..
//
// where 'addr' is:
//
// % (entire file)
// $ [+-NUM]
// 'x [+-NUM] (where x denotes a currently defined mark)
// . [+-NUM]
// [+-NUM]..
// NUM
//
// The ea.cmd pointer is updated to point to the first character following the
// range spec. If an initial address is found, but no second, the upper bound
// is equal to the lower.
// ea.addr_type for user commands is set by find_ucmd // ea.addr_type for user commands is set by find_ucmd
if (!IS_USER_CMDIDX(ea.cmdidx)) { if (!IS_USER_CMDIDX(ea.cmdidx)) {
@ -2564,7 +2587,8 @@ find_ucmd (
} }
if (xp != NULL) { if (xp != NULL) {
xp->xp_arg = uc->uc_compl_arg; xp->xp_arg = uc->uc_compl_arg;
xp->xp_scriptID = uc->uc_scriptID; xp->xp_script_ctx = uc->uc_script_ctx;
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
} }
/* Do not search for further abbreviations /* Do not search for further abbreviations
* if this is an exact match. */ * if this is an exact match. */
@ -4879,7 +4903,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
cmd->uc_argt = argt; cmd->uc_argt = argt;
cmd->uc_def = def; cmd->uc_def = def;
cmd->uc_compl = compl; cmd->uc_compl = compl;
cmd->uc_scriptID = current_SID; cmd->uc_script_ctx = current_sctx;
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
cmd->uc_compl_arg = compl_arg; cmd->uc_compl_arg = compl_arg;
cmd->uc_addr_type = addr_type; cmd->uc_addr_type = addr_type;
@ -5070,9 +5095,10 @@ static void uc_list(char_u *name, size_t name_len)
IObuff[len] = '\0'; IObuff[len] = '\0';
msg_outtrans(IObuff); msg_outtrans(IObuff);
msg_outtrans_special(cmd->uc_rep, FALSE); msg_outtrans_special(cmd->uc_rep, false);
if (p_verbose > 0) if (p_verbose > 0) {
last_set_msg(cmd->uc_scriptID); last_set_msg(cmd->uc_script_ctx);
}
ui_flush(); ui_flush();
os_breakcheck(); os_breakcheck();
if (got_int) if (got_int)
@ -5716,7 +5742,7 @@ static void do_ucmd(exarg_T *eap)
size_t split_len = 0; size_t split_len = 0;
char_u *split_buf = NULL; char_u *split_buf = NULL;
ucmd_T *cmd; ucmd_T *cmd;
scid_T save_current_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
if (eap->cmdidx == CMD_USER) if (eap->cmdidx == CMD_USER)
cmd = USER_CMD(eap->useridx); cmd = USER_CMD(eap->useridx);
@ -5797,10 +5823,10 @@ static void do_ucmd(exarg_T *eap)
buf = xmalloc(totlen + 1); buf = xmalloc(totlen + 1);
} }
current_SID = cmd->uc_scriptID; current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
(void)do_cmdline(buf, eap->getline, eap->cookie, (void)do_cmdline(buf, eap->getline, eap->cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
current_SID = save_current_SID; current_sctx = save_current_sctx;
xfree(buf); xfree(buf);
xfree(split_buf); xfree(split_buf);
} }
@ -8538,6 +8564,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
#define SPEC_ABUF (SPEC_AFILE + 1) #define SPEC_ABUF (SPEC_AFILE + 1)
"<amatch>", // autocommand match name "<amatch>", // autocommand match name
#define SPEC_AMATCH (SPEC_ABUF + 1) #define SPEC_AMATCH (SPEC_ABUF + 1)
"<sflnum>", // script file line number
#define SPEC_SFLNUM (SPEC_AMATCH + 1)
}; };
for (size_t i = 0; i < ARRAY_SIZE(spec_str); ++i) { for (size_t i = 0; i < ARRAY_SIZE(spec_str); ++i) {
@ -8764,7 +8792,8 @@ eval_vars (
return NULL; return NULL;
} }
break; break;
case SPEC_SLNUM: /* line in file for ":so" command */
case SPEC_SLNUM: // line in file for ":so" command
if (sourcing_name == NULL || sourcing_lnum == 0) { if (sourcing_name == NULL || sourcing_lnum == 0) {
*errormsg = (char_u *)_("E842: no line number to use for \"<slnum>\""); *errormsg = (char_u *)_("E842: no line number to use for \"<slnum>\"");
return NULL; return NULL;
@ -8772,6 +8801,17 @@ eval_vars (
snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum); snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum);
result = (char_u *)strbuf; result = (char_u *)strbuf;
break; break;
case SPEC_SFLNUM: // line in script file
if (current_sctx.sc_lnum + sourcing_lnum == 0) {
*errormsg = (char_u *)_("E961: no line number to use for \"<sflnum>\"");
return NULL;
}
snprintf((char *)strbuf, sizeof(strbuf), "%" PRIdLINENR,
current_sctx.sc_lnum + sourcing_lnum);
result = (char_u *)strbuf;
break;
default: default:
// should not happen // should not happen
*errormsg = (char_u *)""; *errormsg = (char_u *)"";
@ -10193,7 +10233,7 @@ Dictionary commands_array(buf_T *buf)
PUT(d, "name", STRING_OBJ(cstr_to_string((char *)cmd->uc_name))); PUT(d, "name", STRING_OBJ(cstr_to_string((char *)cmd->uc_name)));
PUT(d, "definition", STRING_OBJ(cstr_to_string((char *)cmd->uc_rep))); PUT(d, "definition", STRING_OBJ(cstr_to_string((char *)cmd->uc_rep)));
PUT(d, "script_id", INTEGER_OBJ(cmd->uc_scriptID)); PUT(d, "script_id", INTEGER_OBJ(cmd->uc_script_ctx.sc_sid));
PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & BANG))); PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & BANG)));
PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & TRLBAR))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & TRLBAR)));
PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & REGSTR))); PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & REGSTR)));

View File

@ -1909,7 +1909,7 @@ static int command_line_changed(CommandLineState *s)
redrawcmdline(); redrawcmdline();
s->did_incsearch = true; s->did_incsearch = true;
} else if (s->firstc == ':' } else if (s->firstc == ':'
&& current_SID == 0 // only if interactive && current_sctx.sc_sid == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set && *p_icm != NUL // 'inccommand' is set
&& curbuf->b_p_ma // buffer is modifiable && curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password && cmdline_star == 0 // not typing a password
@ -5072,7 +5072,7 @@ static void * call_user_expand_func(user_expand_func_T user_expand_func,
char_u keep = 0; char_u keep = 0;
typval_T args[4]; typval_T args[4];
char_u *pat = NULL; char_u *pat = NULL;
int save_current_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
struct cmdline_info save_ccline; struct cmdline_info save_ccline;
if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
@ -5098,14 +5098,15 @@ static void * call_user_expand_func(user_expand_func_T user_expand_func,
save_ccline = ccline; save_ccline = ccline;
ccline.cmdbuff = NULL; ccline.cmdbuff = NULL;
ccline.cmdprompt = NULL; ccline.cmdprompt = NULL;
current_SID = xp->xp_scriptID; current_sctx = xp->xp_script_ctx;
void *const ret = user_expand_func(xp->xp_arg, 3, args); void *const ret = user_expand_func(xp->xp_arg, 3, args);
ccline = save_ccline; ccline = save_ccline;
current_SID = save_current_SID; current_sctx = save_current_sctx;
if (ccline.cmdbuff != NULL) if (ccline.cmdbuff != NULL) {
ccline.cmdbuff[ccline.cmdlen] = keep; ccline.cmdbuff[ccline.cmdlen] = keep;
}
xfree(pat); xfree(pat);
return ret; return ret;

View File

@ -103,7 +103,7 @@ typedef struct AutoCmd {
bool once; // "One shot": removed after execution bool once; // "One shot": removed after execution
char nested; // If autocommands nest here char nested; // If autocommands nest here
char last; // last command in list char last; // last command in list
scid_T scriptID; // script ID where defined sctx_T script_ctx; // script context where defined
struct AutoCmd *next; // Next AutoCmd in list struct AutoCmd *next; // Next AutoCmd in list
} AutoCmd; } AutoCmd;
@ -5422,20 +5422,25 @@ static void show_autocmd(AutoPat *ap, event_T event)
if (ac->cmd == NULL) { /* skip removed commands */ if (ac->cmd == NULL) { /* skip removed commands */
continue; continue;
} }
if (msg_col >= 14) if (msg_col >= 14) {
msg_putchar('\n'); msg_putchar('\n');
}
msg_col = 14; msg_col = 14;
if (got_int) if (got_int) {
return; return;
}
msg_outtrans(ac->cmd); msg_outtrans(ac->cmd);
if (p_verbose > 0) if (p_verbose > 0) {
last_set_msg(ac->scriptID); last_set_msg(ac->script_ctx);
if (got_int) }
if (got_int) {
return; return;
}
if (ac->next != NULL) { if (ac->next != NULL) {
msg_putchar('\n'); msg_putchar('\n');
if (got_int) if (got_int) {
return; return;
}
} }
} }
} }
@ -6241,7 +6246,8 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested,
prev_ac = &ac->next; prev_ac = &ac->next;
ac = xmalloc(sizeof(AutoCmd)); ac = xmalloc(sizeof(AutoCmd));
ac->cmd = vim_strsave(cmd); ac->cmd = vim_strsave(cmd);
ac->scriptID = current_SID; ac->script_ctx = current_sctx;
ac->script_ctx.sc_lnum += sourcing_lnum;
ac->next = NULL; ac->next = NULL;
*prev_ac = ac; *prev_ac = ac;
ac->once = once; ac->once = once;
@ -6675,7 +6681,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
static int nesting = 0; static int nesting = 0;
AutoPatCmd patcmd; AutoPatCmd patcmd;
AutoPat *ap; AutoPat *ap;
scid_T save_current_SID;
void *save_funccalp; void *save_funccalp;
char_u *save_cmdarg; char_u *save_cmdarg;
long save_cmdbang; long save_cmdbang;
@ -6860,7 +6865,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
save_sourcing_lnum = sourcing_lnum; save_sourcing_lnum = sourcing_lnum;
sourcing_lnum = 0; /* no line number here */ sourcing_lnum = 0; /* no line number here */
save_current_SID = current_SID; const sctx_T save_current_sctx = current_sctx;
if (do_profiling == PROF_YES) if (do_profiling == PROF_YES)
prof_child_enter(&wait_time); /* doesn't count for the caller itself */ prof_child_enter(&wait_time); /* doesn't count for the caller itself */
@ -6954,7 +6959,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
autocmd_fname = save_autocmd_fname; autocmd_fname = save_autocmd_fname;
autocmd_bufnr = save_autocmd_bufnr; autocmd_bufnr = save_autocmd_bufnr;
autocmd_match = save_autocmd_match; autocmd_match = save_autocmd_match;
current_SID = save_current_SID; current_sctx = save_current_sctx;
restore_funccal(save_funccalp); restore_funccal(save_funccalp);
if (do_profiling == PROF_YES) if (do_profiling == PROF_YES)
prof_child_exit(&wait_time); prof_child_exit(&wait_time);
@ -7147,7 +7152,7 @@ char_u *getnextac(int c, void *cookie, int indent)
au_del_cmd(ac); au_del_cmd(ac);
} }
autocmd_nested = ac->nested; autocmd_nested = ac->nested;
current_SID = ac->scriptID; current_sctx = ac->script_ctx;
if (ac->last) { if (ac->last) {
acp->nextcmd = NULL; acp->nextcmd = NULL;
} else { } else {

View File

@ -2957,7 +2957,8 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mp->m_silent = args->silent; mp->m_silent = args->silent;
mp->m_mode = mode; mp->m_mode = mode;
mp->m_expr = args->expr; mp->m_expr = args->expr;
mp->m_script_ID = current_SID; mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
did_it = true; did_it = true;
} }
} }
@ -3032,7 +3033,8 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mp->m_silent = args->silent; mp->m_silent = args->silent;
mp->m_mode = mode; mp->m_mode = mode;
mp->m_expr = args->expr; mp->m_expr = args->expr;
mp->m_script_ID = current_SID; mp->m_script_ctx = current_sctx;
mp->m_script_ctx.sc_lnum += sourcing_lnum;
// add the new entry in front of the abbrlist or maphash[] list // add the new entry in front of the abbrlist or maphash[] list
if (is_abbrev) { if (is_abbrev) {
@ -3375,9 +3377,10 @@ showmap (
msg_outtrans_special(s, FALSE); msg_outtrans_special(s, FALSE);
xfree(s); xfree(s);
} }
if (p_verbose > 0) if (p_verbose > 0) {
last_set_msg(mp->m_script_ID); last_set_msg(mp->m_script_ctx);
ui_flush(); /* show one line at a time */ }
ui_flush(); // show one line at a time
} }
/// Check if a map exists that has given string in the rhs /// Check if a map exists that has given string in the rhs

View File

@ -332,8 +332,8 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_LUA -7 // for Lua scripts/chunks #define SID_LUA -7 // for Lua scripts/chunks
#define SID_API_CLIENT -8 // for API clients #define SID_API_CLIENT -8 // for API clients
// ID of script being sourced or was sourced to define the current function. // Script CTX being sourced or was sourced to define the current function.
EXTERN scid_T current_SID INIT(= 0); EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 });
// ID of the current channel making a client API call // ID of the current channel making a client API call
EXTERN uint64_t current_channel_id INIT(= 0); EXTERN uint64_t current_channel_id INIT(= 0);
@ -342,7 +342,7 @@ EXTERN bool did_source_packages INIT(= false);
// Scope information for the code that indirectly triggered the current // Scope information for the code that indirectly triggered the current
// provider function call // provider function call
EXTERN struct caller_scope { EXTERN struct caller_scope {
scid_T SID; sctx_T script_ctx;
uint8_t *sourcing_name, *autocmd_fname, *autocmd_match; uint8_t *sourcing_name, *autocmd_fname, *autocmd_match;
linenr_T sourcing_lnum; linenr_T sourcing_lnum;
int autocmd_bufnr; int autocmd_bufnr;

View File

@ -824,7 +824,8 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
// Allocate space for the translation. Worst case a single character is // Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end. // replaced by 6 bytes (shifted special key), plus a NUL at the end.
result = xmalloc(from_len * 6 + 1); const size_t buf_len = from_len * 6 + 1;
result = xmalloc(buf_len);
src = from; src = from;
@ -849,14 +850,15 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
// Replace <SID> by K_SNR <script-nr> _. // Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
if (current_SID <= 0) { if (current_sctx.sc_sid <= 0) {
EMSG(_(e_usingsid)); EMSG(_(e_usingsid));
} else { } else {
src += 5; src += 5;
result[dlen++] = K_SPECIAL; result[dlen++] = K_SPECIAL;
result[dlen++] = (int)KS_EXTRA; result[dlen++] = (int)KS_EXTRA;
result[dlen++] = (int)KE_SNR; result[dlen++] = (int)KE_SNR;
sprintf((char *)result + dlen, "%" PRId64, (int64_t)current_SID); snprintf((char *)result + dlen, buf_len - dlen, "%" PRId64,
(int64_t)current_sctx.sc_sid);
dlen += STRLEN(result + dlen); dlen += STRLEN(result + dlen);
result[dlen++] = '_'; result[dlen++] = '_';
continue; continue;

View File

@ -9,6 +9,7 @@
#else #else
# ifndef INIT # ifndef INIT
# define INIT(...) __VA_ARGS__ # define INIT(...) __VA_ARGS__
# define COMMA ,
# endif # endif
#endif #endif

View File

@ -1638,11 +1638,12 @@ static void exe_pre_commands(mparm_T *parmp)
if (cnt > 0) { if (cnt > 0) {
curwin->w_cursor.lnum = 0; /* just in case.. */ curwin->w_cursor.lnum = 0; /* just in case.. */
sourcing_name = (char_u *)_("pre-vimrc command line"); sourcing_name = (char_u *)_("pre-vimrc command line");
current_SID = SID_CMDARG; current_sctx.sc_sid = SID_CMDARG;
for (i = 0; i < cnt; ++i) for (i = 0; i < cnt; i++) {
do_cmdline_cmd(cmds[i]); do_cmdline_cmd(cmds[i]);
}
sourcing_name = NULL; sourcing_name = NULL;
current_SID = 0; current_sctx.sc_sid = 0;
TIME_MSG("--cmd commands"); TIME_MSG("--cmd commands");
} }
} }
@ -1663,16 +1664,18 @@ static void exe_commands(mparm_T *parmp)
if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1)
curwin->w_cursor.lnum = 0; curwin->w_cursor.lnum = 0;
sourcing_name = (char_u *)"command line"; sourcing_name = (char_u *)"command line";
current_SID = SID_CARG; current_sctx.sc_sid = SID_CARG;
for (i = 0; i < parmp->n_commands; ++i) { current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) {
do_cmdline_cmd(parmp->commands[i]); do_cmdline_cmd(parmp->commands[i]);
if (parmp->cmds_tofree[i]) if (parmp->cmds_tofree[i])
xfree(parmp->commands[i]); xfree(parmp->commands[i]);
} }
sourcing_name = NULL; sourcing_name = NULL;
current_SID = 0; current_sctx.sc_sid = 0;
if (curwin->w_cursor.lnum == 0) if (curwin->w_cursor.lnum == 0) {
curwin->w_cursor.lnum = 1; curwin->w_cursor.lnum = 1;
}
if (!exmode_active) if (!exmode_active)
msg_scroll = FALSE; msg_scroll = FALSE;
@ -1858,12 +1861,14 @@ static int execute_env(char *env)
linenr_T save_sourcing_lnum = sourcing_lnum; linenr_T save_sourcing_lnum = sourcing_lnum;
sourcing_name = (char_u *)env; sourcing_name = (char_u *)env;
sourcing_lnum = 0; sourcing_lnum = 0;
scid_T save_sid = current_SID; const sctx_T save_current_sctx = current_sctx;
current_SID = SID_ENV; current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd((char *)initstr); do_cmdline_cmd((char *)initstr);
sourcing_name = save_sourcing_name; sourcing_name = save_sourcing_name;
sourcing_lnum = save_sourcing_lnum; sourcing_lnum = save_sourcing_lnum;
current_SID = save_sid; current_sctx = save_current_sctx;
return OK; return OK;
} }
return FAIL; return FAIL;

View File

@ -1370,7 +1370,7 @@ void ex_emenu(exarg_T *eap)
/* Found the menu, so execute. /* Found the menu, so execute.
* Use the Insert mode entry when returning to Insert mode. */ * Use the Insert mode entry when returning to Insert mode. */
if (((State & INSERT) || restart_edit) && !current_SID) { if (((State & INSERT) || restart_edit) && !current_sctx.sc_sid) {
mode = (char_u *)"Insert"; mode = (char_u *)"Insert";
idx = MENU_INDEX_INSERT; idx = MENU_INDEX_INSERT;
} else if (State & CMDLINE) { } else if (State & CMDLINE) {
@ -1431,7 +1431,7 @@ void ex_emenu(exarg_T *eap)
if (menu->strings[idx] != NULL) { if (menu->strings[idx] != NULL) {
// When executing a script or function execute the commands right now. // When executing a script or function execute the commands right now.
// Otherwise put them in the typeahead buffer. // Otherwise put them in the typeahead buffer.
if (current_SID != 0) { if (current_sctx.sc_sid != 0) {
exec_normal_cmd(menu->strings[idx], menu->noremap[idx], exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
menu->silent[idx]); menu->silent[idx]);
} else { } else {

View File

@ -200,7 +200,7 @@ typedef struct vimoption {
// local option: indirect option index // local option: indirect option index
char_u *def_val[2]; // default values for variable (vi and vim) char_u *def_val[2]; // default values for variable (vi and vim)
LastSet last_set; // script in which the option was last set LastSet last_set; // script in which the option was last set
# define SCRIPTID_INIT , 0 # define SCTX_INIT , { 0, 0, 0 }
} vimoption_T; } vimoption_T;
#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value #define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value
@ -896,7 +896,7 @@ set_option_default(
*flagsp = *flagsp & ~P_INSECURE; *flagsp = *flagsp & ~P_INSECURE;
} }
set_option_scriptID_idx(opt_idx, opt_flags, current_SID); set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
} }
/* /*
@ -1384,10 +1384,10 @@ int do_set(
if (varp == options[opt_idx].var) { if (varp == options[opt_idx].var) {
option_last_set_msg(options[opt_idx].last_set); option_last_set_msg(options[opt_idx].last_set);
} else if ((int)options[opt_idx].indir & PV_WIN) { } else if ((int)options[opt_idx].indir & PV_WIN) {
option_last_set_msg(curwin->w_p_scriptID[ option_last_set_msg(curwin->w_p_script_ctx[
(int)options[opt_idx].indir & PV_MASK]); (int)options[opt_idx].indir & PV_MASK]);
} else if ((int)options[opt_idx].indir & PV_BUF) { } else if ((int)options[opt_idx].indir & PV_BUF) {
option_last_set_msg(curbuf->b_p_scriptID[ option_last_set_msg(curbuf->b_p_script_ctx[
(int)options[opt_idx].indir & PV_MASK]); (int)options[opt_idx].indir & PV_MASK]);
} }
} }
@ -2359,13 +2359,12 @@ static void redraw_titles(void)
static int shada_idx = -1; static int shada_idx = -1;
/* // Set a string option to a new value (without checking the effect).
* Set a string option to a new value (without checking the effect). // The string is copied into allocated memory.
* The string is copied into allocated memory. // if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used.
* if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used. // When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When
* When "set_sid" is zero set the scriptID to current_SID. When "set_sid" is // "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to
* SID_NONE don't set the scriptID. Otherwise set the scriptID to "set_sid". // "set_sid".
*/
void void
set_string_option_direct( set_string_option_direct(
char_u *name, char_u *name,
@ -2417,9 +2416,18 @@ set_string_option_direct(
free_string_option(*varp); free_string_option(*varp);
*varp = empty_option; *varp = empty_option;
} }
if (set_sid != SID_NONE) if (set_sid != SID_NONE) {
set_option_scriptID_idx(idx, opt_flags, sctx_T script_ctx;
set_sid == 0 ? current_SID : set_sid);
if (set_sid == 0) {
script_ctx = current_sctx;
} else {
script_ctx.sc_sid = set_sid;
script_ctx.sc_seq = 0;
script_ctx.sc_lnum = 0;
}
set_option_sctx_idx(idx, opt_flags, script_ctx);
}
} }
} }
@ -3296,12 +3304,10 @@ ambw_end:
} }
} else { } else {
// Remember where the option was set. // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID); set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
/* // Free string options that are in allocated memory.
* Free string options that are in allocated memory. // Use "free_oldval", because recursiveness may change the flags under
* Use "free_oldval", because recursiveness may change the flags under // our fingers (esp. init_highlight()).
* our fingers (esp. init_highlight()).
*/
if (free_oldval) { if (free_oldval) {
free_string_option(oldval); free_string_option(oldval);
} }
@ -3786,15 +3792,16 @@ static bool parse_winhl_opt(win_T *wp)
return true; return true;
} }
/* // Set the script_ctx for an option, taking care of setting the buffer- or
* Set the scriptID for an option, taking care of setting the buffer- or // window-local value.
* window-local value. static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
*/
static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id)
{ {
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int indir = (int)options[opt_idx].indir; int indir = (int)options[opt_idx].indir;
const LastSet last_set = { id, current_channel_id }; const LastSet last_set = { .script_ctx =
{ script_ctx.sc_sid, script_ctx.sc_seq,
script_ctx.sc_lnum + sourcing_lnum },
current_channel_id };
// Remember where the option was set. For local options need to do that // Remember where the option was set. For local options need to do that
// in the buffer or window structure. // in the buffer or window structure.
@ -3803,9 +3810,9 @@ static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id)
} }
if (both || (opt_flags & OPT_LOCAL)) { if (both || (opt_flags & OPT_LOCAL)) {
if (indir & PV_BUF) { if (indir & PV_BUF) {
curbuf->b_p_scriptID[indir & PV_MASK] = last_set; curbuf->b_p_script_ctx[indir & PV_MASK] = last_set;
} else if (indir & PV_WIN) { } else if (indir & PV_WIN) {
curwin->w_p_scriptID[indir & PV_MASK] = last_set; curwin->w_p_script_ctx[indir & PV_MASK] = last_set;
} }
} }
} }
@ -3832,7 +3839,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp,
*(int *)varp = value; // set the new value *(int *)varp = value; // set the new value
// Remember where the option was set. // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID); set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
// May set global value for local option. // May set global value for local option.
@ -4310,7 +4317,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
*pp = value; *pp = value;
// Remember where the option was set. // Remember where the option was set.
set_option_scriptID_idx(opt_idx, opt_flags, current_SID); set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
// For these options we want to fix some invalid values. // For these options we want to fix some invalid values.
if (pp == &p_window) { if (pp == &p_window) {

View File

@ -838,7 +838,7 @@ enum {
/// Stores an identifier of a script or channel that last set an option. /// Stores an identifier of a script or channel that last set an option.
typedef struct { typedef struct {
scid_T script_id; /// Script ID or one of SID_* special values. sctx_T script_ctx; /// script context where the option was last set
uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT. uint64_t channel_id; /// Only used when script_id is SID_API_CLIENT.
} LastSet; } LastSet;

View File

@ -60,7 +60,7 @@ struct hl_group {
int sg_attr; ///< Screen attr @see ATTR_ENTRY int sg_attr; ///< Screen attr @see ATTR_ENTRY
int sg_link; ///< link to this highlight group ID int sg_link; ///< link to this highlight group ID
int sg_set; ///< combination of flags in \ref SG_SET int sg_set; ///< combination of flags in \ref SG_SET
scid_T sg_scriptID; ///< script in which the group was last set sctx_T sg_script_ctx; ///< script in which the group was last set
// for terminal UIs // for terminal UIs
int sg_cterm; ///< "cterm=" highlighting attr int sg_cterm; ///< "cterm=" highlighting attr
///< (combination of \ref HlAttrFlags) ///< (combination of \ref HlAttrFlags)
@ -6565,13 +6565,15 @@ void do_highlight(const char *line, const bool forceit, const bool init)
EMSG(_("E414: group has settings, highlight link ignored")); EMSG(_("E414: group has settings, highlight link ignored"));
} }
} else if (HL_TABLE()[from_id - 1].sg_link != to_id } else if (HL_TABLE()[from_id - 1].sg_link != to_id
|| HL_TABLE()[from_id - 1].sg_scriptID != current_SID || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
!= current_sctx.sc_sid
|| HL_TABLE()[from_id - 1].sg_cleared) { || HL_TABLE()[from_id - 1].sg_cleared) {
if (!init) { if (!init) {
HL_TABLE()[from_id - 1].sg_set |= SG_LINK; HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
} }
HL_TABLE()[from_id - 1].sg_link = to_id; HL_TABLE()[from_id - 1].sg_link = to_id;
HL_TABLE()[from_id - 1].sg_scriptID = current_SID; HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
HL_TABLE()[from_id - 1].sg_cleared = false; HL_TABLE()[from_id - 1].sg_cleared = false;
redraw_all_later(SOME_VALID); redraw_all_later(SOME_VALID);
@ -6950,7 +6952,8 @@ void do_highlight(const char *line, const bool forceit, const bool init)
} else { } else {
set_hl_attr(idx); set_hl_attr(idx);
} }
HL_TABLE()[idx].sg_scriptID = current_SID; HL_TABLE()[idx].sg_script_ctx = current_sctx;
HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
} }
xfree(key); xfree(key);
xfree(arg); xfree(arg);
@ -7034,7 +7037,8 @@ static void highlight_clear(int idx)
// Clear the script ID only when there is no link, since that is not // Clear the script ID only when there is no link, since that is not
// cleared. // cleared.
if (HL_TABLE()[idx].sg_link == 0) { if (HL_TABLE()[idx].sg_link == 0) {
HL_TABLE()[idx].sg_scriptID = 0; HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
} }
} }
@ -7084,8 +7088,9 @@ static void highlight_list_one(const int id)
if (!didh) if (!didh)
highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", ""); highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
if (p_verbose > 0) if (p_verbose > 0) {
last_set_msg(sgp->sg_scriptID); last_set_msg(sgp->sg_script_ctx);
}
} }
/// Outputs a highlight when doing ":hi MyHighlight" /// Outputs a highlight when doing ":hi MyHighlight"

View File

View File

@ -11,6 +11,7 @@ source test_ex_equal.vim
source test_ex_undo.vim source test_ex_undo.vim
source test_ex_z.vim source test_ex_z.vim
source test_execute_func.vim source test_execute_func.vim
source test_expand_func.vim
source test_expr.vim source test_expr.vim
source test_feedkeys.vim source test_feedkeys.vim
source test_filter_cmd.vim source test_filter_cmd.vim

View File

@ -0,0 +1,66 @@
" Tests for expand()
let s:sfile = expand('<sfile>')
let s:slnum = str2nr(expand('<slnum>'))
let s:sflnum = str2nr(expand('<sflnum>'))
func s:expand_sfile()
return expand('<sfile>')
endfunc
func s:expand_slnum()
return str2nr(expand('<slnum>'))
endfunc
func s:expand_sflnum()
return str2nr(expand('<sflnum>'))
endfunc
func Test_expand_sfile()
call assert_match('test_expand_func\.vim$', s:sfile)
call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
" Call in script-local function
call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<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')))
delcommand Sfile
endfunc
func Test_expand_slnum()
call assert_equal(4, s:slnum)
call assert_equal(2, str2nr(expand('<slnum>')))
" Line-continuation
call assert_equal(
\ 5,
\ str2nr(expand('<slnum>')))
" Call in script-local function
call assert_equal(1, s:expand_slnum())
" Call in command
command Slnum echo expand('<slnum>')
call assert_equal(14, str2nr(trim(execute('Slnum'))))
delcommand Slnum
endfunc
func Test_expand_sflnum()
call assert_equal(5, s:sflnum)
call assert_equal(52, str2nr(expand('<sflnum>')))
" Line-continuation
call assert_equal(
\ 55,
\ str2nr(expand('<sflnum>')))
" Call in script-local function
call assert_equal(16, s:expand_sflnum())
" Call in command
command Flnum echo expand('<sflnum>')
call assert_equal(64, str2nr(trim(execute('Flnum'))))
delcommand Flnum
endfunc

View File

@ -1067,6 +1067,33 @@ func Test_func_range_with_edit()
bwipe! bwipe!
endfunc endfunc
func Test_func_exists_on_reload()
call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists')
call assert_equal(0, exists('*ExistingFunction'))
source Xfuncexists
call assert_equal(1, exists('*ExistingFunction'))
" Redefining a function when reloading a script is OK.
source Xfuncexists
call assert_equal(1, exists('*ExistingFunction'))
" But redefining in another script is not OK.
call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2')
call assert_fails('source Xfuncexists2', 'E122:')
delfunc ExistingFunction
call assert_equal(0, exists('*ExistingFunction'))
call writefile([
\ 'func ExistingFunction()', 'echo "yes"', 'endfunc',
\ 'func ExistingFunction()', 'echo "no"', 'endfunc',
\ ], 'Xfuncexists')
call assert_fails('source Xfuncexists', 'E122:')
call assert_equal(1, exists('*ExistingFunction'))
call delete('Xfuncexists2')
call delete('Xfuncexists')
delfunc ExistingFunction
endfunc
sandbox function Fsandbox() sandbox function Fsandbox()
normal ix normal ix
endfunc endfunc

View File

@ -10,23 +10,30 @@ function Test_maparg()
set cpo-=< set cpo-=<
set encoding=utf8 set encoding=utf8
" Test maparg() with a string result " Test maparg() with a string result
let sid = s:SID()
let lnum = expand('<sflnum>')
map foo<C-V> is<F4>foo map foo<C-V> is<F4>foo
vnoremap <script> <buffer> <expr> <silent> bar isbar vnoremap <script> <buffer> <expr> <silent> bar isbar
let sid = s:SID()
call assert_equal("is<F4>foo", maparg('foo<C-V>')) call assert_equal("is<F4>foo", maparg('foo<C-V>'))
call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>', call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>',
\ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'rhs': 'is<F4>foo', \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1,
\ 'buffer': 0}, maparg('foo<C-V>', '', 0, 1)) \ 'rhs': 'is<F4>foo', 'buffer': 0},
\ maparg('foo<C-V>', '', 0, 1))
call assert_equal({'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v', call assert_equal({'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v',
\ 'nowait': 0, 'expr': 1, 'sid': sid, 'rhs': 'isbar', 'buffer': 1}, \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2,
\ 'rhs': 'isbar', 'buffer': 1},
\ maparg('bar', '', 0, 1)) \ maparg('bar', '', 0, 1))
let lnum = expand('<sflnum>')
map <buffer> <nowait> foo bar map <buffer> <nowait> foo bar
call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ', call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ',
\ 'nowait': 1, 'expr': 0, 'sid': sid, 'rhs': 'bar', 'buffer': 1}, \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar',
\ 'buffer': 1},
\ maparg('foo', '', 0, 1)) \ maparg('foo', '', 0, 1))
let lnum = expand('<sflnum>')
tmap baz foo tmap baz foo
call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'baz', 'mode': 't', call assert_equal({'silent': 0, 'noremap': 0, 'lhs': 'baz', 'mode': 't',
\ 'nowait': 0, 'expr': 0, 'sid': sid, 'rhs': 'foo', 'buffer': 0}, \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo',
\ 'buffer': 0},
\ maparg('baz', 't', 0, 1)) \ maparg('baz', 't', 0, 1))
map abc x<char-114>x map abc x<char-114>x

View File

@ -216,7 +216,7 @@ func Test_set_completion()
" Expand files and directories. " Expand files and directories.
call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx') call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('./samples/ ./sautest/ ./setup.vim ./shared.vim', @:) call assert_match('./samples/ ./sautest/ ./screendump.vim ./setup.vim ./shared.vim', @:)
call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx') call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:)

View File

@ -3,6 +3,8 @@ if !has('profile')
finish finish
endif endif
source screendump.vim
func Test_profile_func() func Test_profile_func()
let lines = [ let lines = [
\ 'profile start Xprofile_func.log', \ 'profile start Xprofile_func.log',
@ -49,36 +51,260 @@ func Test_profile_func()
" - Unlike Foo3(), Foo2() should not be deleted since there is a check " - Unlike Foo3(), Foo2() should not be deleted since there is a check
" for v:profiling. " for v:profiling.
" - Bar() is not reported since it does not match "profile func Foo*". " - Bar() is not reported since it does not match "profile func Foo*".
call assert_equal(28, len(lines)) call assert_equal(30, len(lines))
call assert_equal('FUNCTION Foo1()', lines[0]) call assert_equal('FUNCTION Foo1()', lines[0])
call assert_equal('Called 2 times', lines[1]) call assert_match('Defined:.*Xprofile_func.vim', lines[1])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2]) call assert_equal('Called 2 times', lines[2])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[3])
call assert_equal('', lines[4]) call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[4])
call assert_equal('count total (s) self (s)', lines[5]) call assert_equal('', lines[5])
call assert_equal('', lines[6]) call assert_equal('count total (s) self (s)', lines[6])
call assert_equal('FUNCTION Foo2()', lines[7]) call assert_equal('', lines[7])
call assert_equal('Called 1 time', lines[8]) call assert_equal('FUNCTION Foo2()', lines[8])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[9]) call assert_equal('Called 1 time', lines[10])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[10]) call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[11])
call assert_equal('', lines[11]) call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[12])
call assert_equal('count total (s) self (s)', lines[12]) call assert_equal('', lines[13])
call assert_match('^\s*1\s\+.*\slet l:count = 100$', lines[13]) call assert_equal('count total (s) self (s)', lines[14])
call assert_match('^\s*101\s\+.*\swhile l:count > 0$', lines[14]) call assert_match('^\s*1\s\+.*\slet l:count = 100$', lines[15])
call assert_match('^\s*100\s\+.*\s let l:count = l:count - 1$', lines[15]) call assert_match('^\s*101\s\+.*\swhile l:count > 0$', lines[16])
call assert_match('^\s*100\s\+.*\sendwhile$', lines[16]) call assert_match('^\s*100\s\+.*\s let l:count = l:count - 1$', lines[17])
call assert_equal('', lines[17]) call assert_match('^\s*101\s\+.*\sendwhile$', lines[18])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[18]) call assert_equal('', lines[19])
call assert_equal('count total (s) self (s) function', lines[19]) call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[20])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[20]) call assert_equal('count total (s) self (s) function', lines[21])
call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[21]) call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[22])
call assert_equal('', lines[22]) call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[23])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[23]) call assert_equal('', lines[24])
call assert_equal('count total (s) self (s) function', lines[24]) call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[25])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[25]) call assert_equal('count total (s) self (s) function', lines[26])
call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[26]) call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[27])
call assert_equal('', lines[27]) call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[28])
call assert_equal('', lines[29])
call delete('Xprofile_func.vim')
call delete('Xprofile_func.log')
endfunc
func Test_profile_func_with_ifelse()
let lines = [
\ "func! Foo1()",
\ " if 1",
\ " let x = 0",
\ " elseif 1",
\ " let x = 1",
\ " else",
\ " let x = 2",
\ " endif",
\ "endfunc",
\ "func! Foo2()",
\ " if 0",
\ " let x = 0",
\ " elseif 1",
\ " let x = 1",
\ " else",
\ " let x = 2",
\ " endif",
\ "endfunc",
\ "func! Foo3()",
\ " if 0",
\ " let x = 0",
\ " elseif 0",
\ " let x = 1",
\ " else",
\ " let x = 2",
\ " endif",
\ "endfunc",
\ "call Foo1()",
\ "call Foo2()",
\ "call Foo3()",
\ ]
call writefile(lines, 'Xprofile_func.vim')
call system(v:progpath
\ . ' -es -u NONE -U NONE -i NONE --noplugin'
\ . ' -c "profile start Xprofile_func.log"'
\ . ' -c "profile func Foo*"'
\ . ' -c "so Xprofile_func.vim"'
\ . ' -c "qall!"')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_func.log')
" - Foo1() should pass 'if' block.
" - Foo2() should pass 'elseif' block.
" - Foo3() should pass 'else' block.
call assert_equal(57, len(lines))
call assert_equal('FUNCTION Foo1()', lines[0])
call assert_match('Defined:.*Xprofile_func.vim', lines[1])
call assert_equal('Called 1 time', lines[2])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[3])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[4])
call assert_equal('', lines[5])
call assert_equal('count total (s) self (s)', lines[6])
call assert_match('^\s*1\s\+.*\sif 1$', lines[7])
call assert_match('^\s*1\s\+.*\s let x = 0$', lines[8])
call assert_match( '^\s\+elseif 1$', lines[9])
call assert_match( '^\s\+let x = 1$', lines[10])
call assert_match( '^\s\+else$', lines[11])
call assert_match( '^\s\+let x = 2$', lines[12])
call assert_match('^\s*1\s\+.*\sendif$', lines[13])
call assert_equal('', lines[14])
call assert_equal('FUNCTION Foo2()', lines[15])
call assert_equal('Called 1 time', lines[17])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[18])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[19])
call assert_equal('', lines[20])
call assert_equal('count total (s) self (s)', lines[21])
call assert_match('^\s*1\s\+.*\sif 0$', lines[22])
call assert_match( '^\s\+let x = 0$', lines[23])
call assert_match('^\s*1\s\+.*\selseif 1$', lines[24])
call assert_match('^\s*1\s\+.*\s let x = 1$', lines[25])
call assert_match( '^\s\+else$', lines[26])
call assert_match( '^\s\+let x = 2$', lines[27])
call assert_match('^\s*1\s\+.*\sendif$', lines[28])
call assert_equal('', lines[29])
call assert_equal('FUNCTION Foo3()', lines[30])
call assert_equal('Called 1 time', lines[32])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[33])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[34])
call assert_equal('', lines[35])
call assert_equal('count total (s) self (s)', lines[36])
call assert_match('^\s*1\s\+.*\sif 0$', lines[37])
call assert_match( '^\s\+let x = 0$', lines[38])
call assert_match('^\s*1\s\+.*\selseif 0$', lines[39])
call assert_match( '^\s\+let x = 1$', lines[40])
call assert_match('^\s*1\s\+.*\selse$', lines[41])
call assert_match('^\s*1\s\+.*\s let x = 2$', lines[42])
call assert_match('^\s*1\s\+.*\sendif$', lines[43])
call assert_equal('', lines[44])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
call assert_equal('count total (s) self (s) function', lines[46])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[47])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[48])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[49])
call assert_equal('', lines[50])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[51])
call assert_equal('count total (s) self (s) function', lines[52])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[53])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[54])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[55])
call assert_equal('', lines[56])
call delete('Xprofile_func.vim')
call delete('Xprofile_func.log')
endfunc
func Test_profile_func_with_trycatch()
let lines = [
\ "func! Foo1()",
\ " try",
\ " let x = 0",
\ " catch",
\ " let x = 1",
\ " finally",
\ " let x = 2",
\ " endtry",
\ "endfunc",
\ "func! Foo2()",
\ " try",
\ " throw 0",
\ " catch",
\ " let x = 1",
\ " finally",
\ " let x = 2",
\ " endtry",
\ "endfunc",
\ "func! Foo3()",
\ " try",
\ " throw 0",
\ " catch",
\ " throw 1",
\ " finally",
\ " let x = 2",
\ " endtry",
\ "endfunc",
\ "call Foo1()",
\ "call Foo2()",
\ "try",
\ " call Foo3()",
\ "catch",
\ "endtry",
\ ]
call writefile(lines, 'Xprofile_func.vim')
call system(v:progpath
\ . ' -es -u NONE -U NONE -i NONE --noplugin'
\ . ' -c "profile start Xprofile_func.log"'
\ . ' -c "profile func Foo*"'
\ . ' -c "so Xprofile_func.vim"'
\ . ' -c "qall!"')
call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_func.log')
" - Foo1() should pass 'try' 'finally' blocks.
" - Foo2() should pass 'catch' 'finally' blocks.
" - Foo3() should not pass 'endtry'.
call assert_equal(57, len(lines))
call assert_equal('FUNCTION Foo1()', lines[0])
call assert_match('Defined:.*Xprofile_func.vim', lines[1])
call assert_equal('Called 1 time', lines[2])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[3])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[4])
call assert_equal('', lines[5])
call assert_equal('count total (s) self (s)', lines[6])
call assert_match('^\s*1\s\+.*\stry$', lines[7])
call assert_match('^\s*1\s\+.*\s let x = 0$', lines[8])
call assert_match( '^\s\+catch$', lines[9])
call assert_match( '^\s\+let x = 1$', lines[10])
call assert_match('^\s*1\s\+.*\sfinally$', lines[11])
call assert_match('^\s*1\s\+.*\s let x = 2$', lines[12])
call assert_match('^\s*1\s\+.*\sendtry$', lines[13])
call assert_equal('', lines[14])
call assert_equal('FUNCTION Foo2()', lines[15])
call assert_equal('Called 1 time', lines[17])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[18])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[19])
call assert_equal('', lines[20])
call assert_equal('count total (s) self (s)', lines[21])
call assert_match('^\s*1\s\+.*\stry$', lines[22])
call assert_match('^\s*1\s\+.*\s throw 0$', lines[23])
call assert_match('^\s*1\s\+.*\scatch$', lines[24])
call assert_match('^\s*1\s\+.*\s let x = 1$', lines[25])
call assert_match('^\s*1\s\+.*\sfinally$', lines[26])
call assert_match('^\s*1\s\+.*\s let x = 2$', lines[27])
call assert_match('^\s*1\s\+.*\sendtry$', lines[28])
call assert_equal('', lines[29])
call assert_equal('FUNCTION Foo3()', lines[30])
call assert_equal('Called 1 time', lines[32])
call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[33])
call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[34])
call assert_equal('', lines[35])
call assert_equal('count total (s) self (s)', lines[36])
call assert_match('^\s*1\s\+.*\stry$', lines[37])
call assert_match('^\s*1\s\+.*\s throw 0$', lines[38])
call assert_match('^\s*1\s\+.*\scatch$', lines[39])
call assert_match('^\s*1\s\+.*\s throw 1$', lines[40])
call assert_match('^\s*1\s\+.*\sfinally$', lines[41])
call assert_match('^\s*1\s\+.*\s let x = 2$', lines[42])
call assert_match( '^\s\+endtry$', lines[43])
call assert_equal('', lines[44])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
call assert_equal('count total (s) self (s) function', lines[46])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[47])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[48])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[49])
call assert_equal('', lines[50])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[51])
call assert_equal('count total (s) self (s) function', lines[52])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[53])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[54])
call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo.()$', lines[55])
call assert_equal('', lines[56])
call delete('Xprofile_func.vim') call delete('Xprofile_func.vim')
call delete('Xprofile_func.log') call delete('Xprofile_func.log')
@ -123,7 +349,7 @@ func Test_profile_file()
call assert_equal(' " a comment', lines[9]) call assert_equal(' " a comment', lines[9])
" if self and total are equal we only get one number " if self and total are equal we only get one number
call assert_match('^\s*20\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[10]) call assert_match('^\s*20\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[10])
call assert_match('^\s*20\s\+\d\+\.\d\+\s\+endfor$', lines[11]) call assert_match('^\s*22\s\+\d\+\.\d\+\s\+endfor$', lines[11])
" if self and total are equal we only get one number " if self and total are equal we only get one number
call assert_match('^\s*2\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[12]) call assert_match('^\s*2\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[12])
call assert_equal('', lines[13]) call assert_equal('', lines[13])
@ -249,18 +475,19 @@ func Test_profdel_func()
call assert_equal(0, v:shell_error) call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_file.log') let lines = readfile('Xprofile_file.log')
call assert_equal(24, len(lines)) call assert_equal(26, len(lines))
" Check that: " Check that:
" - Foo1() is called twice (profdel not invoked) " - Foo1() is called twice (profdel not invoked)
" - Foo2() is called once (profdel invoked after it was called) " - Foo2() is called once (profdel invoked after it was called)
" - Foo3() is not called (profdel invoked before it was called) " - Foo3() is not called (profdel invoked before it was called)
call assert_equal('FUNCTION Foo1()', lines[0]) call assert_equal('FUNCTION Foo1()', lines[0])
call assert_equal('Called 2 times', lines[1]) call assert_match('Defined:.*Xprofile_file.vim', lines[1])
call assert_equal('FUNCTION Foo2()', lines[7]) call assert_equal('Called 2 times', lines[2])
call assert_equal('Called 1 time', lines[8]) call assert_equal('FUNCTION Foo2()', lines[8])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[14]) call assert_equal('Called 1 time', lines[10])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[19]) call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[16])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[21])
call delete('Xprofile_file.vim') call delete('Xprofile_file.vim')
call delete('Xprofile_file.log') call delete('Xprofile_file.log')
@ -282,13 +509,42 @@ func Test_profdel_star()
call assert_equal(0, v:shell_error) call assert_equal(0, v:shell_error)
let lines = readfile('Xprofile_file.log') let lines = readfile('Xprofile_file.log')
call assert_equal(15, len(lines)) call assert_equal(16, len(lines))
call assert_equal('FUNCTION Foo()', lines[0]) call assert_equal('FUNCTION Foo()', lines[0])
call assert_equal('Called 1 time', lines[1]) call assert_match('Defined:.*Xprofile_file.vim', lines[1])
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[7]) call assert_equal('Called 1 time', lines[2])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[11]) call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[8])
call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[12])
call delete('Xprofile_file.vim') call delete('Xprofile_file.vim')
call delete('Xprofile_file.log') call delete('Xprofile_file.log')
endfunc endfunc
" When typing the function it won't have a script ID, test that this works.
func Test_profile_typed_func()
if !CanRunVimInTerminal()
throw 'Skipped: cannot run Vim in a terminal window'
endif
let lines =<< trim END
profile start XprofileTypedFunc
END
call writefile(lines, 'XtestProfile')
let buf = RunVimInTerminal('-S XtestProfile', #{})
call term_sendkeys(buf, ":func DoSomething()\<CR>"
\ .. "echo 'hello'\<CR>"
\ .. "endfunc\<CR>")
call term_sendkeys(buf, ":profile func DoSomething\<CR>")
call term_sendkeys(buf, ":call DoSomething()\<CR>")
call term_wait(buf, 200)
call StopVimInTerminal(buf)
let lines = readfile('XprofileTypedFunc')
call assert_equal("FUNCTION DoSomething()", lines[0])
call assert_equal("Called 1 time", lines[1])
" clean up
call delete('XprofileTypedFunc')
call delete('XtestProfile')
endfunc

View File

@ -29,6 +29,7 @@ describe('nvim_get_keymap', function()
nowait=0, nowait=0,
mode='n', mode='n',
noremap=1, noremap=1,
lnum=0,
} }
it('returns empty list when no map', function() it('returns empty list when no map', function()
@ -250,6 +251,7 @@ describe('nvim_get_keymap', function()
buffer=0, buffer=0,
nowait=0, nowait=0,
noremap=1, noremap=1,
lnum=0,
} }
local function cpomap(lhs, rhs, mode) local function cpomap(lhs, rhs, mode)
local ret = shallowcopy(cpo_table) local ret = shallowcopy(cpo_table)
@ -306,6 +308,7 @@ describe('nvim_get_keymap', function()
buffer=0, buffer=0,
nowait=0, nowait=0,
noremap=1, noremap=1,
lnum=0,
} }
command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>') command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
eq({space_table}, meths.get_keymap('n')) eq({space_table}, meths.get_keymap('n'))
@ -345,6 +348,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
to_return.expr = not opts.expr and 0 or 1 to_return.expr = not opts.expr and 0 or 1
to_return.sid = not opts.sid and 0 or opts.sid to_return.sid = not opts.sid and 0 or opts.sid
to_return.buffer = not opts.buffer and 0 or opts.buffer to_return.buffer = not opts.buffer and 0 or opts.buffer
to_return.lnum = not opts.lnum and 0 or opts.lnum
return to_return return to_return
end end

View File

@ -21,6 +21,7 @@ describe('maparg()', function()
nowait=0, nowait=0,
mode='n', mode='n',
noremap=1, noremap=1,
lnum=0,
} }
it('returns a dictionary', function() it('returns a dictionary', function()
@ -148,6 +149,7 @@ describe('maparg()', function()
nowait = 0, nowait = 0,
sid = 0, sid = 0,
silent = 0, silent = 0,
lnum = 0,
} }
end end

View File

@ -49,9 +49,9 @@ describe('maparg()', function()
-- Assert buffer contents. -- Assert buffer contents.
expect([[ expect([[
is<F4>foo is<F4>foo
{'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>', 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': 0, 'rhs': 'is<F4>foo', 'buffer': 0} {'lnum': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo<C-V>', 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': 0, 'rhs': 'is<F4>foo', 'buffer': 0}
{'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v', 'nowait': 0, 'expr': 1, 'sid': 0, 'rhs': 'isbar', 'buffer': 1} {'lnum': 0, 'silent': 1, 'noremap': 1, 'lhs': 'bar', 'mode': 'v', 'nowait': 0, 'expr': 1, 'sid': 0, 'rhs': 'isbar', 'buffer': 1}
{'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ', 'nowait': 1, 'expr': 0, 'sid': 0, 'rhs': 'bar', 'buffer': 1} {'lnum': 0, 'silent': 0, 'noremap': 0, 'lhs': 'foo', 'mode': ' ', 'nowait': 1, 'expr': 0, 'sid': 0, 'rhs': 'bar', 'buffer': 1}
xrx xrx
yRy yRy
abcd]]) abcd]])