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 -- Br...@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 ///