Merge pull request #19763 from zeertzjq/vim-8.2.1297

vim-patch:8.2.{1297,1653,1658}: expand('<stack>')
This commit is contained in:
zeertzjq 2022-08-15 10:39:41 +08:00 committed by GitHub
commit a6f0e32d23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 127 additions and 50 deletions

View File

@ -2010,6 +2010,7 @@ expand({string} [, {nosuf} [, {list}]]) *expand()*
a function a function
<SID> "<SNR>123_" where "123" is the <SID> "<SNR>123_" where "123" is the
current script ID |<SID>| current script ID |<SID>|
<stack> call stack
<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

View File

@ -879,12 +879,18 @@ Note: these are typed literally, they are not special keys!
*:<sfile>* *<sfile>* *:<sfile>* *<sfile>*
<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 the call stack,
"function {function-name}[{lnum}]" as with <stack> (this is for backwards compatibility, using
function call nesting is indicated like this: <stack> is preferred).
"function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
Note that filename-modifiers are useless when <sfile> is Note that filename-modifiers are useless when <sfile> is
used inside a function. not used inside a script.
*:<stack>* *<stack>*
<stack> is replaced with the call stack, using
"function {function-name}[{lnum}]" for a function line
and "script {file-name}[{lnum}]" for a script line, and
".." in between items. E.g.:
"function {function-name1}[{lnum}]..{function-name2}[{lnum}]"
If there is no call stack you get error *E489* .
*:<slnum>* *<slnum>* *:<slnum>* *<slnum>*
<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*

View File

