To: vim_dev@googlegroups.com Subject: Patch 8.1.2044 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2044 Problem: No easy way to process postponed work. (Paul Jolly) Solution: Add the SafeState autocommand event. Files: runtime/doc/autocmd.txt, src/main.c, src/proto/main.pro, src/vim.h, src/autocmd.c, src/channel.c, src/edit.c, src/ex_getln.c *** ../vim-8.1.2043/runtime/doc/autocmd.txt 2019-09-09 18:35:28.119252725 +0200 --- runtime/doc/autocmd.txt 2019-09-15 21:50:17.266633152 +0200 *************** *** 355,360 **** --- 355,363 ---- 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 *************** *** 956,961 **** --- 958,984 ---- 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. *** ../vim-8.1.2043/src/main.c 2019-09-14 21:00:01.379100893 +0200 --- src/main.c 2019-09-15 22:53:15.929911499 +0200 *************** *** 1028,1033 **** --- 1028,1091 ---- 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,1138 **** --- 1191,1199 ---- 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,1216 **** --- 1272,1281 ---- 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 *** ../vim-8.1.2043/src/proto/main.pro 2018-05-17 13:52:42.000000000 +0200 --- src/proto/main.pro 2019-09-15 22:58:58.612143846 +0200 *************** *** 2,7 **** --- 2,10 ---- 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); *** ../vim-8.1.2043/src/vim.h 2019-09-15 14:32:49.552731470 +0200 --- src/vim.h 2019-09-15 21:28:08.518157203 +0200 *************** *** 1315,1320 **** --- 1315,1321 ---- 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". *** ../vim-8.1.2043/src/autocmd.c 2019-08-21 14:36:29.383376114 +0200 --- src/autocmd.c 2019-09-15 21:28:44.982046437 +0200 *************** *** 155,160 **** --- 155,161 ---- {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, {"QuitPre", EVENT_QUITPRE}, {"RemoteReply", EVENT_REMOTEREPLY}, + {"SafeState", EVENT_SAFESTATE}, {"SessionLoadPost", EVENT_SESSIONLOADPOST}, {"ShellCmdPost", EVENT_SHELLCMDPOST}, {"ShellFilterPost", EVENT_SHELLFILTERPOST}, *** ../vim-8.1.2043/src/channel.c 2019-09-07 15:45:09.973228904 +0200 --- src/channel.c 2019-09-15 22:36:36.060000155 +0200 *************** *** 3589,3598 **** --- 3589,3605 ---- 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,3609 **** // 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; } if (!more) --- 3607,3615 ---- // search for message "id" if (channel_get_json(channel, part, id, TRUE, rettv) == OK) { ch_log(channel, "Received JSON for id %d", id); ! retval = OK; ! break; } if (!more) *************** *** 3659,3665 **** } if (id >= 0) channel_remove_block_id(chanpart, id); ! return FAIL; } /* --- 3665,3675 ---- } if (id >= 0) channel_remove_block_id(chanpart, id); ! ! // This may trigger a SafeState autocommand. ! leave_unsafe_state(); ! ! return retval; } /* *************** *** 4195,4203 **** free_job_options(&opt); } ! # define KEEP_OPEN_TIME 20 /* msec */ ! # if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) /* * Add open channels to the poll struct. * Return the adjusted struct index. --- 4205,4213 ---- free_job_options(&opt); } ! #define KEEP_OPEN_TIME 20 /* msec */ ! #if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) /* * Add open channels to the poll struct. * Return the adjusted struct index. *************** *** 4288,4296 **** return ret; } ! # endif /* UNIX && !HAVE_SELECT */ ! # if (!defined(MSWIN) && defined(HAVE_SELECT)) || defined(PROTO) /* * The "fd_set" type is hidden to avoid problems with the function proto. --- 4298,4306 ---- return ret; } ! #endif /* UNIX && !HAVE_SELECT */ ! #if (!defined(MSWIN) && defined(HAVE_SELECT)) || defined(PROTO) /* * The "fd_set" type is hidden to avoid problems with the function proto. *************** *** 4381,4387 **** 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(). */ FD_CLR(in_part->ch_fd, wfds); channel_write_input(channel); --ret; --- 4391,4397 ---- 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(). FD_CLR(in_part->ch_fd, wfds); channel_write_input(channel); --ret; *************** *** 4390,4400 **** return ret; } ! # endif /* !MSWIN && HAVE_SELECT */ /* * Execute queued up commands. ! * Invoked from the main loop when it's safe to execute received commands. * Return TRUE when something was done. */ int --- 4400,4411 ---- return ret; } ! #endif // !MSWIN && HAVE_SELECT /* * Execute queued up 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 *** ../vim-8.1.2043/src/edit.c 2019-08-21 14:36:29.383376114 +0200 --- src/edit.c 2019-09-15 22:59:00.044136610 +0200 *************** *** 1509,1514 **** --- 1509,1519 ---- (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 *** ../vim-8.1.2043/src/ex_getln.c 2019-09-09 18:35:28.119252725 +0200 --- src/ex_getln.c 2019-09-15 22:59:05.552108785 +0200 *************** *** 971,976 **** --- 971,979 ---- 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 *** ../vim-8.1.2043/src/version.c 2019-09-15 21:12:18.532950015 +0200 --- src/version.c 2019-09-15 23:00:29.339687077 +0200 *************** *** 759,760 **** --- 759,762 ---- { /* Add new patch number below this line */ + /**/ + 2044, /**/ -- GALAHAD: Camelot ... LAUNCELOT: Camelot ... GAWAIN: It's only a model. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///