vim-patch:8.1.2053: SafeStateAgain not triggered if callback uses feedkeys()

Problem:    SafeStateAgain not triggered if callback uses feedkeys().
Solution:   Check for safe state in the input loop.  Make log messages easier
            to find. Add 'S' flag to state().

d103ee7843

Include misc1.c change from patch 8.1.2062.

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
zeertzjq
2023-08-21 12:16:33 +08:00
parent a0b912c6ec
commit 7ce2acd59b
7 changed files with 41 additions and 18 deletions

View File

@@ -7385,13 +7385,16 @@ state([{what}]) *state()*
added. E.g, this checks if the screen has scrolled: >vim added. E.g, this checks if the screen has scrolled: >vim
if state('s') != '' if state('s') != ''
These characters indicate the state: These characters indicate the state, generally indicating that
something is busy:
m halfway a mapping, :normal command, feedkeys() or m halfway a mapping, :normal command, feedkeys() or
stuffed command stuffed command
o operator pending or waiting for a command argument o operator pending or waiting for a command argument
a Insert mode autocomplete active a Insert mode autocomplete active
x executing an autocommand x executing an autocommand
c callback invoked (repeats for recursiveness up to "ccc") S not triggering SafeState
c callback invoked, including timer (repeats for
recursiveness up to "ccc")
s screen has scrolled for messages s screen has scrolled for messages
stdioopen({opts}) *stdioopen()* stdioopen({opts}) *stdioopen()*

View File

@@ -8760,13 +8760,16 @@ function vim.fn.srand(expr) end
--- added. E.g, this checks if the screen has scrolled: >vim --- added. E.g, this checks if the screen has scrolled: >vim
--- if state('s') != '' --- if state('s') != ''
--- ---
--- These characters indicate the state: --- These characters indicate the state, generally indicating that
--- something is busy:
--- m halfway a mapping, :normal command, feedkeys() or --- m halfway a mapping, :normal command, feedkeys() or
--- stuffed command --- stuffed command
--- o operator pending or waiting for a command argument --- o operator pending or waiting for a command argument
--- a Insert mode autocomplete active --- a Insert mode autocomplete active
--- x executing an autocommand --- x executing an autocommand
--- c callback invoked (repeats for recursiveness up to "ccc") --- S not triggering SafeState
--- c callback invoked, including timer (repeats for
--- recursiveness up to "ccc")
--- s screen has scrolled for messages --- s screen has scrolled for messages
--- ---
--- @param what? string --- @param what? string

View File

@@ -10471,13 +10471,16 @@ M.funcs = {
added. E.g, this checks if the screen has scrolled: >vim added. E.g, this checks if the screen has scrolled: >vim
if state('s') != '' if state('s') != ''
These characters indicate the state: These characters indicate the state, generally indicating that
something is busy:
m halfway a mapping, :normal command, feedkeys() or m halfway a mapping, :normal command, feedkeys() or
stuffed command stuffed command
o operator pending or waiting for a command argument o operator pending or waiting for a command argument
a Insert mode autocomplete active a Insert mode autocomplete active
x executing an autocommand x executing an autocommand
c callback invoked (repeats for recursiveness up to "ccc") S not triggering SafeState
c callback invoked, including timer (repeats for
recursiveness up to "ccc")
s screen has scrolled for messages s screen has scrolled for messages
]=], ]=],
name = 'state', name = 'state',

View File

@@ -4955,6 +4955,9 @@ static void f_state(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (!ctrl_x_mode_none()) { if (!ctrl_x_mode_none()) {
may_add_state_char(&ga, include, 'a'); may_add_state_char(&ga, include, 'a');
} }
if (!get_was_safe_state()) {
may_add_state_char(&ga, include, 'S');
}
for (int i = 0; i < get_callback_depth() && i < 3; i++) { for (int i = 0; i < get_callback_depth() && i < 3; i++) {
may_add_state_char(&ga, include, 'c'); may_add_state_char(&ga, include, 'c');
} }

View File

@@ -885,7 +885,7 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
if (++typebuf.tb_change_cnt == 0) { if (++typebuf.tb_change_cnt == 0) {
typebuf.tb_change_cnt = 1; typebuf.tb_change_cnt = 1;
} }
state_no_longer_safe(); state_no_longer_safe("ins_typebuf()");
addlen = (int)strlen(str); addlen = (int)strlen(str);
@@ -1629,7 +1629,7 @@ int vgetc(void)
// Need to process the character before we know it's safe to do something // Need to process the character before we know it's safe to do something
// else. // else.
if (c != K_IGNORE) { if (c != K_IGNORE) {
state_no_longer_safe(); state_no_longer_safe("key typed");
} }
return c; return c;

View File

@@ -1412,7 +1412,7 @@ static int normal_check(VimState *state)
quit_more = false; quit_more = false;
// it's not safe unless normal_check_safe_state() is called // it's not safe unless normal_check_safe_state() is called
state_no_longer_safe(); state_no_longer_safe(NULL);
// If skip redraw is set (for ":" in wait_return()), don't redraw now. // If skip redraw is set (for ":" in wait_return()), don't redraw now.
// If there is nothing in the stuff_buffer or do_redraw is true, // If there is nothing in the stuff_buffer or do_redraw is true,

View File

@@ -272,20 +272,26 @@ void may_trigger_modechanged(void)
/// When true in a safe state when starting to wait for a character. /// When true in a safe state when starting to wait for a character.
static bool was_safe = false; static bool was_safe = false;
/// Return whether currently it is safe, assuming it was safe before (high level
/// state didn't change).
static bool is_safe_now(void)
{
return stuff_empty()
&& typebuf.tb_len == 0
&& !using_script()
&& !global_busy;
}
/// Trigger SafeState if currently in s safe state, that is "safe" is TRUE and /// Trigger SafeState if currently in s safe state, that is "safe" is TRUE and
/// there is no typeahead. /// there is no typeahead.
void may_trigger_safestate(bool safe) void may_trigger_safestate(bool safe)
{ {
bool is_safe = safe bool is_safe = safe && is_safe_now();
&& stuff_empty()
&& typebuf.tb_len == 0
&& !using_script()
&& !global_busy;
if (was_safe != is_safe) { if (was_safe != is_safe) {
// Only log when the state changes, otherwise it happens at nearly // Only log when the state changes, otherwise it happens at nearly
// every key stroke. // every key stroke.
DLOG(is_safe ? "Start triggering SafeState" : "Stop triggering SafeState"); DLOG(is_safe ? "SafeState: Start triggering" : "SafeState: Stop triggering");
} }
if (is_safe) { if (is_safe) {
apply_autocmds(EVENT_SAFESTATE, NULL, NULL, false, curbuf); apply_autocmds(EVENT_SAFESTATE, NULL, NULL, false, curbuf);
@@ -296,10 +302,15 @@ void may_trigger_safestate(bool safe)
/// Something changed which causes the state possibly to be unsafe, e.g. a /// Something changed which causes the state possibly to be unsafe, e.g. a
/// character was typed. It will remain unsafe until the next call to /// character was typed. It will remain unsafe until the next call to
/// may_trigger_safestate(). /// may_trigger_safestate().
void state_no_longer_safe(void) void state_no_longer_safe(const char *reason)
{ {
if (was_safe) { if (was_safe && reason != NULL) {
DLOG("safe state reset"); DLOG("SafeState reset: %s", reason);
} }
was_safe = false; was_safe = false;
} }
bool get_was_safe_state(void)
{
return was_safe;
}