@ -99,7 +99,7 @@ void do_debug(char_u *cmd)
xfree(debug_newval); xfree(debug_newval);
debug_newval = NULL; debug_newval = NULL;
} }
char *sname = estack_sfile(); char *sname = estack_sfile(ESTACK_NONE);
if (sname != NULL) { if (sname != NULL) {
msg(sname); msg(sname);
} }
@ -324,7 +324,7 @@ static void do_checkbacktracelevel(void)
debug_backtrace_level = 0; debug_backtrace_level = 0;
msg(_("frame is zero")); msg(_("frame is zero"));
} else { } else {
char *sname = estack_sfile(); char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname); int max = get_maxbacktrace_level(sname);
if (debug_backtrace_level > max) { if (debug_backtrace_level > max) {
@ -337,7 +337,7 @@ static void do_checkbacktracelevel(void)
static void do_showbacktrace(char_u *cmd) static void do_showbacktrace(char_u *cmd)
{ {
char *sname = estack_sfile(); char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname); int max = get_maxbacktrace_level(sname);
if (sname != NULL) { if (sname != NULL) {
int i = 0; int i = 0;

View File

@ -7719,6 +7719,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
/// '<cexpr>' to C-expression under the cursor /// '<cexpr>' to C-expression under the cursor
/// '<cfile>' to path name under the cursor /// '<cfile>' to path name under the cursor
/// '<sfile>' to sourced file name /// '<sfile>' to sourced file name
/// '<stack>' to call stack
/// '<slnum>' to sourced file line number /// '<slnum>' to sourced file line number
/// '<afile>' to file name for autocommand /// '<afile>' to file name for autocommand
/// '<abuf>' to buffer number for autocommand /// '<abuf>' to buffer number for autocommand
@ -7912,9 +7913,12 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
break; break;
case SPEC_SFILE: // file name for ":so" command case SPEC_SFILE: // file name for ":so" command
result = estack_sfile(); case SPEC_STACK: // call stack
result = estack_sfile(spec_idx == SPEC_SFILE ? ESTACK_SFILE : ESTACK_STACK);
if (result == NULL) { if (result == NULL) {
*errormsg = _("E498: no :source file name to substitute for \"<sfile>\""); *errormsg = spec_idx == SPEC_SFILE
? _("E498: no :source file name to substitute for \"<sfile>\"")
: _("E489: no call stack to substitute for \"<stack>\"");
return NULL; return NULL;
} }
resultbuf = result; // remember allocated string resultbuf = result; // remember allocated string

View File

@ -278,7 +278,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
// Get the source name and lnum now, it may change before // Get the source name and lnum now, it may change before
// reaching do_errthrow(). // reaching do_errthrow().
elem->sfile = estack_sfile(); elem->sfile = estack_sfile(ESTACK_NONE);
elem->slnum = SOURCING_LNUM; elem->slnum = SOURCING_LNUM;
} }
return true; return true;
@ -490,7 +490,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
entry->sfile = NULL; entry->sfile = NULL;
excp->throw_lnum = entry->slnum; excp->throw_lnum = entry->slnum;
} else { } else {
excp->throw_name = estack_sfile(); excp->throw_name = estack_sfile(ESTACK_NONE);
if (excp->throw_name == NULL) { if (excp->throw_name == NULL) {
excp->throw_name = xstrdup(""); excp->throw_name = xstrdup("");
} }

View File

@ -1906,7 +1906,7 @@ void nlua_set_sctx(sctx_T *current)
break; break;
} }
char *source_path = fix_fname(info->source + 1); char *source_path = fix_fname(info->source + 1);
get_current_script_id((char_u *)source_path, current); get_current_script_id(&source_path, current);
xfree(source_path); xfree(source_path);
current->sc_lnum = info->currentline; current->sc_lnum = info->currentline;
current->sc_seq = -1; current->sc_seq = -1;

View File

@ -555,7 +555,7 @@ static char *get_emsg_source(void)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{ {
if (SOURCING_NAME != NULL && other_sourcing_name()) { if (SOURCING_NAME != NULL && other_sourcing_name()) {
char *sname = estack_sfile(); char *sname = estack_sfile(ESTACK_NONE);
char *tofree = sname; char *tofree = sname;
if (sname == NULL) { if (sname == NULL) {

View File

@ -100,42 +100,60 @@ void estack_pop(void)
} }
/// Get the current value for <sfile> in allocated memory. /// Get the current value for <sfile> in allocated memory.
char *estack_sfile(void) /// @param which ESTACK_SFILE for <sfile> and ESTACK_STACK for <stack>.
char *estack_sfile(estack_arg_T which)
{ {
estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1; estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
if (entry->es_name == NULL) { if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) {
return NULL; if (entry->es_name == NULL) {
} return NULL;
if (entry->es_info.ufunc == NULL) { }
return xstrdup(entry->es_name); return xstrdup(entry->es_name);
} }
// Give information about each stack entry up to the root.
// For a function we compose the call stack, as it was done in the past: // For a function we compose the call stack, as it was done in the past:
// "function One[123]..Two[456]..Three" // "function One[123]..Two[456]..Three"
size_t len = STRLEN(entry->es_name) + 10; garray_T ga;
int idx; ga_init(&ga, sizeof(char), 100);
for (idx = exestack.ga_len - 2; idx >= 0; idx--) { etype_T last_type = ETYPE_SCRIPT;
for (int idx = 0; idx < exestack.ga_len; idx++) {
entry = ((estack_T *)exestack.ga_data) + idx; entry = ((estack_T *)exestack.ga_data) + idx;
if (entry->es_name == NULL || entry->es_info.ufunc == NULL) { if (entry->es_name != NULL) {
idx++; size_t len = strlen(entry->es_name) + 15;
break; char *type_name = "";
if (entry->es_type != last_type) {
switch (entry->es_type) {
case ETYPE_SCRIPT:
type_name = "script "; break;
case ETYPE_UFUNC:
type_name = "function "; break;
default:
type_name = ""; break;
}
last_type = entry->es_type;
}
len += strlen(type_name);
ga_grow(&ga, (int)len);
linenr_T lnum = idx == exestack.ga_len - 1
? which == ESTACK_STACK ? SOURCING_LNUM : 0
: entry->es_lnum;
char *dots = idx == exestack.ga_len - 1 ? "" : "..";
if (lnum == 0) {
// For the bottom entry of <sfile>: do not add the line number,
// it is used in <slnum>. Also leave it out when the number is
// not set.
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s",
type_name, entry->es_name, dots);
} else {
vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%" PRIdLINENR "]%s",
type_name, entry->es_name, lnum, dots);
}
ga.ga_len += (int)strlen((char *)ga.ga_data + ga.ga_len);
} }
len += STRLEN(entry->es_name) + 15;
} }
char *res = (char *)xmalloc(len); return (char *)ga.ga_data;
STRCPY(res, "function ");
size_t done;
while (idx < exestack.ga_len - 1) {
done = STRLEN(res);
entry = ((estack_T *)exestack.ga_data) + idx;
vim_snprintf(res + done, len - done, "%s[%" PRIdLINENR "]..", entry->es_name, entry->es_lnum);
idx++;
}
done = STRLEN(res);
entry = ((estack_T *)exestack.ga_data) + idx;
vim_snprintf(res + done, len - done, "%s", entry->es_name);
return res;
} }
static bool runtime_search_path_valid = false; static bool runtime_search_path_valid = false;
@ -1946,7 +1964,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
save_funccal(&funccalp_entry); save_funccal(&funccalp_entry);
const sctx_T save_current_sctx = current_sctx; const sctx_T save_current_sctx = current_sctx;
si = get_current_script_id((char_u *)fname_exp, &current_sctx); si = get_current_script_id(&fname_exp, &current_sctx);
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES) {
bool forceit = false; bool forceit = false;
@ -2059,9 +2077,9 @@ theend:
/// Check if fname was sourced before to finds its SID. /// Check if fname was sourced before to finds its SID.
/// If it's new, generate a new SID. /// If it's new, generate a new SID.
/// ///
/// @param[in] fname file path of script /// @param[in,out] fnamep pointer to file path of script
/// @param[out] ret_sctx sctx of this script /// @param[out] ret_sctx sctx of this script
scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
{ {
static int last_current_SID_seq = 0; static int last_current_SID_seq = 0;
@ -2078,13 +2096,14 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
// - If a script is deleted and another script is written, with a // - If a script is deleted and another script is written, with a
// different name, the inode may be re-used. // different name, the inode may be re-used.
si = &SCRIPT_ITEM(script_sctx.sc_sid); si = &SCRIPT_ITEM(script_sctx.sc_sid);
if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) { if (si->sn_name != NULL && FNAMECMP(si->sn_name, *fnamep) == 0) {
// Found it! // Found it!
break; break;
} }
} }
if (script_sctx.sc_sid == 0) { if (script_sctx.sc_sid == 0) {
si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid); si = new_script_item(*fnamep, &script_sctx.sc_sid);
*fnamep = xstrdup((char *)si->sn_name);
} }
if (ret_sctx != NULL) { if (ret_sctx != NULL) {
*ret_sctx = script_sctx; *ret_sctx = script_sctx;

View File

@ -42,6 +42,13 @@ extern garray_T exestack;
/// line number in the message source or zero /// line number in the message source or zero
#define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum) #define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum)
/// Argument for estack_sfile().
typedef enum {
ESTACK_NONE,
ESTACK_SFILE,
ESTACK_STACK,
} estack_arg_T;
typedef struct scriptitem_S { typedef struct scriptitem_S {
char_u *sn_name; char_u *sn_name;
bool sn_prof_on; ///< true when script is/was profiled bool sn_prof_on; ///< true when script is/was profiled

View File

@ -37,17 +37,29 @@ func Test_expand_sflnum()
delcommand Flnum delcommand Flnum
endfunc endfunc
func Test_expand_sfile() func Test_expand_sfile_and_stack()
call assert_match('test_expand_func\.vim$', s:sfile) call assert_match('test_expand_func\.vim$', s:sfile)
call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>')) let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack'
call assert_match(expected .. '$', expand('<sfile>'))
call assert_match(expected .. '\[4\]' , expand('<stack>'))
" Call in script-local function " Call in script-local function
call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile()) call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
" Call in command " Call in command
command Sfile echo expand('<sfile>') command Sfile echo expand('<sfile>')
call assert_match('^function .*\.\.Test_expand_sfile$', trim(execute('Sfile'))) call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$', trim(execute('Sfile')))
delcommand Sfile delcommand Sfile
" Use <stack> from sourced script.
let lines =<< trim END
" comment here
let g:stack_value = expand('<stack>')
END
call writefile(lines, 'Xstack')
source Xstack
call assert_match('\<Xstack\[2\]$', g:stack_value)
call delete('Xstack')
endfunc endfunc
func Test_expand_slnum() func Test_expand_slnum()

View File

@ -18,7 +18,7 @@
static void prepare_assert_error(garray_T *gap) static void prepare_assert_error(garray_T *gap)
{ {
char buf[NUMBUFLEN]; char buf[NUMBUFLEN];
char *sname = estack_sfile(); char *sname = estack_sfile(ESTACK_NONE);
ga_init(gap, 1, 100); ga_init(gap, 1, 100);
if (sname != NULL) { if (sname != NULL) {

View File

@ -13,6 +13,10 @@ local exec_lua = helpers.exec_lua
local eval = helpers.eval local eval = helpers.eval
local exec_capture = helpers.exec_capture local exec_capture = helpers.exec_capture
local neq = helpers.neq local neq = helpers.neq
local matches = helpers.matches
local iswin = helpers.iswin
local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
describe(':source', function() describe(':source', function()
before_each(function() before_each(function()
@ -39,6 +43,30 @@ describe(':source', function()
os.remove(test_file) os.remove(test_file)
end) end)
it("changing 'shellslash' changes the result of expand()", function()
if not iswin() then
pending("'shellslash' only works on Windows")
return
end
mkdir('Xshellslash')
local script = [[
let g:result1 = expand('<stack>')
set shellslash
let g:result2 = expand('<stack>')
set noshellslash
let g:result3 = expand('<stack>')
]]
write_file([[Xshellslash/Xexpand.vim]], script)
meths.set_option('shellslash', false)
command([[source Xshellslash/Xexpand.vim]])
matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result1'))
matches([[Xshellslash/Xexpand%.vim]], meths.get_var('result2'))
matches([[Xshellslash\Xexpand%.vim]], meths.get_var('result3'))
rmdir('Xshellslash')
end)
it('current buffer', function() it('current buffer', function()
insert([[ insert([[
let a = 2 let a = 2