mirror of
https://github.com/neovim/neovim.git
synced 2025-09-23 03:28:33 +00:00
ex_getln: Save and restore try state
Problem: when processing cycle such as :for pat in [' \ze*', ' \zs*'] : try : let l = matchlist('x x', pat) : $put ='E888 NOT detected for ' . pat : catch : $put ='E888 detected for ' . pat : endtry :endfor `:let l = …` throwing an error causes this error to be caught after color_cmdline attempts to get callback for highlighting next line (the one with `$put = 'E888 NOT…`). Saving/restoring state prevents this from happening.
This commit is contained in:
@@ -37,6 +37,52 @@ typedef struct {
|
|||||||
# include "api/private/ui_events_metadata.generated.h"
|
# include "api/private/ui_events_metadata.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Start block that may cause VimL exceptions while evaluating another code
|
||||||
|
///
|
||||||
|
/// Used when caller is supposed to be operating when other VimL code is being
|
||||||
|
/// processed and that “other VimL code” must not be affected.
|
||||||
|
///
|
||||||
|
/// @param[out] tstate Location where try state should be saved.
|
||||||
|
void try_enter(TryState *const tstate)
|
||||||
|
{
|
||||||
|
*tstate = (TryState) {
|
||||||
|
.trylevel = trylevel,
|
||||||
|
.got_int = got_int,
|
||||||
|
.did_throw = did_throw,
|
||||||
|
.msg_list = (const struct msglist *const *)msg_list,
|
||||||
|
.private_msg_list = NULL,
|
||||||
|
};
|
||||||
|
trylevel = 1;
|
||||||
|
got_int = false;
|
||||||
|
did_throw = false;
|
||||||
|
msg_list = &tstate->private_msg_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// End try block, set the error message if any and restore previous state
|
||||||
|
///
|
||||||
|
/// @warning Return is consistent with most functions (false on error), not with
|
||||||
|
/// try_end (true on error).
|
||||||
|
///
|
||||||
|
/// @param[in] tstate Previous state to restore.
|
||||||
|
/// @param[out] err Location where error should be saved.
|
||||||
|
///
|
||||||
|
/// @return false if error occurred, true otherwise.
|
||||||
|
bool try_leave(const TryState *const tstate, Error *const err)
|
||||||
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
|
{
|
||||||
|
const bool ret = !try_end(err);
|
||||||
|
assert(trylevel == 0);
|
||||||
|
assert(!got_int);
|
||||||
|
assert(!did_throw);
|
||||||
|
assert(msg_list == &tstate->private_msg_list);
|
||||||
|
assert(*msg_list == NULL);
|
||||||
|
trylevel = tstate->trylevel;
|
||||||
|
got_int = tstate->got_int;
|
||||||
|
did_throw = tstate->did_throw;
|
||||||
|
msg_list = (struct msglist **)tstate->msg_list;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/// Start block that may cause vimscript exceptions
|
/// Start block that may cause vimscript exceptions
|
||||||
void try_start(void)
|
void try_start(void)
|
||||||
{
|
{
|
||||||
|
@@ -82,6 +82,18 @@
|
|||||||
#define api_free_window(value)
|
#define api_free_window(value)
|
||||||
#define api_free_tabpage(value)
|
#define api_free_tabpage(value)
|
||||||
|
|
||||||
|
/// Structure used for saving state for :try
|
||||||
|
///
|
||||||
|
/// Used when caller is supposed to be operating when other VimL code is being
|
||||||
|
/// processed and that “other VimL code” must not be affected.
|
||||||
|
typedef struct {
|
||||||
|
int trylevel;
|
||||||
|
int got_int;
|
||||||
|
int did_throw;
|
||||||
|
struct msglist *private_msg_list;
|
||||||
|
const struct msglist *const *msg_list;
|
||||||
|
} TryState;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "api/private/helpers.h.generated.h"
|
# include "api/private/helpers.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@@ -2222,6 +2222,11 @@ static bool color_cmdline(void)
|
|||||||
bool ret = true;
|
bool ret = true;
|
||||||
kv_size(ccline_colors) = 0;
|
kv_size(ccline_colors) = 0;
|
||||||
|
|
||||||
|
if (ccline.cmdbuff == NULL || *ccline.cmdbuff == NUL) {
|
||||||
|
// Nothing to do, exiting.
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
const int saved_force_abort = force_abort;
|
const int saved_force_abort = force_abort;
|
||||||
force_abort = true;
|
force_abort = true;
|
||||||
bool arg_allocated = false;
|
bool arg_allocated = false;
|
||||||
@@ -2235,11 +2240,12 @@ static bool color_cmdline(void)
|
|||||||
static int prev_prompt_errors = 0;
|
static int prev_prompt_errors = 0;
|
||||||
Callback color_cb = { .type = kCallbackNone };
|
Callback color_cb = { .type = kCallbackNone };
|
||||||
bool can_free_cb = false;
|
bool can_free_cb = false;
|
||||||
|
TryState tstate;
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
const char *err_errmsg = (const char *)e_intern2;
|
const char *err_errmsg = (const char *)e_intern2;
|
||||||
bool dgc_ret = true;
|
bool dgc_ret = true;
|
||||||
|
|
||||||
try_start();
|
try_enter(&tstate);
|
||||||
if (ccline.input_fn) {
|
if (ccline.input_fn) {
|
||||||
color_cb = getln_input_callback;
|
color_cb = getln_input_callback;
|
||||||
} else if (ccline.cmdfirstc == ':') {
|
} else if (ccline.cmdfirstc == ':') {
|
||||||
@@ -2257,7 +2263,7 @@ static bool color_cmdline(void)
|
|||||||
} else {
|
} else {
|
||||||
goto color_cmdline_end;
|
goto color_cmdline_end;
|
||||||
}
|
}
|
||||||
if (try_end(&err) || !dgc_ret) {
|
if (!try_leave(&tstate, &err) || !dgc_ret) {
|
||||||
goto color_cmdline_error;
|
goto color_cmdline_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2285,10 +2291,10 @@ static bool color_cmdline(void)
|
|||||||
// correct, with msg_col it just misses leading `:`. Since `redraw!` in
|
// correct, with msg_col it just misses leading `:`. Since `redraw!` in
|
||||||
// callback lags this is least of the user problems.
|
// callback lags this is least of the user problems.
|
||||||
//
|
//
|
||||||
// Also using try_start() because error messages may overwrite typed
|
// Also using try_enter() because error messages may overwrite typed
|
||||||
// command-line which is not expected.
|
// command-line which is not expected.
|
||||||
getln_interrupted_highlight = false;
|
getln_interrupted_highlight = false;
|
||||||
try_start();
|
try_enter(&tstate);
|
||||||
err_errmsg = N_("E5407: Callback has thrown an exception: %s");
|
err_errmsg = N_("E5407: Callback has thrown an exception: %s");
|
||||||
const int saved_msg_col = msg_col;
|
const int saved_msg_col = msg_col;
|
||||||
msg_silent++;
|
msg_silent++;
|
||||||
@@ -2298,7 +2304,7 @@ static bool color_cmdline(void)
|
|||||||
if (got_int) {
|
if (got_int) {
|
||||||
getln_interrupted_highlight = true;
|
getln_interrupted_highlight = true;
|
||||||
}
|
}
|
||||||
if (try_end(&err) || !cbcall_ret) {
|
if (!try_leave(&tstate, &err) || !cbcall_ret) {
|
||||||
goto color_cmdline_error;
|
goto color_cmdline_error;
|
||||||
}
|
}
|
||||||
if (tv.v_type != VAR_LIST) {
|
if (tv.v_type != VAR_LIST) {
|
||||||
|
@@ -7,6 +7,8 @@ local clear = helpers.clear
|
|||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local source = helpers.source
|
local source = helpers.source
|
||||||
|
local dedent = helpers.dedent
|
||||||
|
local curbufmeths = helpers.curbufmeths
|
||||||
|
|
||||||
local screen
|
local screen
|
||||||
|
|
||||||
@@ -421,6 +423,8 @@ describe('Command-line coloring', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
-- TODO Check for all other errors
|
-- TODO Check for all other errors
|
||||||
|
-- TODO Check for colored input() called in a cycle which previously errorred
|
||||||
|
-- out
|
||||||
end)
|
end)
|
||||||
describe('Ex commands coloring support', function()
|
describe('Ex commands coloring support', function()
|
||||||
it('still executes command-line even if errored out', function()
|
it('still executes command-line even if errored out', function()
|
||||||
@@ -430,7 +434,27 @@ describe('Ex commands coloring support', function()
|
|||||||
local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
|
local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
|
||||||
eq('\n'..msg, funcs.execute('messages'))
|
eq('\n'..msg, funcs.execute('messages'))
|
||||||
end)
|
end)
|
||||||
|
it('does not error out when called from a errorred out cycle', function()
|
||||||
|
-- Apparently when there is a cycle in which one of the commands errors out
|
||||||
|
-- this error may be caught by color_cmdline before it is presented to the
|
||||||
|
-- user.
|
||||||
|
feed(dedent([[
|
||||||
|
:set regexpengine=2
|
||||||
|
:for pat in [' \ze*', ' \zs*']
|
||||||
|
: try
|
||||||
|
: let l = matchlist('x x', pat)
|
||||||
|
: $put ='E888 NOT detected for ' . pat
|
||||||
|
: catch
|
||||||
|
: $put ='E888 detected for ' . pat
|
||||||
|
: endtry
|
||||||
|
:endfor
|
||||||
|
]]))
|
||||||
|
eq({'', 'E888 detected for \\ze*', 'E888 detected for \\zs*'},
|
||||||
|
curbufmeths.get_lines(0, -1, false))
|
||||||
|
eq('', funcs.execute('messages'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- TODO Specifically test for coloring in cmdline and expr modes
|
-- TODO Specifically test for coloring in cmdline and expr modes
|
||||||
|
-- TODO Check for errors from tv_dict_get_callback()
|
||||||
-- TODO Check using highlighted input() from inside highlighted input()
|
-- TODO Check using highlighted input() from inside highlighted input()
|
||||||
|
Reference in New Issue
Block a user