mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:9.1.0984: exception handling can be improved
Problem: exception handling can be improved
Solution: add v:stacktrace and getstacktrace()
closes: vim/vim#16360
663d18d610
Co-authored-by: ichizok <gclient.gaap@gmail.com>
Co-authored-by: Naruhiko Nishino <naru123456789@gmail.com>
This commit is contained in:
parent
06ff5480ce
commit
d5308637bf
15
runtime/doc/builtin.txt
generated
15
runtime/doc/builtin.txt
generated
@ -4182,6 +4182,21 @@ getscriptinfo([{opts}]) *getscriptinfo()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
(`vim.fn.getscriptinfo.ret[]`)
|
(`vim.fn.getscriptinfo.ret[]`)
|
||||||
|
|
||||||
|
getstacktrace() *getstacktrace()*
|
||||||
|
Returns the current stack trace of Vim scripts.
|
||||||
|
Stack trace is a |List|, of which each item is a |Dictionary|
|
||||||
|
with the following items:
|
||||||
|
funcref The funcref if the stack is at the function,
|
||||||
|
otherwise this item is not exist.
|
||||||
|
event The string of the event description if the
|
||||||
|
stack is at autocmd event, otherwise this item
|
||||||
|
is not exist.
|
||||||
|
lnum The line number of the script on the stack.
|
||||||
|
filepath The file path of the script on the stack.
|
||||||
|
|
||||||
|
Return: ~
|
||||||
|
(`table[]`)
|
||||||
|
|
||||||
gettabinfo([{tabnr}]) *gettabinfo()*
|
gettabinfo([{tabnr}]) *gettabinfo()*
|
||||||
If {tabnr} is not specified, then information about all the
|
If {tabnr} is not specified, then information about all the
|
||||||
tab pages is returned as a |List|. Each List item is a
|
tab pages is returned as a |List|. Each List item is a
|
||||||
|
@ -2848,7 +2848,8 @@ in the variable |v:exception|: >
|
|||||||
: echo "Number thrown. Value is" v:exception
|
: echo "Number thrown. Value is" v:exception
|
||||||
|
|
||||||
You may also be interested where an exception was thrown. This is stored in
|
You may also be interested where an exception was thrown. This is stored in
|
||||||
|v:throwpoint|. Note that "v:exception" and "v:throwpoint" are valid for the
|
|v:throwpoint|. And you can obtain the stack trace from |v:stacktrace|.
|
||||||
|
Note that "v:exception", "v:stacktrace" and "v:throwpoint" are valid for the
|
||||||
exception most recently caught as long it is not finished.
|
exception most recently caught as long it is not finished.
|
||||||
Example: >
|
Example: >
|
||||||
|
|
||||||
|
@ -1103,7 +1103,8 @@ Various: *various-functions*
|
|||||||
did_filetype() check if a FileType autocommand was used
|
did_filetype() check if a FileType autocommand was used
|
||||||
eventhandler() check if invoked by an event handler
|
eventhandler() check if invoked by an event handler
|
||||||
getpid() get process ID of Vim
|
getpid() get process ID of Vim
|
||||||
getscriptinfo() get list of sourced vim scripts
|
getscriptinfo() get list of sourced Vim scripts
|
||||||
|
getstacktrace() get current stack trace of Vim scripts
|
||||||
|
|
||||||
libcall() call a function in an external library
|
libcall() call a function in an external library
|
||||||
libcallnr() idem, returning a number
|
libcallnr() idem, returning a number
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
Predefined variables *vvars*
|
Predefined variables *vvars*
|
||||||
|
|
||||||
Some variables can be set by the user, but the type cannot be changed.
|
Most variables are read-only, when a variable can be set by the user, it will
|
||||||
|
be mentioned at the variable description below. The type cannot be changed.
|
||||||
|
|
||||||
Type |gO| to see the table of contents.
|
Type |gO| to see the table of contents.
|
||||||
|
|
||||||
@ -195,7 +196,8 @@ v:event
|
|||||||
*v:exception* *exception-variable*
|
*v:exception* *exception-variable*
|
||||||
v:exception
|
v:exception
|
||||||
The value of the exception most recently caught and not
|
The value of the exception most recently caught and not
|
||||||
finished. See also |v:throwpoint| and |throw-variables|.
|
finished. See also |v:stacktrace|, |v:throwpoint|, and
|
||||||
|
|throw-variables|.
|
||||||
Example: >vim
|
Example: >vim
|
||||||
try
|
try
|
||||||
throw "oops"
|
throw "oops"
|
||||||
@ -586,6 +588,13 @@ v:shell_error
|
|||||||
endif
|
endif
|
||||||
<
|
<
|
||||||
|
|
||||||
|
*v:stacktrace* *stacktrace-variable*
|
||||||
|
v:stacktrace
|
||||||
|
The stack trace of the exception most recently caught and
|
||||||
|
not finished. Refer to |getstacktrace()| for the structure of
|
||||||
|
stack trace. See also |v:exception|, |v:throwpoint|, and
|
||||||
|
|throw-variables|.
|
||||||
|
|
||||||
*v:statusmsg* *statusmsg-variable*
|
*v:statusmsg* *statusmsg-variable*
|
||||||
v:statusmsg
|
v:statusmsg
|
||||||
Last given status message.
|
Last given status message.
|
||||||
@ -679,7 +688,7 @@ v:this_session
|
|||||||
v:throwpoint
|
v:throwpoint
|
||||||
The point where the exception most recently caught and not
|
The point where the exception most recently caught and not
|
||||||
finished was thrown. Not set when commands are typed. See
|
finished was thrown. Not set when commands are typed. See
|
||||||
also |v:exception| and |throw-variables|.
|
also |v:exception|, |v:stacktrace|, and |throw-variables|.
|
||||||
Example: >vim
|
Example: >vim
|
||||||
try
|
try
|
||||||
throw "oops"
|
throw "oops"
|
||||||
|
14
runtime/lua/vim/_meta/vimfn.lua
generated
14
runtime/lua/vim/_meta/vimfn.lua
generated
@ -3770,6 +3770,20 @@ function vim.fn.getregtype(regname) end
|
|||||||
--- @return vim.fn.getscriptinfo.ret[]
|
--- @return vim.fn.getscriptinfo.ret[]
|
||||||
function vim.fn.getscriptinfo(opts) end
|
function vim.fn.getscriptinfo(opts) end
|
||||||
|
|
||||||
|
--- Returns the current stack trace of Vim scripts.
|
||||||
|
--- Stack trace is a |List|, of which each item is a |Dictionary|
|
||||||
|
--- with the following items:
|
||||||
|
--- funcref The funcref if the stack is at the function,
|
||||||
|
--- otherwise this item is not exist.
|
||||||
|
--- event The string of the event description if the
|
||||||
|
--- stack is at autocmd event, otherwise this item
|
||||||
|
--- is not exist.
|
||||||
|
--- lnum The line number of the script on the stack.
|
||||||
|
--- filepath The file path of the script on the stack.
|
||||||
|
---
|
||||||
|
--- @return table[]
|
||||||
|
function vim.fn.getstacktrace() end
|
||||||
|
|
||||||
--- If {tabnr} is not specified, then information about all the
|
--- If {tabnr} is not specified, then information about all the
|
||||||
--- tab pages is returned as a |List|. Each List item is a
|
--- tab pages is returned as a |List|. Each List item is a
|
||||||
--- |Dictionary|. Otherwise, {tabnr} specifies the tab page
|
--- |Dictionary|. Otherwise, {tabnr} specifies the tab page
|
||||||
|
12
runtime/lua/vim/_meta/vvars.lua
generated
12
runtime/lua/vim/_meta/vvars.lua
generated
@ -203,7 +203,8 @@ vim.v.errors = ...
|
|||||||
vim.v.event = ...
|
vim.v.event = ...
|
||||||
|
|
||||||
--- The value of the exception most recently caught and not
|
--- The value of the exception most recently caught and not
|
||||||
--- finished. See also `v:throwpoint` and `throw-variables`.
|
--- finished. See also `v:stacktrace`, `v:throwpoint`, and
|
||||||
|
--- `throw-variables`.
|
||||||
--- Example:
|
--- Example:
|
||||||
---
|
---
|
||||||
--- ```vim
|
--- ```vim
|
||||||
@ -616,6 +617,13 @@ vim.v.servername = ...
|
|||||||
--- @type integer
|
--- @type integer
|
||||||
vim.v.shell_error = ...
|
vim.v.shell_error = ...
|
||||||
|
|
||||||
|
--- The stack trace of the exception most recently caught and
|
||||||
|
--- not finished. Refer to `getstacktrace()` for the structure of
|
||||||
|
--- stack trace. See also `v:exception`, `v:throwpoint`, and
|
||||||
|
--- `throw-variables`.
|
||||||
|
--- @type table[]
|
||||||
|
vim.v.stacktrace = ...
|
||||||
|
|
||||||
--- Last given status message.
|
--- Last given status message.
|
||||||
--- Modifiable (can be set).
|
--- Modifiable (can be set).
|
||||||
--- @type string
|
--- @type string
|
||||||
@ -718,7 +726,7 @@ vim.v.this_session = ...
|
|||||||
|
|
||||||
--- The point where the exception most recently caught and not
|
--- The point where the exception most recently caught and not
|
||||||
--- finished was thrown. Not set when commands are typed. See
|
--- finished was thrown. Not set when commands are typed. See
|
||||||
--- also `v:exception` and `throw-variables`.
|
--- also `v:exception`, `v:stacktrace`, and `throw-variables`.
|
||||||
--- Example:
|
--- Example:
|
||||||
---
|
---
|
||||||
--- ```vim
|
--- ```vim
|
||||||
|
@ -270,6 +270,7 @@ static struct vimvar {
|
|||||||
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
|
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
|
||||||
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
|
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
|
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
|
||||||
|
VV(VV_STACKTRACE, "stacktrace", VAR_LIST, VV_RO),
|
||||||
// Neovim
|
// Neovim
|
||||||
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
|
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
|
||||||
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
|
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
|
||||||
|
@ -167,6 +167,7 @@ typedef enum {
|
|||||||
VV_COLLATE,
|
VV_COLLATE,
|
||||||
VV_EXITING,
|
VV_EXITING,
|
||||||
VV_MAXCOL,
|
VV_MAXCOL,
|
||||||
|
VV_STACKTRACE,
|
||||||
// Nvim
|
// Nvim
|
||||||
VV_STDERR,
|
VV_STDERR,
|
||||||
VV_MSGPACK_TYPES,
|
VV_MSGPACK_TYPES,
|
||||||
|
@ -4670,6 +4670,25 @@ M.funcs = {
|
|||||||
returns = 'vim.fn.getscriptinfo.ret[]',
|
returns = 'vim.fn.getscriptinfo.ret[]',
|
||||||
signature = 'getscriptinfo([{opts}])',
|
signature = 'getscriptinfo([{opts}])',
|
||||||
},
|
},
|
||||||
|
getstacktrace = {
|
||||||
|
args = 0,
|
||||||
|
desc = [=[
|
||||||
|
Returns the current stack trace of Vim scripts.
|
||||||
|
Stack trace is a |List|, of which each item is a |Dictionary|
|
||||||
|
with the following items:
|
||||||
|
funcref The funcref if the stack is at the function,
|
||||||
|
otherwise this item is not exist.
|
||||||
|
event The string of the event description if the
|
||||||
|
stack is at autocmd event, otherwise this item
|
||||||
|
is not exist.
|
||||||
|
lnum The line number of the script on the stack.
|
||||||
|
filepath The file path of the script on the stack.
|
||||||
|
]=],
|
||||||
|
name = 'getstacktrace',
|
||||||
|
params = {},
|
||||||
|
returns = 'table[]',
|
||||||
|
signature = 'getstacktrace()',
|
||||||
|
},
|
||||||
gettabinfo = {
|
gettabinfo = {
|
||||||
args = { 0, 1 },
|
args = { 0, 1 },
|
||||||
base = 1,
|
base = 1,
|
||||||
|
@ -2633,6 +2633,30 @@ int tv_dict_add_allocated_str(dict_T *const d, const char *const key, const size
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a function entry to dictionary.
|
||||||
|
///
|
||||||
|
/// @param[out] d Dictionary to add entry to.
|
||||||
|
/// @param[in] key Key to add.
|
||||||
|
/// @param[in] key_len Key length.
|
||||||
|
/// @param[in] fp Function to add.
|
||||||
|
///
|
||||||
|
/// @return OK in case of success, FAIL when key already exists.
|
||||||
|
int tv_dict_add_func(dict_T *const d, const char *const key, const size_t key_len,
|
||||||
|
ufunc_T *const fp)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
||||||
|
{
|
||||||
|
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
|
||||||
|
|
||||||
|
item->di_tv.v_type = VAR_FUNC;
|
||||||
|
item->di_tv.vval.v_string = xstrdup(fp->uf_name);
|
||||||
|
if (tv_dict_add(d, item) == FAIL) {
|
||||||
|
tv_dict_item_free(item);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
func_ref(item->di_tv.vval.v_string);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
//{{{2 Operations on the whole dict
|
//{{{2 Operations on the whole dict
|
||||||
|
|
||||||
/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
|
/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
|
||||||
|
@ -479,6 +479,9 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
|
|||||||
excp->throw_lnum = SOURCING_LNUM;
|
excp->throw_lnum = SOURCING_LNUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
excp->stacktrace = stacktrace_create();
|
||||||
|
tv_list_ref(excp->stacktrace);
|
||||||
|
|
||||||
if (p_verbose >= 13 || debug_break_level > 0) {
|
if (p_verbose >= 13 || debug_break_level > 0) {
|
||||||
int save_msg_silent = msg_silent;
|
int save_msg_silent = msg_silent;
|
||||||
|
|
||||||
@ -563,6 +566,7 @@ static void discard_exception(except_T *excp, bool was_finished)
|
|||||||
free_msglist(excp->messages);
|
free_msglist(excp->messages);
|
||||||
}
|
}
|
||||||
xfree(excp->throw_name);
|
xfree(excp->throw_name);
|
||||||
|
tv_list_unref(excp->stacktrace);
|
||||||
xfree(excp);
|
xfree(excp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,6 +588,7 @@ static void catch_exception(except_T *excp)
|
|||||||
excp->caught = caught_stack;
|
excp->caught = caught_stack;
|
||||||
caught_stack = excp;
|
caught_stack = excp;
|
||||||
set_vim_var_string(VV_EXCEPTION, excp->value, -1);
|
set_vim_var_string(VV_EXCEPTION, excp->value, -1);
|
||||||
|
set_vim_var_list(VV_STACKTRACE, excp->stacktrace);
|
||||||
if (*excp->throw_name != NUL) {
|
if (*excp->throw_name != NUL) {
|
||||||
if (excp->throw_lnum != 0) {
|
if (excp->throw_lnum != 0) {
|
||||||
vim_snprintf(IObuff, IOSIZE, _("%s, line %" PRId64),
|
vim_snprintf(IObuff, IOSIZE, _("%s, line %" PRId64),
|
||||||
@ -633,6 +638,7 @@ static void finish_exception(except_T *excp)
|
|||||||
caught_stack = caught_stack->caught;
|
caught_stack = caught_stack->caught;
|
||||||
if (caught_stack != NULL) {
|
if (caught_stack != NULL) {
|
||||||
set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
|
set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
|
||||||
|
set_vim_var_list(VV_STACKTRACE, caught_stack->stacktrace);
|
||||||
if (*caught_stack->throw_name != NUL) {
|
if (*caught_stack->throw_name != NUL) {
|
||||||
if (caught_stack->throw_lnum != 0) {
|
if (caught_stack->throw_lnum != 0) {
|
||||||
vim_snprintf(IObuff, IOSIZE,
|
vim_snprintf(IObuff, IOSIZE,
|
||||||
@ -651,6 +657,7 @@ static void finish_exception(except_T *excp)
|
|||||||
} else {
|
} else {
|
||||||
set_vim_var_string(VV_EXCEPTION, NULL, -1);
|
set_vim_var_string(VV_EXCEPTION, NULL, -1);
|
||||||
set_vim_var_string(VV_THROWPOINT, NULL, -1);
|
set_vim_var_string(VV_THROWPOINT, NULL, -1);
|
||||||
|
set_vim_var_list(VV_STACKTRACE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard the exception, but use the finish message for 'verbose'.
|
// Discard the exception, but use the finish message for 'verbose'.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "nvim/eval/typval_defs.h"
|
||||||
#include "nvim/pos_defs.h"
|
#include "nvim/pos_defs.h"
|
||||||
|
|
||||||
/// A list used for saving values of "emsg_silent". Used by ex_try() to save the
|
/// A list used for saving values of "emsg_silent". Used by ex_try() to save the
|
||||||
@ -107,6 +108,7 @@ struct vim_exception {
|
|||||||
msglist_T *messages; ///< message(s) causing error exception
|
msglist_T *messages; ///< message(s) causing error exception
|
||||||
char *throw_name; ///< name of the throw point
|
char *throw_name; ///< name of the throw point
|
||||||
linenr_T throw_lnum; ///< line number of the throw point
|
linenr_T throw_lnum; ///< line number of the throw point
|
||||||
|
list_T *stacktrace; ///< stacktrace
|
||||||
except_T *caught; ///< next exception on the caught stack
|
except_T *caught; ///< next exception on the caught stack
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -228,6 +228,72 @@ char *estack_sfile(estack_arg_T which)
|
|||||||
return (char *)ga.ga_data;
|
return (char *)ga.ga_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void stacktrace_push_item(list_T *const l, ufunc_T *const fp, const char *const event,
|
||||||
|
const linenr_T lnum, char *const filepath,
|
||||||
|
const bool filepath_alloced)
|
||||||
|
{
|
||||||
|
dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
|
||||||
|
typval_T tv = {
|
||||||
|
.v_type = VAR_DICT,
|
||||||
|
.v_lock = VAR_LOCKED,
|
||||||
|
.vval.v_dict = d,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fp != NULL) {
|
||||||
|
tv_dict_add_func(d, S_LEN("funcref"), fp);
|
||||||
|
}
|
||||||
|
if (event != NULL) {
|
||||||
|
tv_dict_add_str(d, S_LEN("event"), event);
|
||||||
|
}
|
||||||
|
tv_dict_add_nr(d, S_LEN("lnum"), lnum);
|
||||||
|
if (filepath_alloced) {
|
||||||
|
tv_dict_add_allocated_str(d, S_LEN("filepath"), filepath);
|
||||||
|
} else {
|
||||||
|
tv_dict_add_str(d, S_LEN("filepath"), filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
tv_list_append_tv(l, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the stacktrace from exestack.
|
||||||
|
list_T *stacktrace_create(void)
|
||||||
|
{
|
||||||
|
list_T *const l = tv_list_alloc(exestack.ga_len);
|
||||||
|
|
||||||
|
for (int i = 0; i < exestack.ga_len; i++) {
|
||||||
|
estack_T *const entry = &((estack_T *)exestack.ga_data)[i];
|
||||||
|
linenr_T lnum = entry->es_lnum;
|
||||||
|
|
||||||
|
if (entry->es_type == ETYPE_SCRIPT) {
|
||||||
|
stacktrace_push_item(l, NULL, NULL, lnum, entry->es_name, false);
|
||||||
|
} else if (entry->es_type == ETYPE_UFUNC) {
|
||||||
|
ufunc_T *const fp = entry->es_info.ufunc;
|
||||||
|
const sctx_T sctx = fp->uf_script_ctx;
|
||||||
|
bool filepath_alloced = false;
|
||||||
|
char *filepath = sctx.sc_sid > 0
|
||||||
|
? get_scriptname((LastSet){ .script_ctx = sctx },
|
||||||
|
&filepath_alloced) : "";
|
||||||
|
lnum += sctx.sc_lnum;
|
||||||
|
stacktrace_push_item(l, fp, NULL, lnum, filepath, filepath_alloced);
|
||||||
|
} else if (entry->es_type == ETYPE_AUCMD) {
|
||||||
|
const sctx_T sctx = entry->es_info.aucmd->script_ctx;
|
||||||
|
bool filepath_alloced = false;
|
||||||
|
char *filepath = sctx.sc_sid > 0
|
||||||
|
? get_scriptname((LastSet){ .script_ctx = sctx },
|
||||||
|
&filepath_alloced) : "";
|
||||||
|
lnum += sctx.sc_lnum;
|
||||||
|
stacktrace_push_item(l, NULL, entry->es_name, lnum, filepath, filepath_alloced);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// getstacktrace() function
|
||||||
|
void f_getstacktrace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
|
{
|
||||||
|
tv_list_set_ret(rettv, stacktrace_create());
|
||||||
|
}
|
||||||
|
|
||||||
static bool runtime_search_path_valid = false;
|
static bool runtime_search_path_valid = false;
|
||||||
static int *runtime_search_path_ref = NULL;
|
static int *runtime_search_path_ref = NULL;
|
||||||
static RuntimeSearchPath runtime_search_path;
|
static RuntimeSearchPath runtime_search_path;
|
||||||
|
@ -220,7 +220,8 @@ M.vars = {
|
|||||||
type = 'string',
|
type = 'string',
|
||||||
desc = [=[
|
desc = [=[
|
||||||
The value of the exception most recently caught and not
|
The value of the exception most recently caught and not
|
||||||
finished. See also |v:throwpoint| and |throw-variables|.
|
finished. See also |v:stacktrace|, |v:throwpoint|, and
|
||||||
|
|throw-variables|.
|
||||||
Example: >vim
|
Example: >vim
|
||||||
try
|
try
|
||||||
throw "oops"
|
throw "oops"
|
||||||
@ -701,6 +702,15 @@ M.vars = {
|
|||||||
<
|
<
|
||||||
]=],
|
]=],
|
||||||
},
|
},
|
||||||
|
stacktrace = {
|
||||||
|
type = 'table[]',
|
||||||
|
desc = [=[
|
||||||
|
The stack trace of the exception most recently caught and
|
||||||
|
not finished. Refer to |getstacktrace()| for the structure of
|
||||||
|
stack trace. See also |v:exception|, |v:throwpoint|, and
|
||||||
|
|throw-variables|.
|
||||||
|
]=],
|
||||||
|
},
|
||||||
statusmsg = {
|
statusmsg = {
|
||||||
type = 'string',
|
type = 'string',
|
||||||
desc = [=[
|
desc = [=[
|
||||||
@ -823,7 +833,7 @@ M.vars = {
|
|||||||
desc = [=[
|
desc = [=[
|
||||||
The point where the exception most recently caught and not
|
The point where the exception most recently caught and not
|
||||||
finished was thrown. Not set when commands are typed. See
|
finished was thrown. Not set when commands are typed. See
|
||||||
also |v:exception| and |throw-variables|.
|
also |v:exception|, |v:stacktrace|, and |throw-variables|.
|
||||||
Example: >vim
|
Example: >vim
|
||||||
try
|
try
|
||||||
throw "oops"
|
throw "oops"
|
||||||
|
107
test/old/testdir/test_stacktrace.vim
Normal file
107
test/old/testdir/test_stacktrace.vim
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
" Test for getstacktrace() and v:stacktrace
|
||||||
|
|
||||||
|
let s:thisfile = expand('%:p')
|
||||||
|
let s:testdir = s:thisfile->fnamemodify(':h')
|
||||||
|
|
||||||
|
func Filepath(name)
|
||||||
|
return s:testdir .. '/' .. a:name
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func AssertStacktrace(expect, actual)
|
||||||
|
call assert_equal(#{lnum: 581, filepath: Filepath('runtest.vim')}, a:actual[0])
|
||||||
|
call assert_equal(a:expect, a:actual[-len(a:expect):])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_getstacktrace()
|
||||||
|
let g:stacktrace = []
|
||||||
|
let lines1 =<< trim [SCRIPT]
|
||||||
|
" Xscript1
|
||||||
|
source Xscript2
|
||||||
|
func Xfunc1()
|
||||||
|
" Xfunc1
|
||||||
|
call Xfunc2()
|
||||||
|
endfunc
|
||||||
|
[SCRIPT]
|
||||||
|
let lines2 =<< trim [SCRIPT]
|
||||||
|
" Xscript2
|
||||||
|
func Xfunc2()
|
||||||
|
" Xfunc2
|
||||||
|
let g:stacktrace = getstacktrace()
|
||||||
|
endfunc
|
||||||
|
[SCRIPT]
|
||||||
|
call writefile(lines1, 'Xscript1', 'D')
|
||||||
|
call writefile(lines2, 'Xscript2', 'D')
|
||||||
|
source Xscript1
|
||||||
|
call Xfunc1()
|
||||||
|
call AssertStacktrace([
|
||||||
|
\ #{funcref: funcref('Test_getstacktrace'), lnum: 35, filepath: s:thisfile},
|
||||||
|
\ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
|
||||||
|
\ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
|
||||||
|
\ ], g:stacktrace)
|
||||||
|
unlet g:stacktrace
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_getstacktrace_event()
|
||||||
|
let g:stacktrace = []
|
||||||
|
let lines1 =<< trim [SCRIPT]
|
||||||
|
" Xscript1
|
||||||
|
func Xfunc()
|
||||||
|
" Xfunc
|
||||||
|
let g:stacktrace = getstacktrace()
|
||||||
|
endfunc
|
||||||
|
augroup test_stacktrace
|
||||||
|
autocmd SourcePre * call Xfunc()
|
||||||
|
augroup END
|
||||||
|
[SCRIPT]
|
||||||
|
let lines2 =<< trim [SCRIPT]
|
||||||
|
" Xscript2
|
||||||
|
[SCRIPT]
|
||||||
|
call writefile(lines1, 'Xscript1', 'D')
|
||||||
|
call writefile(lines2, 'Xscript2', 'D')
|
||||||
|
source Xscript1
|
||||||
|
source Xscript2
|
||||||
|
call AssertStacktrace([
|
||||||
|
\ #{funcref: funcref('Test_getstacktrace_event'), lnum: 62, filepath: s:thisfile},
|
||||||
|
\ #{event: 'SourcePre Autocommands for "*"', lnum: 7, filepath: Filepath('Xscript1')},
|
||||||
|
\ #{funcref: funcref('Xfunc'), lnum: 4, filepath: Filepath('Xscript1')},
|
||||||
|
\ ], g:stacktrace)
|
||||||
|
augroup test_stacktrace
|
||||||
|
autocmd!
|
||||||
|
augroup END
|
||||||
|
unlet g:stacktrace
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_vstacktrace()
|
||||||
|
let lines1 =<< trim [SCRIPT]
|
||||||
|
" Xscript1
|
||||||
|
source Xscript2
|
||||||
|
func Xfunc1()
|
||||||
|
" Xfunc1
|
||||||
|
call Xfunc2()
|
||||||
|
endfunc
|
||||||
|
[SCRIPT]
|
||||||
|
let lines2 =<< trim [SCRIPT]
|
||||||
|
" Xscript2
|
||||||
|
func Xfunc2()
|
||||||
|
" Xfunc2
|
||||||
|
throw 'Exception from Xfunc2'
|
||||||
|
endfunc
|
||||||
|
[SCRIPT]
|
||||||
|
call writefile(lines1, 'Xscript1', 'D')
|
||||||
|
call writefile(lines2, 'Xscript2', 'D')
|
||||||
|
source Xscript1
|
||||||
|
call assert_equal([], v:stacktrace)
|
||||||
|
try
|
||||||
|
call Xfunc1()
|
||||||
|
catch
|
||||||
|
let stacktrace = v:stacktrace
|
||||||
|
endtry
|
||||||
|
call assert_equal([], v:stacktrace)
|
||||||
|
call AssertStacktrace([
|
||||||
|
\ #{funcref: funcref('Test_vstacktrace'), lnum: 95, filepath: s:thisfile},
|
||||||
|
\ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
|
||||||
|
\ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
|
||||||
|
\ ], stacktrace)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
Loading…
Reference in New Issue
Block a user