Patch 8.1.2047
Problem: Cannot check the current state.
Solution: Add the state() function.
Files: runtime/doc/eval.txt, src/misc1.c, src/proto/
misc1.pro,
src/evalfunc.c, src/proto/
evalfunc.pro, src/main.c,
src/proto/
main.pro, src/channel.c, src/proto/
channel.pro,
src/userfunc.c, src/proto/
userfunc.pro
*** ../vim-8.1.2046/runtime/doc/eval.txt 2019-09-15 14:32:49.552731470 +0200
--- runtime/doc/eval.txt 2019-09-16 22:53:17.384608555 +0200
***************
*** 2750,2755 ****
--- 2755,2761 ----
split({expr} [, {pat} [, {keepempty}]])
List make |List| from {pat} separated {expr}
sqrt({expr}) Float square root of {expr}
+ state([{what}]) String current state of Vim
str2float({expr}) Float convert String to Float
str2list({expr} [, {utf8}]) List convert each character of {expr} to
ASCII/UTF8 value
***************
*** 7056,7061 ****
--- 7067,7073 ----
If [expr] is supplied and it evaluates to a non-zero Number or
a non-empty String (|non-zero-arg|), then the full mode is
returned, otherwise only the first letter is returned.
+ Also see |state()|.
n Normal, Terminal-Normal
no Operator-pending
***************
*** 9031,9036 ****
--- 9043,9076 ----
{only available when compiled with the |+float| feature}
+ state([{what}]) *state()*
+ Return a string which contains characters indicating the
+ current state. Mostly useful in callbacks that want to do
+ work that may not always be safe. Roughly this works like:
+ - callback uses state() to check if work is safe to do.
+ If yes, then do it right away.
+ Otherwise add to work queue and add SafeState and/or
+ SafeStateAgain autocommand.
+ - When SafeState or SafeStateAgain is triggered, check with
+ state() if the work can be done now, and if yes remove it
+ from the queue and execute.
+ Also see |mode()|.
+
+ When {what} is given only characters in this string will be
+ added. E.g, this checks if the screen has scrolled: >
+ if state('s') != ''
+ <
+ These characters indicate the state:
+ m halfway a mapping, :normal command, feedkeys() or
+ stuffed command
+ o operator pending or waiting for a command argument
+ a Insert mode autocomplete active
+ x executing an autocommand
+ w blocked on waiting, e.g. ch_evalexpr() and
+ ch_read(), ch_readraw() when reading json.
+ c callback invoked (repeats for recursiveness up to "ccc")
+ s screen has scrolled for messages
+
str2float({expr}) *str2float()*
Convert String {expr} to a Float. This mostly works the same
as when using a floating point number in an expression, see
*** ../vim-8.1.2046/src/misc1.c 2019-09-10 21:27:15.175646978 +0200
--- src/misc1.c 2019-09-16 22:44:09.886708358 +0200
***************
*** 1213,1218 ****
--- 1213,1356 ----
}
#endif
+ #if defined(FEAT_EVAL) || defined(PROTO)
+
+ /*
+ * "mode()" function
+ */
+ void
+ f_mode(typval_T *argvars, typval_T *rettv)
+ {
+ char_u buf[4];
+
+ vim_memset(buf, 0, sizeof(buf));
+
+ if (time_for_testing == 93784)
+ {
+ /* Testing the two-character code. */
+ buf[0] = 'x';
+ buf[1] = '!';
+ }
+ #ifdef FEAT_TERMINAL
+ else if (term_use_loop())
+ buf[0] = 't';
+ #endif
+ else if (VIsual_active)
+ {
+ if (VIsual_select)
+ buf[0] = VIsual_mode + 's' - 'v';
+ else
+ buf[0] = VIsual_mode;
+ }
+ else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
+ || State == CONFIRM)
+ {
+ buf[0] = 'r';
+ if (State == ASKMORE)
+ buf[1] = 'm';
+ else if (State == CONFIRM)
+ buf[1] = '?';
+ }
+ else if (State == EXTERNCMD)
+ buf[0] = '!';
+ else if (State & INSERT)
+ {
+ if (State & VREPLACE_FLAG)
+ {
+ buf[0] = 'R';
+ buf[1] = 'v';
+ }
+ else
+ {
+ if (State & REPLACE_FLAG)
+ buf[0] = 'R';
+ else
+ buf[0] = 'i';
+ if (ins_compl_active())
+ buf[1] = 'c';
+ else if (ctrl_x_mode_not_defined_yet())
+ buf[1] = 'x';
+ }
+ }
+ else if ((State & CMDLINE) || exmode_active)
+ {
+ buf[0] = 'c';
+ if (exmode_active == EXMODE_VIM)
+ buf[1] = 'v';
+ else if (exmode_active == EXMODE_NORMAL)
+ buf[1] = 'e';
+ }
+ else
+ {
+ buf[0] = 'n';
+ if (finish_op)
+ {
+ buf[1] = 'o';
+ // to be able to detect force-linewise/blockwise/characterwise operations
+ buf[2] = motion_force;
+ }
+ else if (restart_edit == 'I' || restart_edit == 'R'
+ || restart_edit == 'V')
+ {
+ buf[1] = 'i';
+ buf[2] = restart_edit;
+ }
+ }
+
+ /* Clear out the minor mode when the argument is not a non-zero number or
+ * non-empty string. */
+ if (!non_zero_arg(&argvars[0]))
+ buf[1] = NUL;
+
+ rettv->vval.v_string = vim_strsave(buf);
+ rettv->v_type = VAR_STRING;
+ }
+
+ static void
+ may_add_state_char(garray_T *gap, char_u *include, int c)
+ {
+ if (include == NULL || vim_strchr(include, c) != NULL)
+ ga_append(gap, c);
+ }
+
+ /*
+ * "state()" function
+ */
+ void
+ f_state(typval_T *argvars, typval_T *rettv)
+ {
+ garray_T ga;
+ char_u *include = NULL;
+ int i;
+
+ ga_init2(&ga, 1, 20);
+ if (argvars[0].v_type != VAR_UNKNOWN)
+ include = tv_get_string(&argvars[0]);
+
+ if (!(stuff_empty() && typebuf.tb_len == 0 && scriptin[curscript] == NULL))
+ may_add_state_char(&ga, include, 'm');
+ if (op_pending())
+ may_add_state_char(&ga, include, 'o');
+ if (autocmd_busy)
+ may_add_state_char(&ga, include, 'x');
+ if (!ctrl_x_mode_none())
+ may_add_state_char(&ga, include, 'a');
+
+ # ifdef FEAT_JOB_CHANNEL
+ if (channel_in_blocking_wait())
+ may_add_state_char(&ga, include, 'w');
+ # endif
+ for (i = 0; i < get_callback_depth() && i < 3; ++i)
+ may_add_state_char(&ga, include, 'c');
+ if (msg_scrolled > 0)
+ may_add_state_char(&ga, include, 's');
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = ga.ga_data;
+ }
+
+ #endif // FEAT_EVAL
+
/*
* Get a key stroke directly from the user.
* Ignores mouse clicks and scrollbar events, except a click for the left
*** ../vim-8.1.2046/src/proto/
misc1.pro 2019-09-10 21:27:15.175646978 +0200
--- src/proto/
misc1.pro 2019-09-16 22:29:00.170493457 +0200
***************
*** 24,29 ****
--- 24,31 ----
void check_status(buf_T *buf);
int ask_yesno(char_u *str, int direct);
int is_mouse_key(int c);
+ void f_mode(typval_T *argvars, typval_T *rettv);
+ void f_state(typval_T *argvars, typval_T *rettv);
int get_keystroke(void);
int get_number(int colon, int *mouse_used);
int prompt_for_number(int *mouse_used);
*** ../vim-8.1.2046/src/evalfunc.c 2019-09-15 21:00:51.362604284 +0200
--- src/evalfunc.c 2019-09-16 22:29:49.386320301 +0200
***************
*** 146,152 ****
static void f_matchstrpos(typval_T *argvars, typval_T *rettv);
static void f_max(typval_T *argvars, typval_T *rettv);
static void f_min(typval_T *argvars, typval_T *rettv);
- static void f_mode(typval_T *argvars, typval_T *rettv);
#ifdef FEAT_MZSCHEME
static void f_mzeval(typval_T *argvars, typval_T *rettv);
#endif
--- 146,151 ----
***************
*** 723,728 ****
--- 722,730 ----
{"split", 1, 3, FEARG_1, f_split},
#ifdef FEAT_FLOAT
{"sqrt", 1, 1, FEARG_1, f_sqrt},
+ #endif
+ {"state", 0, 1, FEARG_1, f_state},
+ #ifdef FEAT_FLOAT
{"str2float", 1, 1, FEARG_1, f_str2float},
#endif
{"str2list", 1, 2, FEARG_1, f_str2list},
***************
*** 1046,1052 ****
/*
* Return TRUE for a non-zero Number and a non-empty String.
*/
! static int
non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
--- 1048,1054 ----
/*
* Return TRUE for a non-zero Number and a non-empty String.
*/
! int
non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
***************
*** 4911,5007 ****
max_min(argvars, rettv, FALSE);
}
- /*
- * "mode()" function
- */
- static void
- f_mode(typval_T *argvars, typval_T *rettv)
- {
- char_u buf[4];
-
- vim_memset(buf, 0, sizeof(buf));
-
- if (time_for_testing == 93784)
- {
- /* Testing the two-character code. */
- buf[0] = 'x';
- buf[1] = '!';
- }
- #ifdef FEAT_TERMINAL
- else if (term_use_loop())
- buf[0] = 't';
- #endif
- else if (VIsual_active)
- {
- if (VIsual_select)
- buf[0] = VIsual_mode + 's' - 'v';
- else
- buf[0] = VIsual_mode;
- }
- else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
- || State == CONFIRM)
- {
- buf[0] = 'r';
- if (State == ASKMORE)
- buf[1] = 'm';
- else if (State == CONFIRM)
- buf[1] = '?';
- }
- else if (State == EXTERNCMD)
- buf[0] = '!';
- else if (State & INSERT)
- {
- if (State & VREPLACE_FLAG)
- {
- buf[0] = 'R';
- buf[1] = 'v';
- }
- else
- {
- if (State & REPLACE_FLAG)
- buf[0] = 'R';
- else
- buf[0] = 'i';
- if (ins_compl_active())
- buf[1] = 'c';
- else if (ctrl_x_mode_not_defined_yet())
- buf[1] = 'x';
- }
- }
- else if ((State & CMDLINE) || exmode_active)
- {
- buf[0] = 'c';
- if (exmode_active == EXMODE_VIM)
- buf[1] = 'v';
- else if (exmode_active == EXMODE_NORMAL)
- buf[1] = 'e';
- }
- else
- {
- buf[0] = 'n';
- if (finish_op)
- {
- buf[1] = 'o';
- // to be able to detect force-linewise/blockwise/characterwise operations
- buf[2] = motion_force;
- }
- else if (restart_edit == 'I' || restart_edit == 'R'
- || restart_edit == 'V')
- {
- buf[1] = 'i';
- buf[2] = restart_edit;
- }
- }
-
- /* Clear out the minor mode when the argument is not a non-zero number or
- * non-empty string. */
- if (!non_zero_arg(&argvars[0]))
- buf[1] = NUL;
-
- rettv->vval.v_string = vim_strsave(buf);
- rettv->v_type = VAR_STRING;
- }
-
#if defined(FEAT_MZSCHEME) || defined(PROTO)
/*
* "mzeval()" function
--- 4913,4918 ----
*** ../vim-8.1.2046/src/proto/
evalfunc.pro 2019-09-07 15:45:09.977228927 +0200
--- src/proto/
evalfunc.pro 2019-09-16 22:30:12.546238795 +0200
***************
*** 4,12 ****
int has_internal_func(char_u *name);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
linenr_T tv_get_lnum(typval_T *argvars);
linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
- buf_T *buflist_find_by_name(char_u *name, int curtab_only);
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
buf_T *get_buf_arg(typval_T *arg);
win_T *get_optional_window(typval_T *argvars, int idx);
--- 4,12 ----
int has_internal_func(char_u *name);
int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv);
int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv);
+ int non_zero_arg(typval_T *argvars);
linenr_T tv_get_lnum(typval_T *argvars);
linenr_T tv_get_lnum_buf(typval_T *argvars, buf_T *buf);
buf_T *tv_get_buf(typval_T *tv, int curtab_only);
buf_T *get_buf_arg(typval_T *arg);
win_T *get_optional_window(typval_T *argvars, int idx);
*** ../vim-8.1.2046/src/main.c 2019-09-16 21:58:08.792800986 +0200
--- src/main.c 2019-09-16 22:28:25.686614721 +0200
***************
*** 1031,1050 ****
// When TRUE in a safe state when starting to wait for a character.
static int was_safe = FALSE;
/*
! * 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);
}
/*
--- 1031,1051 ----
// When TRUE in a safe state when starting to wait for a character.
static int was_safe = FALSE;
+ static oparg_T *current_oap = NULL;
/*
! * Return TRUE if an operator was started but not finished yet.
! * Includes typing a count or a register name.
*/
! int
! op_pending(void)
{
! return !(current_oap != NULL
! && !finish_op
! && current_oap->prev_opcount == 0
! && current_oap->prev_count0 == 0
! && current_oap->op_type == OP_NOP
! && current_oap->regname == NUL);
}
/*
***************
*** 1100,1114 ****
int cmdwin, /* TRUE when working in the command-line window */
int noexmode) /* TRUE when return on entering Ex mode */
{
! oparg_T oa; /* operator arguments */
! volatile int previous_got_int = FALSE; /* "got_int" was TRUE */
#ifdef FEAT_CONCEAL
! /* these are static to avoid a compiler warning */
static linenr_T conceal_old_cursor_line = 0;
static linenr_T conceal_new_cursor_line = 0;
static int conceal_update_lines = FALSE;
#endif
#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
/* Setup to catch a terminating error from the X server. Just ignore
* it, restore the state and continue. This might not always work
--- 1101,1119 ----
int cmdwin, /* TRUE when working in the command-line window */
int noexmode) /* TRUE when return on entering Ex mode */
{
! oparg_T oa; // operator arguments
! oparg_T *prev_oap; // operator arguments
! volatile int previous_got_int = FALSE; // "got_int" was TRUE
#ifdef FEAT_CONCEAL
! // these are static to avoid a compiler warning
static linenr_T conceal_old_cursor_line = 0;
static linenr_T conceal_new_cursor_line = 0;
static int conceal_update_lines = FALSE;
#endif
+ prev_oap = current_oap;
+ current_oap = &oa;
+
#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
/* Setup to catch a terminating error from the X server. Just ignore
* it, restore the state and continue. This might not always work
***************
*** 1276,1282 ****
// 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,
--- 1281,1287 ----
// If nothing is pending and we are going to wait for the user to
// type a character, trigger SafeState.
! may_trigger_safestate(!op_pending() && restart_edit == 0);
#if defined(FEAT_DIFF)
// Updating diffs from changed() does not always work properly,
***************
*** 1430,1436 ****
if (exmode_active)
{
if (noexmode) /* End of ":global/path/visual" commands */
! return;
do_exmode(exmode_active == EXMODE_VIM);
}
else
--- 1435,1441 ----
if (exmode_active)
{
if (noexmode) /* End of ":global/path/visual" commands */
! goto theend;
do_exmode(exmode_active == EXMODE_VIM);
}
else
***************
*** 1457,1462 ****
--- 1462,1470 ----
}
}
}
+
+ theend:
+ current_oap = prev_oap;
}
*** ../vim-8.1.2046/src/proto/
main.pro 2019-09-16 21:58:08.792800986 +0200
--- src/proto/
main.pro 2019-09-16 22:28:30.294598515 +0200
***************
*** 2,7 ****
--- 2,8 ----
int vim_main2(void);
void common_init(mparm_T *paramp);
int is_not_a_term(void);
+ int op_pending(void);
void may_trigger_safestate(int safe);
void state_no_longer_safe(void);
void leave_unsafe_state(void);
*** ../vim-8.1.2046/src/channel.c 2019-09-16 21:58:08.792800986 +0200
--- src/channel.c 2019-09-16 22:38:13.432152959 +0200
***************
*** 3483,3488 ****
--- 3483,3489 ----
* Read from RAW or NL "channel"/"part". Blocks until there is something to
* read or the timeout expires.
* When "raw" is TRUE don't block waiting on a NL.
+ * Does not trigger timers or handle messages.
* Returns what was read in allocated memory.
* Returns NULL in case of error or timeout.
*/
***************
*** 3569,3574 ****
--- 3570,3586 ----
return msg;
}
+ static int channel_blocking_wait = 0;
+
+ /*
+ * Return TRUE if in a blocking wait that might trigger callbacks.
+ */
+ int
+ channel_in_blocking_wait(void)
+ {
+ return channel_blocking_wait > 0;
+ }
+
/*
* Read one JSON message with ID "id" from "channel"/"part" and store the
* result in "rettv".
***************
*** 3592,3597 ****
--- 3604,3610 ----
int retval = FAIL;
ch_log(channel, "Blocking read JSON for id %d", id);
+ ++channel_blocking_wait;
if (id >= 0)
channel_add_block_id(chanpart, id);
***************
*** 3661,3666 ****
--- 3674,3680 ----
}
if (id >= 0)
channel_remove_block_id(chanpart, id);
+ --channel_blocking_wait;
return retval;
}
*** ../vim-8.1.2046/src/proto/
channel.pro 2019-08-20 20:13:40.330821936 +0200
--- src/proto/
channel.pro 2019-09-16 22:38:16.424140406 +0200
***************
*** 24,29 ****
--- 24,30 ----
void channel_close(channel_T *channel, int invoke_close_cb);
void channel_clear(channel_T *channel);
void channel_free_all(void);
+ int channel_in_blocking_wait(void);
void channel_handle_events(int only_keep_open);
int channel_any_keep_open(void);
void channel_set_nonblock(channel_T *channel, ch_part_T part);
*** ../vim-8.1.2046/src/userfunc.c 2019-08-29 21:32:52.248093098 +0200
--- src/userfunc.c 2019-09-16 22:42:10.199183306 +0200
***************
*** 1447,1452 ****
--- 1447,1460 ----
return r;
}
+ static int callback_depth = 0;
+
+ int
+ get_callback_depth(void)
+ {
+ return callback_depth;
+ }
+
/*
* Invoke call_func() with a callback.
*/
***************
*** 1460,1471 ****
// PLUS ONE elements!
{
funcexe_T funcexe;
vim_memset(&funcexe, 0, sizeof(funcexe));
funcexe.evaluate = TRUE;
funcexe.partial = callback->cb_partial;
! return call_func(callback->cb_name, len, rettv, argcount, argvars,
! &funcexe);
}
/*
--- 1468,1482 ----
// PLUS ONE elements!
{
funcexe_T funcexe;
+ int ret;
vim_memset(&funcexe, 0, sizeof(funcexe));
funcexe.evaluate = TRUE;
funcexe.partial = callback->cb_partial;
! ++callback_depth;
! ret = call_func(callback->cb_name, len, rettv, argcount, argvars, &funcexe);
! --callback_depth;
! return ret;
}
/*
*** ../vim-8.1.2046/src/proto/
userfunc.pro 2019-08-03 18:17:07.680638632 +0200
--- src/proto/
userfunc.pro 2019-09-16 22:42:29.207107298 +0200
***************
*** 10,15 ****
--- 10,16 ----
funccall_T *get_current_funccal(void);
void free_all_functions(void);
int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
+ int get_callback_depth(void);
int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial);
*** ../vim-8.1.2046/src/version.c 2019-09-16 21:58:08.792800986 +0200
--- src/version.c 2019-09-16 22:11:29.370159862 +0200
***************
*** 759,760 ****
--- 759,762 ----
{ /* Add new patch number below this line */
+ /**/
+ 2047,
/**/
--
A KNIGHT rides into shot and hacks him to the ground. He rides off.
We stay for a moment on the glade. A MIDDLE-AGED LADY in a C. & A.
twin-set emerges from the trees and looks in horror at the body of her
HUSBAND.
MRS HISTORIAN: FRANK!
"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 ///