]> granicus.if.org Git - vim/commitdiff
patch 8.1.2044: no easy way to process postponed work v8.1.2044
authorBram Moolenaar <Bram@vim.org>
Sun, 15 Sep 2019 21:02:04 +0000 (23:02 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 15 Sep 2019 21:02:04 +0000 (23:02 +0200)
Problem:    No easy way to process postponed work. (Paul Jolly)
Solution:   Add the SafeState autocommand event.

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

index aa28362839b03f61670e784c75fb787b4a389473..d336ba9fac4d73ac4af3e94f96ba88aa537f4642 100644 (file)
@@ -355,6 +355,9 @@ Name                        triggered by ~
                        when popup menu visible
 |TextYankPost|         after text has been yanked or deleted
 
+|SafeState|            nothing pending, going to wait for the user to type a
+                       character
+
 |ColorSchemePre|       before loading a color scheme
 |ColorScheme|          after loading a color scheme
 
@@ -955,6 +958,27 @@ RemoteReply                        When a reply from a Vim that functions as
                                Note that even if an autocommand is defined,
                                the reply should be read with |remote_read()|
                                to consume it.
+                                                       *SafeState*
+SafeState                      When nothing is pending, going to wait for the
+                               user to type a character.
+                               This will not be triggered when:
+                               - an operator is pending
+                               - a register was entered with "r
+                               - halfway executing a command
+                               - executing a mapping
+                               - there is typeahead
+                               - Insert mode completion is active
+                               - Command line completion is active
+                               You can use `mode()` to find out what state
+                               Vim is in.  That may be:
+                               - VIsual mode
+                               - Normal mode
+                               - Insert mode
+                               - Command-line mode
+                               Depending on what you want to do, you may also
+                               check more with `state()`, e.g. whether the
+                               screen was scrolled for messages.
+
                                                        *SessionLoadPost*
 SessionLoadPost                        After loading the session file created using
                                the |:mksession| command.
index 8a6896bae845d5b5a4f01d4576b21b0ee65b1da4..fb6c84491d74c53212e4e35e3500b6a607c918ff 100644 (file)
@@ -155,6 +155,7 @@ static struct event_name
     {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
     {"QuitPre",                EVENT_QUITPRE},
     {"RemoteReply",    EVENT_REMOTEREPLY},
+    {"SafeState",      EVENT_SAFESTATE},
     {"SessionLoadPost",        EVENT_SESSIONLOADPOST},
     {"ShellCmdPost",   EVENT_SHELLCMDPOST},
     {"ShellFilterPost",        EVENT_SHELLFILTERPOST},
index e4dbcf60392df2ed133030a9c34f9b7aa421dc78..3792b354b7fc452ae0662f1c5f3b12e2bb104dd2 100644 (file)
@@ -3589,10 +3589,17 @@ channel_read_json_block(
     sock_T     fd;
     int                timeout;
     chanpart_T *chanpart = &channel->ch_part[part];
+    int                retval = FAIL;
 
     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);
+
     for (;;)
     {
        more = channel_parse_json(channel, part);
@@ -3600,10 +3607,9 @@ channel_read_json_block(
        // search for message "id"
        if (channel_get_json(channel, part, id, TRUE, rettv) == OK)
        {
-           if (id >= 0)
-               channel_remove_block_id(chanpart, id);
            ch_log(channel, "Received JSON for id %d", id);
-           return OK;
+           retval = OK;
+           break;
        }
 
        if (!more)
@@ -3659,7 +3665,11 @@ channel_read_json_block(
     }
     if (id >= 0)
        channel_remove_block_id(chanpart, id);
-    return FAIL;
+
+    // This may trigger a SafeState autocommand.
+    leave_unsafe_state();
+
+    return retval;
 }
 
 /*
@@ -4195,9 +4205,9 @@ ch_raw_common(typval_T *argvars, typval_T *rettv, int eval)
     free_job_options(&opt);
 }
 
-# define KEEP_OPEN_TIME 20  /* msec */
+#define KEEP_OPEN_TIME 20  /* msec */
 
-# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO)
+#if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO)
 /*
  * Add open channels to the poll struct.
  * Return the adjusted struct index.
@@ -4288,9 +4298,9 @@ channel_poll_check(int ret_in, void *fds_in)
 
     return ret;
 }
-# endif /* UNIX && !HAVE_SELECT */
+#endif /* UNIX && !HAVE_SELECT */
 
-# if (!defined(MSWIN) && defined(HAVE_SELECT)) || defined(PROTO)
+#if (!defined(MSWIN) && defined(HAVE_SELECT)) || defined(PROTO)
 
 /*
  * The "fd_set" type is hidden to avoid problems with the function proto.
@@ -4381,7 +4391,7 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in)
        if (ret > 0 && in_part->ch_fd != INVALID_FD
                                            && FD_ISSET(in_part->ch_fd, wfds))
        {
-           /* Clear the flag first, ch_fd may change in channel_write_input(). */
+           // Clear the flag first, ch_fd may change in channel_write_input().
            FD_CLR(in_part->ch_fd, wfds);
            channel_write_input(channel);
            --ret;
@@ -4390,11 +4400,12 @@ channel_select_check(int ret_in, void *rfds_in, void *wfds_in)
 
     return ret;
 }
-# endif /* !MSWIN && HAVE_SELECT */
+#endif // !MSWIN && HAVE_SELECT
 
 /*
  * Execute queued up commands.
- * Invoked from the main loop when it's safe to execute received commands.
+ * Invoked from the main loop when it's safe to execute received commands,
+ * and during a blocking wait for ch_evalexpr().
  * Return TRUE when something was done.
  */
     int
index cc41d49cc741db7f629bc0bc2b4f83f7118775aa..67141448fa1b0ab17f3f9a5181cf8dd0633bebfc 100644 (file)
@@ -1509,6 +1509,11 @@ ins_redraw(int ready)        // not busy with something
                                        (linenr_T)(curwin->w_cursor.lnum + 1));
     }
 
