# HG changeset patch # User Bram Moolenaar # Date 1568664004 -7200 # Node ID 0d9ec3a2821fbc17649ec988795ace52b24d70ee # Parent 6d2d7e46c2124c3400119b1f76ab2604bd37e2fb patch 8.1.2046: SafeState may be triggered at the wrong moment Commit: https://github.com/vim/vim/commit/69198cb8c08f124729c41a4681f2d142228a9139 Author: Bram Moolenaar Date: Mon Sep 16 21:58:13 2019 +0200 patch 8.1.2046: SafeState may be triggered at the wrong moment Problem: SafeState may be triggered at the wrong moment. Solution: Move it up higher to after where messages are processed. Add a SafeStateAgain event to tigger there. diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -357,6 +357,7 @@ Name triggered by ~ |SafeState| nothing pending, going to wait for the user to type a character +|SafeStateAgain| repeated SafeState |ColorSchemePre| before loading a color scheme |ColorScheme| after loading a color scheme @@ -978,6 +979,11 @@ SafeState When nothing is pending, goi Depending on what you want to do, you may also check more with `state()`, e.g. whether the screen was scrolled for messages. + *SafeStateAgain* +SafeStateAgain Like SafeState but after processing any + messages and invoking callbacks. This may be + triggered often, don't do something that takes + time. *SessionLoadPost* SessionLoadPost After loading the session file created using diff --git a/src/autocmd.c b/src/autocmd.c --- a/src/autocmd.c +++ b/src/autocmd.c @@ -156,6 +156,7 @@ static struct event_name {"QuitPre", EVENT_QUITPRE}, {"RemoteReply", EVENT_REMOTEREPLY}, {"SafeState", EVENT_SAFESTATE}, + {"SafeStateAgain", EVENT_SAFESTATEAGAIN}, {"SessionLoadPost", EVENT_SESSIONLOADPOST}, {"ShellCmdPost", EVENT_SHELLCMDPOST}, {"ShellFilterPost", EVENT_SHELLFILTERPOST}, diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -3593,10 +3593,6 @@ channel_read_json_block( ch_log(channel, "Blocking read JSON for id %d", id); - // Not considered a safe state here, since we are processing a JSON message - // and parsing other messages while waiting. - enter_unsafe_state(); - if (id >= 0) channel_add_block_id(chanpart, id); @@ -3666,9 +3662,6 @@ channel_read_json_block( if (id >= 0) channel_remove_block_id(chanpart, id); - // This may trigger a SafeState autocommand. - leave_unsafe_state(); - return retval; } diff --git a/src/getchar.c b/src/getchar.c --- a/src/getchar.c +++ b/src/getchar.c @@ -933,6 +933,7 @@ ins_typebuf( init_typebuf(); if (++typebuf.tb_change_cnt == 0) typebuf.tb_change_cnt = 1; + state_no_longer_safe(); addlen = (int)STRLEN(str); @@ -1779,10 +1780,11 @@ vgetc(void) */ may_garbage_collect = FALSE; #endif + #ifdef FEAT_BEVAL_TERM if (c != K_MOUSEMOVE && c != K_IGNORE && c != K_CURSORHOLD) { - /* Don't trigger 'balloonexpr' unless only the mouse was moved. */ + // Don't trigger 'balloonexpr' unless only the mouse was moved. bevalexpr_due_set = FALSE; ui_remove_balloon(); } @@ -1792,6 +1794,11 @@ vgetc(void) c = K_IGNORE; #endif + // Need to process the character before we know it's safe to do something + // else. + if (c != K_IGNORE) + state_no_longer_safe(); + return c; } @@ -2039,12 +2046,15 @@ parse_queued_messages(void) int old_curbuf_fnum = curbuf->b_fnum; int i; int save_may_garbage_collect = may_garbage_collect; + static int entered = 0; // Do not handle messages while redrawing, because it may cause buffers to // change or be wiped while they are being redrawn. if (updating_screen) return; + ++entered; + // may_garbage_collect is set in main_loop() to do garbage collection when // blocking to wait on a character. We don't want that while parsing // messages, a callback may invoke vgetc() while lists and dicts are in use @@ -2090,12 +2100,19 @@ parse_queued_messages(void) break; } + // When not nested we'll go back to waiting for a typed character. If it + // was safe before then this triggers a SafeStateAgain autocommand event. + if (entered == 1) + leave_unsafe_state(); + may_garbage_collect = save_may_garbage_collect; // If the current window or buffer changed we need to bail out of the // waiting loop. E.g. when a job exit callback closes the terminal window. if (curwin->w_id != old_curwin_id || curbuf->b_fnum != old_curbuf_fnum) ins_char_typebuf(K_IGNORE); + + --entered; } #endif diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1029,8 +1029,8 @@ is_not_a_term() } +// When TRUE in a safe state when starting to wait for a character. static int was_safe = FALSE; -static int not_safe_now = 0; /* * Trigger SafeState if currently in a safe state for main_loop(). @@ -1057,6 +1057,7 @@ may_trigger_safestate(int safe) int is_safe = safe && stuff_empty() && typebuf.tb_len == 0 + && scriptin[curscript] == NULL && !global_busy; if (is_safe) @@ -1065,24 +1066,25 @@ may_trigger_safestate(int safe) } /* - * Entering a not-safe state. + * 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 + * may_trigger_safestate(). */ void -enter_unsafe_state(void) +state_no_longer_safe(void) { - ++not_safe_now; + was_safe = FALSE; } /* - * Leaving a not-safe state. Trigger SafeState if we were in a safe state - * before first calling enter_not_safe_state(). + * Invoked when leaving code that invokes callbacks. Then trigger + * SafeStateAgain, if it was safe when starting to wait for a character. */ void leave_unsafe_state(void) { - --not_safe_now; - if (not_safe_now == 0 && was_safe) - apply_autocmds(EVENT_SAFESTATE, NULL, NULL, FALSE, curbuf); + if (was_safe) + apply_autocmds(EVENT_SAFESTATEAGAIN, NULL, NULL, FALSE, curbuf); } diff --git a/src/proto/main.pro b/src/proto/main.pro --- a/src/proto/main.pro +++ b/src/proto/main.pro @@ -3,7 +3,7 @@ int vim_main2(void); void common_init(mparm_T *paramp); int is_not_a_term(void); void may_trigger_safestate(int safe); -void enter_unsafe_state(void); +void state_no_longer_safe(void); void leave_unsafe_state(void); void main_loop(int cmdwin, int noexmode); void getout_preserve_modified(int exitval); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2046, +/**/ 2045, /**/ 2044, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -1316,6 +1316,7 @@ enum auto_event EVENT_QUITPRE, // before :quit EVENT_REMOTEREPLY, // upon string reception from a remote vim EVENT_SAFESTATE, // going to wait for a character + EVENT_SAFESTATEAGAIN, // still waiting for a character EVENT_SESSIONLOADPOST, // after loading a session file EVENT_SHELLCMDPOST, // after ":!cmd" EVENT_SHELLFILTERPOST, // after ":1,2!cmd", ":w !cmd", ":r !cmd".