mirror of
https://github.com/neovim/neovim.git
synced 2025-11-28 21:20:45 +00:00
Merge pull request #13686 from bfredl/fastevent
state: throttle batched event processing when input is available
This commit is contained in:
@@ -1024,7 +1024,7 @@ static int insert_handle_key(InsertState *s)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case K_EVENT: // some event
|
case K_EVENT: // some event
|
||||||
multiqueue_process_events(main_loop.events);
|
state_handle_k_event();
|
||||||
goto check_pum;
|
goto check_pum;
|
||||||
|
|
||||||
case K_COMMAND: // some command
|
case K_COMMAND: // some command
|
||||||
|
|||||||
@@ -3029,10 +3029,11 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
if (argvars[0].v_type == VAR_UNKNOWN) {
|
if (argvars[0].v_type == VAR_UNKNOWN) {
|
||||||
// getchar(): blocking wait.
|
// getchar(): blocking wait.
|
||||||
|
// TODO(bfredl): deduplicate shared logic with state_enter ?
|
||||||
if (!(char_avail() || using_script() || input_available())) {
|
if (!(char_avail() || using_script() || input_available())) {
|
||||||
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
|
(void)os_inchar(NULL, 0, -1, 0, main_loop.events);
|
||||||
if (!multiqueue_empty(main_loop.events)) {
|
if (!multiqueue_empty(main_loop.events)) {
|
||||||
multiqueue_process_events(main_loop.events);
|
state_handle_k_event();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -935,7 +935,7 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
|
|
||||||
if (s->c == K_EVENT || s->c == K_COMMAND) {
|
if (s->c == K_EVENT || s->c == K_COMMAND) {
|
||||||
if (s->c == K_EVENT) {
|
if (s->c == K_EVENT) {
|
||||||
multiqueue_process_events(main_loop.events);
|
state_handle_k_event();
|
||||||
} else {
|
} else {
|
||||||
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
|
do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8103,7 +8103,7 @@ static void nv_event(cmdarg_T *cap)
|
|||||||
// lists or dicts being used.
|
// lists or dicts being used.
|
||||||
may_garbage_collect = false;
|
may_garbage_collect = false;
|
||||||
bool may_restart = (restart_edit != 0);
|
bool may_restart = (restart_edit != 0);
|
||||||
multiqueue_process_events(main_loop.events);
|
state_handle_k_event();
|
||||||
finish_op = false;
|
finish_op = false;
|
||||||
if (may_restart) {
|
if (may_restart) {
|
||||||
// Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
|
// Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
|
||||||
|
|||||||
@@ -159,16 +159,28 @@ bool os_char_avail(void)
|
|||||||
return inbuf_poll(0, NULL) == kInputAvail;
|
return inbuf_poll(0, NULL) == kInputAvail;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for CTRL-C typed by reading all available characters.
|
/// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
|
||||||
|
///
|
||||||
|
/// This invokes a full libuv loop iteration which can be quite costly.
|
||||||
|
/// Prefer `line_breakcheck()` if called in a busy inner loop.
|
||||||
|
///
|
||||||
|
/// Caller must at least check `got_int` before calling this function again.
|
||||||
|
/// checking for other low-level input state like `input_available()` might
|
||||||
|
/// also be relevant (i e to throttle idle processing when user input is
|
||||||
|
/// available)
|
||||||
void os_breakcheck(void)
|
void os_breakcheck(void)
|
||||||
{
|
{
|
||||||
|
if (got_int) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int save_us = updating_screen;
|
int save_us = updating_screen;
|
||||||
// We do not want screen_resize() to redraw here.
|
// We do not want screen_resize() to redraw here.
|
||||||
|
// TODO(bfredl): we are already special casing redraw events, is this
|
||||||
|
// hack still needed?
|
||||||
updating_screen++;
|
updating_screen++;
|
||||||
|
|
||||||
if (!got_int) {
|
loop_poll_events(&main_loop, 0);
|
||||||
loop_poll_events(&main_loop, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
updating_screen = save_us;
|
updating_screen = save_us;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,34 @@ getkey:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// process events on main_loop, but interrupt if input is available
|
||||||
|
///
|
||||||
|
/// This should be used to handle K_EVENT in states accepting input
|
||||||
|
/// otherwise bursts of events can block break checking indefinitely.
|
||||||
|
void state_handle_k_event(void)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
Event event = multiqueue_get(main_loop.events);
|
||||||
|
if (event.handler) {
|
||||||
|
event.handler(event.argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multiqueue_empty(main_loop.events)) {
|
||||||
|
// don't breakcheck before return, caller should return to main-loop
|
||||||
|
// and handle input already.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bfredl): as an further micro-optimization, we could check whether
|
||||||
|
// event.handler already checked input.
|
||||||
|
os_breakcheck();
|
||||||
|
if (input_available() || got_int) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Return true if in the current mode we need to use virtual.
|
/// Return true if in the current mode we need to use virtual.
|
||||||
bool virtual_active(void)
|
bool virtual_active(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ static int terminal_execute(VimState *state, int key)
|
|||||||
case K_EVENT:
|
case K_EVENT:
|
||||||
// We cannot let an event free the terminal yet. It is still needed.
|
// We cannot let an event free the terminal yet. It is still needed.
|
||||||
s->term->refcount++;
|
s->term->refcount++;
|
||||||
multiqueue_process_events(main_loop.events);
|
state_handle_k_event();
|
||||||
s->term->refcount--;
|
s->term->refcount--;
|
||||||
if (s->term->buf_handle == 0) {
|
if (s->term->buf_handle == 0) {
|
||||||
s->close = true;
|
s->close = true;
|
||||||
|
|||||||
@@ -257,6 +257,7 @@ describe('LSP', function()
|
|||||||
eq(0, client.resolved_capabilities().text_document_did_change)
|
eq(0, client.resolved_capabilities().text_document_did_change)
|
||||||
client.request('shutdown')
|
client.request('shutdown')
|
||||||
client.notify('exit')
|
client.notify('exit')
|
||||||
|
client.stop()
|
||||||
end;
|
end;
|
||||||
on_exit = function(code, signal)
|
on_exit = function(code, signal)
|
||||||
eq(0, code, "exit code", fake_lsp_logfile)
|
eq(0, code, "exit code", fake_lsp_logfile)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim
|
local clear, feed_command = helpers.clear, helpers.feed_command
|
||||||
local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
|
local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
local expect = helpers.expect
|
local expect = helpers.expect
|
||||||
|
local meths = helpers.meths
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
local write_file = helpers.write_file
|
local write_file = helpers.write_file
|
||||||
local Screen = require('test.functional.ui.screen')
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
describe('mappings', function()
|
before_each(clear)
|
||||||
local cid
|
|
||||||
|
|
||||||
|
describe('mappings', function()
|
||||||
local add_mapping = function(mapping, send)
|
local add_mapping = function(mapping, send)
|
||||||
local cmd = "nnoremap "..mapping.." :call rpcnotify("..cid..", 'mapped', '"
|
local cmd = "nnoremap "..mapping.." :call rpcnotify(1, 'mapped', '"
|
||||||
..send:gsub('<', '<lt>').."')<cr>"
|
..send:gsub('<', '<lt>').."')<cr>"
|
||||||
feed_command(cmd)
|
feed_command(cmd)
|
||||||
end
|
end
|
||||||
@@ -21,8 +23,6 @@ describe('mappings', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
|
||||||
cid = nvim('get_api_info')[1]
|
|
||||||
add_mapping('<C-L>', '<C-L>')
|
add_mapping('<C-L>', '<C-L>')
|
||||||
add_mapping('<C-S-L>', '<C-S-L>')
|
add_mapping('<C-S-L>', '<C-S-L>')
|
||||||
add_mapping('<s-up>', '<s-up>')
|
add_mapping('<s-up>', '<s-up>')
|
||||||
@@ -115,7 +115,6 @@ describe('mappings', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('input utf sequences that contain CSI/K_SPECIAL', function()
|
describe('input utf sequences that contain CSI/K_SPECIAL', function()
|
||||||
before_each(clear)
|
|
||||||
it('ok', function()
|
it('ok', function()
|
||||||
feed('i…<esc>')
|
feed('i…<esc>')
|
||||||
expect('…')
|
expect('…')
|
||||||
@@ -129,7 +128,6 @@ describe('input non-printable chars', function()
|
|||||||
|
|
||||||
it("doesn't crash when echoing them back", function()
|
it("doesn't crash when echoing them back", function()
|
||||||
write_file("Xtest-overwrite", [[foobar]])
|
write_file("Xtest-overwrite", [[foobar]])
|
||||||
clear()
|
|
||||||
local screen = Screen.new(60,8)
|
local screen = Screen.new(60,8)
|
||||||
screen:set_default_attr_ids({
|
screen:set_default_attr_ids({
|
||||||
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||||
@@ -215,3 +213,27 @@ describe('input non-printable chars', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("event processing and input", function()
|
||||||
|
it('not blocked by event bursts', function()
|
||||||
|
meths.set_keymap('', '<f2>', "<cmd>lua vim.rpcnotify(1, 'stop') winning = true <cr>", {noremap=true})
|
||||||
|
|
||||||
|
exec_lua [[
|
||||||
|
winning = false
|
||||||
|
burst = vim.schedule_wrap(function(tell)
|
||||||
|
if tell then
|
||||||
|
vim.rpcnotify(1, 'start')
|
||||||
|
end
|
||||||
|
-- Are we winning, son?
|
||||||
|
if not winning then
|
||||||
|
burst(false)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
burst(true)
|
||||||
|
]]
|
||||||
|
|
||||||
|
eq({'notification', 'start', {}}, next_msg())
|
||||||
|
feed '<f2>'
|
||||||
|
eq({'notification', 'stop', {}}, next_msg())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user