mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #24816 from zeertzjq/vim-8.1.2044
vim-patch:8.1.{2044,2046,2047,2048,2053,2066,2067,2068,2069,2099},8.2.4299: SafeState, state()
This commit is contained in:
commit
3b0515e674
@ -862,6 +862,27 @@ RecordingLeave When a macro stops recording.
|
||||
Sets these |v:event| keys:
|
||||
regcontents
|
||||
regname
|
||||
*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.
|
||||
|
29
runtime/doc/builtin.txt
generated
29
runtime/doc/builtin.txt
generated
@ -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,34 @@ 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, generally indicating that
|
||||
something is busy:
|
||||
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
|
||||
S not triggering SafeState
|
||||
c callback invoked, including timer (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
|
||||
|
@ -619,6 +619,7 @@ Eval:
|
||||
*v:sizeofpointer*
|
||||
|
||||
Events:
|
||||
*SafeStateAgain*
|
||||
*SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead.
|
||||
|
||||
Highlight groups:
|
||||
|
32
runtime/lua/vim/_meta/vimfn.lua
generated
32
runtime/lua/vim/_meta/vimfn.lua
generated
@ -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,37 @@ 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, generally indicating that
|
||||
--- something is busy:
|
||||
--- 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
|
||||
--- S not triggering SafeState
|
||||
--- c callback invoked, including timer (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|.
|
||||
|
@ -85,6 +85,7 @@ return {
|
||||
'RecordingEnter', -- when starting to record a macro
|
||||
'RecordingLeave', -- just before a macro stops recording
|
||||
'RemoteReply', -- upon string reception from a remote vim
|
||||
'SafeState', -- going to wait for a character
|
||||
'SearchWrapped', -- after the search wrapped around
|
||||
'SessionLoadPost', -- after loading a session file
|
||||
'ShellCmdPost', -- after ":!cmd"
|
||||
|
@ -1355,6 +1355,11 @@ void ins_redraw(bool ready)
|
||||
curbuf->b_changed_invalid = false;
|
||||
}
|
||||
|
||||
// Trigger SafeState if nothing is pending.
|
||||
may_trigger_safestate(ready
|
||||
&& !ins_compl_active()
|
||||
&& !pum_visible());
|
||||
|
||||
pum_check_clear();
|
||||
show_cursor_info_later(false);
|
||||
if (must_redraw) {
|
||||
|
@ -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,
|
||||
|
@ -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,42 @@ 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, generally indicating that
|
||||
something is busy:
|
||||
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
|
||||
S not triggering SafeState
|
||||
c callback invoked, including timer (repeats for
|
||||
recursiveness up to "ccc")
|
||||
s screen has scrolled for messages
|
||||
]=],
|
||||
fast = true,
|
||||
name = 'state',
|
||||
params = { { 'what', 'string' } },
|
||||
signature = 'state([{what}])',
|
||||
},
|
||||
str2float = {
|
||||
args = 1,
|
||||
base = 1,
|
||||
|
@ -4925,6 +4925,50 @@ 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 (ins_compl_active()) {
|
||||
may_add_state_char(&ga, include, 'a');
|
||||
}
|
||||
if (!get_was_safe_state()) {
|
||||
may_add_state_char(&ga, include, 'S');
|
||||
}
|
||||
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
|
||||
|
@ -943,6 +943,8 @@ theend:
|
||||
|
||||
static int command_line_check(VimState *state)
|
||||
{
|
||||
CommandLineState *s = (CommandLineState *)state;
|
||||
|
||||
redir_off = true; // Don't redirect the typed command.
|
||||
// Repeated, because a ":redir" inside
|
||||
// completion may switch it on.
|
||||
@ -952,6 +954,9 @@ static int command_line_check(VimState *state)
|
||||
// that occurs while typing a command should
|
||||
// cause the command not to be executed.
|
||||
|
||||
// Trigger SafeState if nothing is pending.
|
||||
may_trigger_safestate(s->xpc.xp_numfiles <= 0);
|
||||
|
||||
cursorcmd(); // set the cursor on the right spot
|
||||
ui_cursor_shape();
|
||||
return 1;
|
||||
|
@ -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"
|
||||
|
@ -885,6 +885,7 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
|
||||
if (++typebuf.tb_change_cnt == 0) {
|
||||
typebuf.tb_change_cnt = 1;
|
||||
}
|
||||
state_no_longer_safe("ins_typebuf()");
|
||||
|
||||
addlen = (int)strlen(str);
|
||||
|
||||
@ -1625,6 +1626,12 @@ int vgetc(void)
|
||||
// Execute Lua on_key callbacks.
|
||||
nlua_execute_on_key(c);
|
||||
|
||||
// Need to process the character before we know it's safe to do something
|
||||
// else.
|
||||
if (c != K_IGNORE) {
|
||||
state_no_longer_safe("key typed");
|
||||
}
|
||||
|
||||
return 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)
|
||||
@ -1295,6 +1312,13 @@ static void normal_check_buffer_modified(NormalState *s)
|
||||
}
|
||||
}
|
||||
|
||||
/// If nothing is pending and we are going to wait for the user to
|
||||
/// type a character, trigger SafeState.
|
||||
static void normal_check_safe_state(NormalState *s)
|
||||
{
|
||||
may_trigger_safestate(!op_pending() && restart_edit == 0);
|
||||
}
|
||||
|
||||
static void normal_check_folds(NormalState *s)
|
||||
{
|
||||
// Include a closed fold completely in the Visual area.
|
||||
@ -1387,6 +1411,9 @@ static int normal_check(VimState *state)
|
||||
}
|
||||
quit_more = false;
|
||||
|
||||
// it's not safe unless normal_check_safe_state() is called
|
||||
state_no_longer_safe(NULL);
|
||||
|
||||
// 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,
|
||||
// update cursor and redraw.
|
||||
@ -1403,6 +1430,7 @@ static int normal_check(VimState *state)
|
||||
normal_check_text_changed(s);
|
||||
normal_check_window_scrolled(s);
|
||||
normal_check_buffer_modified(s);
|
||||
normal_check_safe_state(s);
|
||||
|
||||
// Updating diffs from changed() does not always work properly,
|
||||
// esp. updating folds. Do an update just before redrawing if
|
||||
|
@ -268,3 +268,50 @@ void may_trigger_modechanged(void)
|
||||
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
|
||||
/// When true in a safe state when starting to wait for a character.
|
||||
static bool was_safe = false;
|
||||
|
||||
/// Return whether currently it is safe, assuming it was safe before (high level
|
||||
/// state didn't change).
|
||||
static bool is_safe_now(void)
|
||||
{
|
||||
return stuff_empty()
|
||||
&& typebuf.tb_len == 0
|
||||
&& !using_script()
|
||||
&& !global_busy
|
||||
&& !debug_mode;
|
||||
}
|
||||
|
||||
/// Trigger SafeState if currently in s safe state, that is "safe" is TRUE and
|
||||
/// there is no typeahead.
|
||||
void may_trigger_safestate(bool safe)
|
||||
{
|
||||
bool is_safe = safe && is_safe_now();
|
||||
|
||||
if (was_safe != is_safe) {
|
||||
// Only log when the state changes, otherwise it happens at nearly
|
||||
// every key stroke.
|
||||
DLOG(is_safe ? "SafeState: Start triggering" : "SafeState: Stop triggering");
|
||||
}
|
||||
if (is_safe) {
|
||||
apply_autocmds(EVENT_SAFESTATE, NULL, NULL, false, curbuf);
|
||||
}
|
||||
was_safe = is_safe;
|
||||
}
|
||||
|
||||
/// Something changed which causes the state possibly to be unsafe, e.g. a
|
||||
/// character was typed. It will remain unsafe until the next call to
|
||||
/// may_trigger_safestate().
|
||||
void state_no_longer_safe(const char *reason)
|
||||
{
|
||||
if (was_safe && reason != NULL) {
|
||||
DLOG("SafeState reset: %s", reason);
|
||||
}
|
||||
was_safe = false;
|
||||
}
|
||||
|
||||
bool get_was_safe_state(void)
|
||||
{
|
||||
return was_safe;
|
||||
}
|
||||
|
57
test/functional/autocmd/safestate_spec.lua
Normal file
57
test/functional/autocmd/safestate_spec.lua
Normal file
@ -0,0 +1,57 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
local exec = helpers.exec
|
||||
local feed = helpers.feed
|
||||
local meths = helpers.meths
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('SafeState autocommand', function()
|
||||
local function create_autocmd()
|
||||
exec([[
|
||||
let g:safe = 0
|
||||
autocmd SafeState * ++once let g:safe += 1
|
||||
]])
|
||||
end
|
||||
|
||||
it('with pending operator', function()
|
||||
feed('d')
|
||||
create_autocmd()
|
||||
eq(0, meths.get_var('safe'))
|
||||
feed('d')
|
||||
eq(1, meths.get_var('safe'))
|
||||
end)
|
||||
|
||||
it('with specified register', function()
|
||||
feed('"r')
|
||||
create_autocmd()
|
||||
eq(0, meths.get_var('safe'))
|
||||
feed('x')
|
||||
eq(1, meths.get_var('safe'))
|
||||
end)
|
||||
|
||||
it('with i_CTRL-O', function()
|
||||
feed('i<C-O>')
|
||||
create_autocmd()
|
||||
eq(0, meths.get_var('safe'))
|
||||
feed('x')
|
||||
eq(1, meths.get_var('safe'))
|
||||
end)
|
||||
|
||||
it('with Insert mode completion', function()
|
||||
feed('i<C-X><C-V>')
|
||||
create_autocmd()
|
||||
eq(0, meths.get_var('safe'))
|
||||
feed('<C-X><C-Z>')
|
||||
eq(1, meths.get_var('safe'))
|
||||
end)
|
||||
|
||||
it('with Cmdline completion', function()
|
||||
feed(':<Tab>')
|
||||
create_autocmd()
|
||||
eq(0, meths.get_var('safe'))
|
||||
feed('<C-E>')
|
||||
eq(1, meths.get_var('safe'))
|
||||
end)
|
||||
end)
|
69
test/functional/vimscript/state_spec.lua
Normal file
69
test/functional/vimscript/state_spec.lua
Normal file
@ -0,0 +1,69 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
local exec = helpers.exec
|
||||
local exec_lua = helpers.exec_lua
|
||||
local feed = helpers.feed
|
||||
local meths = helpers.meths
|
||||
local poke_eventloop = helpers.poke_eventloop
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('state() function', function()
|
||||
it('works', function()
|
||||
meths.ui_attach(80, 24, {}) -- Allow hit-enter-prompt
|
||||
|
||||
exec_lua([[
|
||||
function _G.Get_state_mode()
|
||||
_G.res = { vim.fn.state(), vim.api.nvim_get_mode().mode:sub(1, 1) }
|
||||
end
|
||||
function _G.Run_timer()
|
||||
local timer = vim.uv.new_timer()
|
||||
timer:start(0, 0, function()
|
||||
_G.Get_state_mode()
|
||||
timer:close()
|
||||
end)
|
||||
end
|
||||
]])
|
||||
exec([[
|
||||
call setline(1, ['one', 'two', 'three'])
|
||||
map ;; gg
|
||||
set complete=.
|
||||
func RunTimer()
|
||||
call timer_start(0, {id -> v:lua.Get_state_mode()})
|
||||
endfunc
|
||||
au Filetype foobar call v:lua.Get_state_mode()
|
||||
]])
|
||||
|
||||
-- Using a ":" command Vim is busy, thus "S" is returned
|
||||
feed([[:call v:lua.Get_state_mode()<CR>]])
|
||||
eq({ 'S', 'n' }, exec_lua('return _G.res'))
|
||||
|
||||
-- Using a timer callback
|
||||
feed([[:call RunTimer()<CR>]])
|
||||
poke_eventloop() -- Allow polling for events
|
||||
eq({ 'c', 'n' }, exec_lua('return _G.res'))
|
||||
|
||||
-- Halfway a mapping
|
||||
feed([[:call v:lua.Run_timer()<CR>;]])
|
||||
meths.get_mode() -- Allow polling for fast events
|
||||
feed(';')
|
||||
eq({ 'mS', 'n' }, exec_lua('return _G.res'))
|
||||
|
||||
-- Insert mode completion
|
||||
feed([[:call RunTimer()<CR>Got<C-N>]])
|
||||
poke_eventloop() -- Allow polling for events
|
||||
feed('<Esc>')
|
||||
eq({ 'aSc', 'i' }, exec_lua('return _G.res'))
|
||||
|
||||
-- Autocommand executing
|
||||
feed([[:set filetype=foobar<CR>]])
|
||||
eq({ 'xS', 'n' }, exec_lua('return _G.res'))
|
||||
|
||||
-- messages scrolled
|
||||
feed([[:call v:lua.Run_timer() | echo "one\ntwo\nthree"<CR>]])
|
||||
meths.get_mode() -- Allow polling for fast events
|
||||
feed('<CR>')
|
||||
eq({ 'Ss', 'r' }, exec_lua('return _G.res'))
|
||||
end)
|
||||
end)
|
@ -2959,6 +2959,39 @@ func Test_autocmd_in_try_block()
|
||||
au! BufEnter
|
||||
endfunc
|
||||
|
||||
func Test_autocmd_SafeState()
|
||||
CheckRunVimInTerminal
|
||||
|
||||
let lines =<< trim END
|
||||
let g:safe = 0
|
||||
let g:again = ''
|
||||
au SafeState * let g:safe += 1
|
||||
au SafeStateAgain * let g:again ..= 'x'
|
||||
func CallTimer()
|
||||
call timer_start(10, {id -> execute('let g:again ..= "t"')})
|
||||
endfunc
|
||||
END
|
||||
call writefile(lines, 'XSafeState')
|
||||
let buf = RunVimInTerminal('-S XSafeState', #{rows: 6})
|
||||
|
||||
" Sometimes we loop to handle an K_IGNORE
|
||||
call term_sendkeys(buf, ":echo g:safe\<CR>")
|
||||
call WaitForAssert({-> assert_match('^[12] ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
call term_sendkeys(buf, ":echo g:again\<CR>")
|
||||
call WaitForAssert({-> assert_match('^xxxx', term_getline(buf, 6))}, 1000)
|
||||
|
||||
call term_sendkeys(buf, ":let g:again = ''\<CR>:call CallTimer()\<CR>")
|
||||
call term_wait(buf)
|
||||
call term_sendkeys(buf, ":\<CR>")
|
||||
call term_wait(buf)
|
||||
call term_sendkeys(buf, ":echo g:again\<CR>")
|
||||
call WaitForAssert({-> assert_match('xtx', term_getline(buf, 6))}, 1000)
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
call delete('XSafeState')
|
||||
endfunc
|
||||
|
||||
func Test_autocmd_CmdWinEnter()
|
||||
CheckRunVimInTerminal
|
||||
" There is not cmdwin switch, so
|
||||
|
@ -2565,6 +2565,66 @@ func Test_bufadd_bufload()
|
||||
call delete('XotherName')
|
||||
endfunc
|
||||
|
||||
func Test_state()
|
||||
CheckRunVimInTerminal
|
||||
|
||||
let lines =<< trim END
|
||||
call setline(1, ['one', 'two', 'three'])
|
||||
map ;; gg
|
||||
set complete=.
|
||||
func RunTimer()
|
||||
call timer_start(10, {id -> execute('let g:state = state()') .. execute('let g:mode = mode()')})
|
||||
endfunc
|
||||
au Filetype foobar let g:state = state()|let g:mode = mode()
|
||||
END
|
||||
call writefile(lines, 'XState')
|
||||
let buf = RunVimInTerminal('-S XState', #{rows: 6})
|
||||
|
||||
" Using a ":" command Vim is busy, thus "S" is returned
|
||||
call term_sendkeys(buf, ":echo 'state: ' .. state() .. '; mode: ' .. mode()\<CR>")
|
||||
call WaitForAssert({-> assert_match('state: S; mode: n', term_getline(buf, 6))}, 1000)
|
||||
call term_sendkeys(buf, ":\<CR>")
|
||||
|
||||
" Using a timer callback
|
||||
call term_sendkeys(buf, ":call RunTimer()\<CR>")
|
||||
call term_wait(buf, 50)
|
||||
let getstate = ":echo 'state: ' .. g:state .. '; mode: ' .. g:mode\<CR>"
|
||||
call term_sendkeys(buf, getstate)
|
||||
call WaitForAssert({-> assert_match('state: c; mode: n', term_getline(buf, 6))}, 1000)
|
||||
|
||||
" Halfway a mapping
|
||||
call term_sendkeys(buf, ":call RunTimer()\<CR>;")
|
||||
call term_wait(buf, 50)
|
||||
call term_sendkeys(buf, ";")
|
||||
call term_sendkeys(buf, getstate)
|
||||
call WaitForAssert({-> assert_match('state: mSc; mode: n', term_getline(buf, 6))}, 1000)
|
||||
|
||||
" Insert mode completion (bit slower on Mac)
|
||||
call term_sendkeys(buf, ":call RunTimer()\<CR>Got\<C-N>")
|
||||
call term_wait(buf, 200)
|
||||
call term_sendkeys(buf, "\<Esc>")
|
||||
call term_sendkeys(buf, getstate)
|
||||
call WaitForAssert({-> assert_match('state: aSc; mode: i', term_getline(buf, 6))}, 1000)
|
||||
|
||||
" Autocommand executing
|
||||
call term_sendkeys(buf, ":set filetype=foobar\<CR>")
|
||||
call term_wait(buf, 50)
|
||||
call term_sendkeys(buf, getstate)
|
||||
call WaitForAssert({-> assert_match('state: xS; mode: n', term_getline(buf, 6))}, 1000)
|
||||
|
||||
" Todo: "w" - waiting for ch_evalexpr()
|
||||
|
||||
" messages scrolled
|
||||
call term_sendkeys(buf, ":call RunTimer()\<CR>:echo \"one\\ntwo\\nthree\"\<CR>")
|
||||
call term_wait(buf, 50)
|
||||
call term_sendkeys(buf, "\<CR>")
|
||||
call term_sendkeys(buf, getstate)
|
||||
call WaitForAssert({-> assert_match('state: Scs; mode: r', term_getline(buf, 6))}, 1000)
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
call delete('XState')
|
||||
endfunc
|
||||
|
||||
func Test_range()
|
||||
" destructuring
|
||||
let [x, y] = range(2)
|
||||
|
Loading…
Reference in New Issue
Block a user