+    // Trigger SafeState if nothing is pending.
+    may_trigger_safestate(ready
+           && !ins_compl_active()
+           && !pum_visible());
+
 #if defined(FEAT_CONCEAL)
     if ((conceal_update_lines
            && (conceal_old_cursor_line != conceal_new_cursor_line
index 5e32f2b6c95b0596bb577c7b2a14012d5918b89b..4c88bc86b9fb92d69d0948cc2aa143beabbb8aa7 100644 (file)
@@ -971,6 +971,9 @@ getcmdline_int(
                                   that occurs while typing a command should
                                   cause the command not to be executed. */
 
+       // Trigger SafeState if nothing is pending.
+       may_trigger_safestate(xpc.xp_numfiles <= 0);
+
        cursorcmd();            /* set the cursor on the right spot */
 
        /* Get a character.  Ignore K_IGNORE and K_NOP, they should not do
index d1d9e32e4bc561b994db742f485562bcc945f59d..94bb8468085ccb46e3371fa1f4495d3152096038 100644 (file)
@@ -1028,6 +1028,64 @@ is_not_a_term()
     return params.not_a_term;
 }
 
+
+static int     was_safe = FALSE;
+static int     not_safe_now = 0;
+
+/*
+ * Trigger SafeState if currently in a safe state for main_loop().
+ */
+    static void
+may_trigger_safestate_main(oparg_T *oap)
+{
+    may_trigger_safestate(
+           !finish_op
+           && oap->prev_opcount > 0
+           && oap->prev_count0 == 0
+           && oap->op_type == OP_NOP
+           && oap->regname == NUL
+           && restart_edit == 0);
+}
+
+/*
+ * Trigger SafeState if currently in s safe state, that is "safe" is TRUE and
+ * there is no typeahead.
+ */
+    void
+may_trigger_safestate(int safe)
+{
+    int is_safe = safe
+                   && stuff_empty()
+                   && typebuf.tb_len == 0
+                   && !global_busy;
+
+    if (is_safe)
+       apply_autocmds(EVENT_SAFESTATE, NULL, NULL, FALSE, curbuf);
+    was_safe = is_safe;
+}
+
+/*
+ * Entering a not-safe state.
+ */
+    void
+enter_unsafe_state(void)
+{
+    ++not_safe_now;
+}
+
+/*
+ * Leaving a not-safe state.  Trigger SafeState if we were in a safe state
+ * before first calling enter_not_safe_state().
+ */
+    void
+leave_unsafe_state(void)
+{
+    --not_safe_now;
+    if (not_safe_now == 0 && was_safe)
+       apply_autocmds(EVENT_SAFESTATE, NULL, NULL, FALSE, curbuf);
+}
+
+
 /*
  * Main loop: Execute Normal mode commands until exiting Vim.
  * Also used to handle commands in the command-line window, until the window
@@ -1133,6 +1191,9 @@ main_loop(
            msg_scroll = FALSE;
        quit_more = FALSE;
 
+       // it's not safe unless may_trigger_safestate_main() is called
+       was_safe = FALSE;
+
        /*
         * 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,
@@ -1211,6 +1272,10 @@ main_loop(
                curbuf->b_last_changedtick = CHANGEDTICK(curbuf);
            }
 
+           // If nothing is pending and we are going to wait for the user to
+           // type a character, trigger SafeState.
+           may_trigger_safestate_main(&oa);
+
 #if defined(FEAT_DIFF)
            // Updating diffs from changed() does not always work properly,
            // esp. updating folds.  Do an update just before redrawing if
index 278d1addfd982bf0dbad67a8e17f383915055b58..a6b9784a30207c5fb1460788467f6b9dc298430d 100644 (file)
@@ -2,6 +2,9 @@
 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 leave_unsafe_state(void);
 void main_loop(int cmdwin, int noexmode);
 void getout_preserve_modified(int exitval);
 void getout(int exitval);
index 919d8fdf3f87d6706f329fbed057c80a9baa43eb..c23fc839b129d8be7bc6578c827707f3d2d84a2b 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2044,
 /**/
     2043,
 /**/
index 8afa9335252faef60030230c0c18bd0ce23d1aca..4b817c59992168cea9c4b4bf536fbc2ebaf65bf6 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -1315,6 +1315,7 @@ enum auto_event
     EVENT_QUICKFIXCMDPRE,      // before :make, :grep etc.
     EVENT_QUITPRE,             // before :quit
     EVENT_REMOTEREPLY,         // upon string reception from a remote vim
+    EVENT_SAFESTATE,           // going to wait for a character
     EVENT_SESSIONLOADPOST,     // after loading a session file
     EVENT_SHELLCMDPOST,                // after ":!cmd"
     EVENT_SHELLFILTERPOST,     // after ":1,2!cmd", ":w !cmd", ":r !cmd".