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 -- [email protected] -- 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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/201909162056.x8GKusDt021067%40masaka.moolenaar.net.

Raspunde prin e-mail lui