]> granicus.if.org Git - vim/commitdiff
patch 8.1.2046: SafeState may be triggered at the wrong moment v8.1.2046
authorBram Moolenaar <Bram@vim.org>
Mon, 16 Sep 2019 19:58:13 +0000 (21:58 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 16 Sep 2019 19:58:13 +0000 (21:58 +0200)
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.

runtime/doc/autocmd.txt
src/autocmd.c
src/channel.c
src/getchar.c
src/main.c
src/proto/main.pro
src/version.c
src/vim.h

index d336ba9fac4d73ac4af3e94f96ba88aa537f4642..85a5547449ce40743e535322a0aa77867334b5bd 100644 (file)
@@ -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, going to wait for the
                                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
index fb6c84491d74c53212e4e35e3500b6a607c918ff..3f87e931cd5d5a90c60661d1087ef1d27d6a11e8 100644 (file)
@@ -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},
index 3792b354b7fc452ae0662f1c5f3b12e2bb104dd2..0dab3be5f43a7ee39c91acb2a85a5ff635f10d4a 100644 (file)
@@ -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;
 }
 
index 32e35504ff3a3f2e2b49b9451ff34d5f581a0bd0..3f3c6bca588a9d5c9ee7dbf5a6472346893aeb9d 100644 (file)
@@ -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
 
index 94bb8468085ccb46e3371fa1f4495d3152096038..51e3915d96105c6baf74c9a1a1b48bc9f2641bfb 100644 (file)
@@ -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);
 }
 
 
index a6b9784a30207c5fb1460788467f6b9dc298430d..2c07ceab0d738665da08e7ad666f27d8981f5408 100644 (file)
@@ -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);
index 34e3e6bdfc3c62662b6966a0ff42cd7af6a76db1..02e8cfbb31cf6976215ba95d722e099205fac341 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2046,
 /**/
     2045,
 /**/
index 4b817c59992168cea9c4b4bf536fbc2ebaf65bf6..8bfbcaf9f5a32ca4fcd9883e166af7e188634af6 100644 (file)
--- 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".