]> granicus.if.org Git - vim/commitdiff
patch 8.1.2047: cannot check the current state v8.1.2047
authorBram Moolenaar <Bram@vim.org>
Mon, 16 Sep 2019 20:56:03 +0000 (22:56 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 16 Sep 2019 20:56:03 +0000 (22:56 +0200)
Problem:    Cannot check the current state.
Solution:   Add the state() function.

12 files changed:
runtime/doc/eval.txt
src/channel.c
src/evalfunc.c
src/main.c
src/misc1.c
src/proto/channel.pro
src/proto/evalfunc.pro
src/proto/main.pro
src/proto/misc1.pro
src/proto/userfunc.pro
src/userfunc.c
src/version.c

index a8173175943f419c237bd7cff19bbdd4d7302c88..7806120d6a705d77f7404d35110e4bd71eed009f 100644 (file)
@@ -2755,6 +2755,7 @@ spellsuggest({word} [, {max} [, {capital}]])
 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
@@ -7066,6 +7067,7 @@ mode([expr])      Return a string that indicates the current mode.
                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
@@ -9041,6 +9043,34 @@ sqrt({expr})                                             *sqrt()*
                {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
index 0dab3be5f43a7ee39c91acb2a85a5ff635f10d4a..f40d16ab966f417d0e24d27c804a83bb01751cb0 100644 (file)
@@ -3483,6 +3483,7 @@ channel_read(channel_T *channel, ch_part_T part, char *func)
  * 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,6 +3570,17 @@ channel_read_block(
     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,6 +3604,7 @@ channel_read_json_block(
     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,6 +3674,7 @@ channel_read_json_block(
     }
     if (id >= 0)
        channel_remove_block_id(chanpart, id);
+    --channel_blocking_wait;
 
     return retval;
 }
index af72edb18d961b59363ac5cbdcc91a2ac443e0ed..04e131391682a1953bd37642fe803b0e22841427 100644 (file)
@@ -146,7 +146,6 @@ static void f_matchstr(typval_T *argvars, typval_T *rettv);
 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
@@ -723,6 +722,9 @@ static funcentry_T global_functions[] =
     {"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,7 +1048,7 @@ call_internal_method(
 /*
  * Return TRUE for a non-zero Number and a non-empty String.
  */
-    static int
+    int
 non_zero_arg(typval_T *argvars)
 {
     return ((argvars[0].v_type == VAR_NUMBER
@@ -4911,97 +4913,6 @@ f_min(typval_T *argvars, typval_T *rettv)
     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
index 51e3915d96105c6baf74c9a1a1b48bc9f2641bfb..42aa9903434750b9b4d2c08e62577af8911bb825 100644 (file)
@@ -1031,20 +1031,21 @@ is_not_a_term()
 
 // When TRUE in a safe state when starting to wait for a character.
 static int     was_safe = FALSE;
+static oparg_T *current_oap = NULL;
 
 /*
- * Trigger SafeState if currently in a safe state for main_loop().
+ * Return TRUE if an operator was started but not finished yet.
+ * Includes typing a count or a register name.
  */
-    static void
-may_trigger_safestate_main(oparg_T *oap)
+    int
+op_pending(void)
 {
-    may_trigger_safestate(
-           !finish_op
-           && oap->prev_opcount > 0
-           && oap->prev_count0 == 0
-           && oap->op_type == OP_NOP
-           && oap->regname == NUL
-           && restart_edit == 0);
+    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,15 +1101,19 @@ main_loop(
     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 */
+    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 */
+    // 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,7 +1281,7 @@ main_loop(
 
            // If nothing is pending and we are going to wait for the user to
            // type a character, trigger SafeState.
-           may_trigger_safestate_main(&oa);
+           may_trigger_safestate(!op_pending() && restart_edit == 0);
 
 #if defined(FEAT_DIFF)
            // Updating diffs from changed() does not always work properly,
@@ -1430,7 +1435,7 @@ main_loop(
        if (exmode_active)
        {
            if (noexmode)   /* End of ":global/path/visual" commands */
-               return;
+               goto theend;
            do_exmode(exmode_active == EXMODE_VIM);
        }
        else
@@ -1457,6 +1462,9 @@ main_loop(
            }
        }
     }
+
+theend:
+    current_oap = prev_oap;
 }
 
 
index ca5afef00b15ae3bee8067521e8236b6ced6f599..e05ad1654eb42ed678fdb6312a8f74dae32ae9b4 100644 (file)
@@ -1213,6 +1213,144 @@ is_mouse_key(int c)
 }
 #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
index 6bf214706449194a468d000d3dae6ded9f7011b8..fd40a3333c1ab2801dd70f5fed1ea25ce69575ce 100644 (file)
@@ -24,6 +24,7 @@ char *channel_status(channel_T *channel, int req_part);
 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);
index 83dfacf674ba77f81470b49f07503b755398043c..064f12f8d3376c662be2537735af4749c73725e9 100644 (file)
@@ -4,9 +4,9 @@ char_u *get_expr_name(expand_T *xp, int idx);
 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 *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);
index 2c07ceab0d738665da08e7ad666f27d8981f5408..caf435e40ab55408344c041e13146e2b2e0962ce 100644 (file)
@@ -2,6 +2,7 @@
 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);
index 08eb853752f3db192da110d000b3d46fb808aea9..8de1affa48830620d655393bc0f7832bbc00819e 100644 (file)
@@ -24,6 +24,8 @@ char_u *skip_to_option_part(char_u *p);
 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);
index 3b11bb9af884d74e3c940f822583cd5370704dcf..b97e5f69a1e74a1ba68920fb922ccd5afdecce6b 100644 (file)
@@ -10,6 +10,7 @@ void restore_funccal(void);
 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);
index 3f9171d2e6b9e2ef25d8ee20a908a9f428888ba4..a6ac29ece82bdec6ebda47e299dd0bf9f77e4c6f 100644 (file)
@@ -1447,6 +1447,14 @@ func_call(
     return r;
 }
 
+static int callback_depth = 0;
+
+    int
+get_callback_depth(void)
+{
+    return callback_depth;
+}
+
 /*
  * Invoke call_func() with a callback.
  */
@@ -1460,12 +1468,15 @@ call_callback(
                                // PLUS ONE elements!
 {
     funcexe_T  funcexe;
+    int                ret;
 
     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);
+    ++callback_depth;
+    ret = call_func(callback->cb_name, len, rettv, argcount, argvars, &funcexe);
+    --callback_depth;
+    return ret;
 }
 
 /*
index 02e8cfbb31cf6976215ba95d722e099205fac341..622f1687c811f83f89135ac7fe9644536fc58fa7 100644 (file)
@@ -757,6 +757,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2047,
 /**/
     2046,
 /**/