diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-08-21 11:22:25 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2023-08-21 14:16:16 +0800 |
commit | 64ccfdaafef56b451e3a5eed94367fad93978ec8 (patch) | |
tree | 47a238c11a2a234da43fb07cb5db6581b6a4ebcb | |
parent | 4956f267449ca7526145c63ef095bfd731174635 (diff) | |
download | rneovim-64ccfdaafef56b451e3a5eed94367fad93978ec8.tar.gz rneovim-64ccfdaafef56b451e3a5eed94367fad93978ec8.tar.bz2 rneovim-64ccfdaafef56b451e3a5eed94367fad93978ec8.zip |
vim-patch:8.1.2047: cannot check the current state
Problem: Cannot check the current state.
Solution: Add the state() function.
https://github.com/vim/vim/commit/0e57dd859ecb1e8a3b91509d2f4343e839340eb8
Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r-- | runtime/doc/builtin.txt | 26 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vimfn.lua | 29 | ||||
-rw-r--r-- | src/nvim/eval.c | 13 | ||||
-rw-r--r-- | src/nvim/eval.lua | 33 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 41 | ||||
-rw-r--r-- | src/nvim/generators/gen_eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/normal.c | 26 |
7 files changed, 161 insertions, 8 deletions
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 92cedbdc80..c57b8637e0 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -4753,6 +4753,7 @@ mode([expr]) *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 no Operator-pending @@ -7368,6 +7369,31 @@ srand([{expr}]) *srand()* echo rand(seed) < +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 autocommand. + - When SafeState 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: >vim + 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 + c callback invoked (repeats for recursiveness up to "ccc") + s screen has scrolled for messages + stdioopen({opts}) *stdioopen()* With |--headless| this opens stdin and stdout as a |channel|. May be called only once. See |channel-stdio|. stderr is not diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 300210b296..4665f71052 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -5713,6 +5713,7 @@ function vim.fn.mkdir(name, flags, prot) end --- 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 --- no Operator-pending @@ -8744,6 +8745,34 @@ function vim.fn.sqrt(expr) end --- @return any function vim.fn.srand(expr) end +--- 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 autocommand. +--- - When SafeState 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: >vim +--- 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 +--- c callback invoked (repeats for recursiveness up to "ccc") +--- s screen has scrolled for messages +--- +--- @param what? string +--- @return any +function vim.fn.state(what) end + --- With |--headless| this opens stdin and stdout as a |channel|. --- May be called only once. See |channel-stdio|. stderr is not --- handled by this function, see |v:stderr|. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 08c5ef743b..052d67b05f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5994,6 +5994,13 @@ bool callback_from_typval(Callback *const callback, const typval_T *const arg) return true; } +static int callback_depth = 0; + +int get_callback_depth(void) +{ + return callback_depth; +} + /// @return whether the callback could be called. bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) @@ -6041,7 +6048,11 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = true; funcexe.fe_partial = partial; - return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); + + callback_depth++; + int ret = call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); + callback_depth--; + return ret; } bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index be87201bbf..f244d26375 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6917,6 +6917,7 @@ M.funcs = { 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 no Operator-pending @@ -10451,6 +10452,38 @@ M.funcs = { params = { { 'what', 'any' } }, signature = 'stdpath({what})', }, + state = { + args = {0, 1}, + base = 1, + desc = [=[ + 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 autocommand. + - When SafeState 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: >vim + 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 + c callback invoked (repeats for recursiveness up to "ccc") + s screen has scrolled for messages + ]=], + name = 'state', + params = { { 'what', 'string' } }, + signature = 'state([{what}])', + }, str2float = { args = 1, base = 1, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 250b5c5556..c47fd4cfcb 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4925,6 +4925,47 @@ static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->v_type = VAR_STRING; } +static void may_add_state_char(garray_T *gap, const char *include, uint8_t c) +{ + if (include == NULL || vim_strchr(include, c) != NULL) { + ga_append(gap, c); + } +} + +/// "state()" function +static void f_state(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + garray_T ga; + ga_init(&ga, 1, 20); + const char *include = NULL; + + if (argvars[0].v_type != VAR_UNKNOWN) { + include = tv_get_string(&argvars[0]); + } + + if (!(stuff_empty() && typebuf.tb_len == 0 && !using_script())) { + 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'); + } + for (int 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; +} + /// "msgpackdump()" function static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index c7978f0d19..0433894465 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -36,6 +36,7 @@ hashpipe:write([[ #include "nvim/quickfix.h" #include "nvim/runtime.h" #include "nvim/search.h" +#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/sign.h" #include "nvim/testing.h" diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 324bfc0c46..56bc75d658 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -482,6 +482,20 @@ bool check_text_or_curbuf_locked(oparg_T *oap) return true; } +static oparg_T *current_oap = NULL; + +/// Check if an operator was started but not finished yet. +/// Includes typing a count or a register name. +bool 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); +} + /// Normal state entry point. This is called on: /// /// - Startup, In this case the function never returns. @@ -490,15 +504,18 @@ bool check_text_or_curbuf_locked(oparg_T *oap) /// for example. Returns when re-entering ex mode(because ex mode recursion is /// not allowed) /// -/// This used to be called main_loop on main.c +/// This used to be called main_loop() on main.c void normal_enter(bool cmdwin, bool noexmode) { NormalState state; normal_state_init(&state); + oparg_T *prev_oap = current_oap; + current_oap = &state.oa; state.cmdwin = cmdwin; state.noexmode = noexmode; state.toplevel = (!cmdwin || cmdwin_result == 0) && !noexmode; state_enter(&state.state); + current_oap = prev_oap; } static void normal_prepare(NormalState *s) @@ -1299,12 +1316,7 @@ static void normal_check_buffer_modified(NormalState *s) /// type a character, trigger SafeState. static void normal_check_safe_state(NormalState *s) { - may_trigger_safestate(!finish_op - && s->oa.prev_opcount > 0 - && s->oa.prev_count0 == 0 - && s->oa.op_type == OP_NOP - && s->oa.regname == NUL - && restart_edit == 0); + may_trigger_safestate(!op_pending() && restart_edit == 0); } static void normal_check_folds(NormalState *s) |