mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #19763 from zeertzjq/vim-8.2.1297
vim-patch:8.2.{1297,1653,1658}: expand('<stack>')
This commit is contained in:
commit
a6f0e32d23
@ -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
|
||||||
|
@ -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*
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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("");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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, ¤t_sctx);
|
si = get_current_script_id(&fname_exp, ¤t_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;
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user