mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00
Merge pull request #10878 from bfredl/pastedefer
TUI: defer nvim_paste event properly
This commit is contained in:
@@ -1283,8 +1283,6 @@ theend:
|
|||||||
api_free_array(args);
|
api_free_array(args);
|
||||||
if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
|
if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
|
||||||
draining = false;
|
draining = false;
|
||||||
// XXX: Tickle main loop to ensure cursor is updated.
|
|
||||||
loop_schedule_deferred(&main_loop, event_create(loop_dummy_event, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !cancel;
|
return !cancel;
|
||||||
|
@@ -71,7 +71,7 @@ bool loop_poll_events(Loop *loop, int ms)
|
|||||||
return timeout_expired;
|
return timeout_expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedules an event from another thread.
|
/// Schedules a fast event from another thread.
|
||||||
///
|
///
|
||||||
/// @note Event is queued into `fast_events`, which is processed outside of the
|
/// @note Event is queued into `fast_events`, which is processed outside of the
|
||||||
/// primary `events` queue by loop_poll_events(). For `main_loop`, that
|
/// primary `events` queue by loop_poll_events(). For `main_loop`, that
|
||||||
@@ -79,7 +79,7 @@ bool loop_poll_events(Loop *loop, int ms)
|
|||||||
/// (VimState.execute), so redraw and other side-effects are likely to be
|
/// (VimState.execute), so redraw and other side-effects are likely to be
|
||||||
/// skipped.
|
/// skipped.
|
||||||
/// @see loop_schedule_deferred
|
/// @see loop_schedule_deferred
|
||||||
void loop_schedule(Loop *loop, Event event)
|
void loop_schedule_fast(Loop *loop, Event event)
|
||||||
{
|
{
|
||||||
uv_mutex_lock(&loop->mutex);
|
uv_mutex_lock(&loop->mutex);
|
||||||
multiqueue_put_event(loop->thread_events, event);
|
multiqueue_put_event(loop->thread_events, event);
|
||||||
@@ -87,15 +87,15 @@ void loop_schedule(Loop *loop, Event event)
|
|||||||
uv_mutex_unlock(&loop->mutex);
|
uv_mutex_unlock(&loop->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedules an event from another thread. Unlike loop_schedule(), the event
|
/// Schedules an event from another thread. Unlike loop_schedule_fast(), the
|
||||||
/// is forwarded to `Loop.events`, instead of being processed immediately.
|
/// event is forwarded to `Loop.events`, instead of being processed immediately.
|
||||||
///
|
///
|
||||||
/// @see loop_schedule
|
/// @see loop_schedule_fast
|
||||||
void loop_schedule_deferred(Loop *loop, Event event)
|
void loop_schedule_deferred(Loop *loop, Event event)
|
||||||
{
|
{
|
||||||
Event *eventp = xmalloc(sizeof(*eventp));
|
Event *eventp = xmalloc(sizeof(*eventp));
|
||||||
*eventp = event;
|
*eventp = event;
|
||||||
loop_schedule(loop, event_create(loop_deferred_event, 2, loop, eventp));
|
loop_schedule_fast(loop, event_create(loop_deferred_event, 2, loop, eventp));
|
||||||
}
|
}
|
||||||
static void loop_deferred_event(void **argv)
|
static void loop_deferred_event(void **argv)
|
||||||
{
|
{
|
||||||
|
@@ -269,7 +269,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// vim
|
// vim
|
||||||
if (luaL_dostring(lstate, (char *)&vim_module[0])) {
|
const char *code = (char *)&vim_module[0];
|
||||||
|
if (luaL_loadbuffer(lstate, code, strlen(code), "@vim.lua")
|
||||||
|
|| lua_pcall(lstate, 0, LUA_MULTRET, 0)) {
|
||||||
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
|
nlua_error(lstate, _("E5106: Error while creating vim module: %.*s"));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@@ -753,7 +753,7 @@ void msg_schedule_emsgf(const char *const fmt, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
char *s = xstrdup((char *)IObuff);
|
char *s = xstrdup((char *)IObuff);
|
||||||
loop_schedule(&main_loop, event_create(msg_emsgf_event, 1, s));
|
multiqueue_put(main_loop.events, msg_emsgf_event, 1, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -5685,9 +5685,6 @@ end:
|
|||||||
/// @param[out] reg Expected to be empty
|
/// @param[out] reg Expected to be empty
|
||||||
bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
|
bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
|
||||||
{
|
{
|
||||||
if (regtype.size > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char type = regtype.data ? regtype.data[0] : NUL;
|
char type = regtype.data ? regtype.data[0] : NUL;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -5707,6 +5704,23 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reg->y_width = 0;
|
||||||
|
if (regtype.size > 1) {
|
||||||
|
if (reg->y_type != kMTBlockWise) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow "b7" for a block at least 7 spaces wide
|
||||||
|
if (!ascii_isdigit(regtype.data[1])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char *p = regtype.data+1;
|
||||||
|
reg->y_width = getdigits_int((char_u **)&p)-1;
|
||||||
|
if (regtype.size > (size_t)(p-regtype.data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reg->y_array = xcalloc(lines, sizeof(uint8_t *));
|
reg->y_array = xcalloc(lines, sizeof(uint8_t *));
|
||||||
reg->y_size = lines;
|
reg->y_size = lines;
|
||||||
reg->additional_data = NULL;
|
reg->additional_data = NULL;
|
||||||
@@ -5743,7 +5757,7 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(maxlen <= INT_MAX);
|
assert(maxlen <= INT_MAX);
|
||||||
reg->y_width = (int)maxlen - 1;
|
reg->y_width = MAX(reg->y_width, (int)maxlen - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -106,17 +106,15 @@ static void tinput_wait_enqueue(void **argv)
|
|||||||
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
|
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
|
||||||
const String keys = { .data = buf, .size = len };
|
const String keys = { .data = buf, .size = len };
|
||||||
if (input->paste) {
|
if (input->paste) {
|
||||||
Error err = ERROR_INIT;
|
String copy = copy_string(keys);
|
||||||
// Paste phase: "continue" (unless handler canceled).
|
multiqueue_put(main_loop.events, tinput_paste_event, 3,
|
||||||
input->paste = !nvim_paste(keys, true, input->paste, &err)
|
copy.data, copy.size, (intptr_t)input->paste);
|
||||||
? 0 : (1 == input->paste ? 2 : input->paste);
|
if (input->paste == 1) {
|
||||||
|
// Paste phase: "continue"
|
||||||
|
input->paste = 2;
|
||||||
|
}
|
||||||
rbuffer_consumed(input->key_buffer, len);
|
rbuffer_consumed(input->key_buffer, len);
|
||||||
rbuffer_reset(input->key_buffer);
|
rbuffer_reset(input->key_buffer);
|
||||||
if (ERROR_SET(&err)) {
|
|
||||||
// TODO(justinmk): emsgf() does not display, why?
|
|
||||||
msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, "paste: %s", err.msg);
|
|
||||||
api_clear_error(&err);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const size_t consumed = input_enqueue(keys);
|
const size_t consumed = input_enqueue(keys);
|
||||||
if (consumed) {
|
if (consumed) {
|
||||||
@@ -134,12 +132,27 @@ static void tinput_wait_enqueue(void **argv)
|
|||||||
uv_mutex_unlock(&input->key_buffer_mutex);
|
uv_mutex_unlock(&input->key_buffer_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tinput_paste_event(void **argv)
|
||||||
|
{
|
||||||
|
String keys = { .data = argv[0], .size = (size_t)argv[1] };
|
||||||
|
intptr_t phase = (intptr_t)argv[2];
|
||||||
|
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
nvim_paste(keys, true, phase, &err);
|
||||||
|
if (ERROR_SET(&err)) {
|
||||||
|
emsgf("paste: %s", err.msg);
|
||||||
|
api_clear_error(&err);
|
||||||
|
}
|
||||||
|
|
||||||
|
api_free_string(keys);
|
||||||
|
}
|
||||||
|
|
||||||
static void tinput_flush(TermInput *input, bool wait_until_empty)
|
static void tinput_flush(TermInput *input, bool wait_until_empty)
|
||||||
{
|
{
|
||||||
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
||||||
do {
|
do {
|
||||||
uv_mutex_lock(&input->key_buffer_mutex);
|
uv_mutex_lock(&input->key_buffer_mutex);
|
||||||
loop_schedule(&main_loop, event_create(tinput_wait_enqueue, 1, input));
|
loop_schedule_fast(&main_loop, event_create(tinput_wait_enqueue, 1, input));
|
||||||
input->waiting = true;
|
input->waiting = true;
|
||||||
while (input->waiting) {
|
while (input->waiting) {
|
||||||
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
||||||
@@ -497,7 +510,7 @@ static void tinput_read_cb(Stream *stream, RBuffer *buf, size_t count_,
|
|||||||
TermInput *input = data;
|
TermInput *input = data;
|
||||||
|
|
||||||
if (eof) {
|
if (eof) {
|
||||||
loop_schedule(&main_loop, event_create(tinput_done_event, 0));
|
loop_schedule_fast(&main_loop, event_create(tinput_done_event, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -454,7 +454,7 @@ static void tui_scheduler(Event event, void *d)
|
|||||||
{
|
{
|
||||||
UI *ui = d;
|
UI *ui = d;
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
loop_schedule(data->loop, event); // `tui_loop` local to tui_main().
|
loop_schedule_fast(data->loop, event); // `tui_loop` local to tui_main().
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
|
@@ -228,7 +228,11 @@ static void ui_refresh_event(void **argv)
|
|||||||
|
|
||||||
void ui_schedule_refresh(void)
|
void ui_schedule_refresh(void)
|
||||||
{
|
{
|
||||||
loop_schedule(&main_loop, event_create(ui_refresh_event, 0));
|
// TODO(bfredl): "fast" is not optimal. UI should be refreshed only at
|
||||||
|
// deferred processing plus a few more blocked-on-input situtions like
|
||||||
|
// wait_return(), but not any os_breakcheck(). Alternatively make this
|
||||||
|
// defered and make wait_return() process deferred events already.
|
||||||
|
loop_schedule_fast(&main_loop, event_create(ui_refresh_event, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_default_colors_set(void)
|
void ui_default_colors_set(void)
|
||||||
|
@@ -518,6 +518,35 @@ describe('API', function()
|
|||||||
line 3]])
|
line 3]])
|
||||||
eq({0,3,6,0}, funcs.getpos('.'))
|
eq({0,3,6,0}, funcs.getpos('.'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('allows block width', function()
|
||||||
|
-- behave consistently with setreg(); support "\022{NUM}" return by getregtype()
|
||||||
|
meths.put({'line 1','line 2','line 3'}, 'l', false, false)
|
||||||
|
expect([[
|
||||||
|
line 1
|
||||||
|
line 2
|
||||||
|
line 3
|
||||||
|
]])
|
||||||
|
|
||||||
|
-- larger width create spaces
|
||||||
|
meths.put({'a', 'bc'}, 'b3', false, false)
|
||||||
|
expect([[
|
||||||
|
a line 1
|
||||||
|
bc line 2
|
||||||
|
line 3
|
||||||
|
]])
|
||||||
|
-- smaller width is ignored
|
||||||
|
meths.put({'xxx', 'yyy'}, '\0221', false, true)
|
||||||
|
expect([[
|
||||||
|
xxxa line 1
|
||||||
|
yyybc line 2
|
||||||
|
line 3
|
||||||
|
]])
|
||||||
|
eq({false, "Invalid type: 'bx'"},
|
||||||
|
meth_pcall(meths.put, {'xxx', 'yyy'}, 'bx', false, true))
|
||||||
|
eq({false, "Invalid type: 'b3x'"},
|
||||||
|
meth_pcall(meths.put, {'xxx', 'yyy'}, 'b3x', false, true))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('nvim_strwidth', function()
|
describe('nvim_strwidth', function()
|
||||||
|
@@ -352,15 +352,23 @@ describe('TUI', function()
|
|||||||
]]}
|
]]}
|
||||||
-- Start pasting...
|
-- Start pasting...
|
||||||
feed_data('\027[200~line 1\nline 2\n')
|
feed_data('\027[200~line 1\nline 2\n')
|
||||||
expect_child_buf_lines({'foo',''})
|
screen:expect{grid=[[
|
||||||
screen:expect{any='paste: Error executing lua'}
|
foo |
|
||||||
|
|
|
||||||
|
{5: }|
|
||||||
|
{8:paste: Error executing lua: [string "<nvim>"]:2: f}|
|
||||||
|
{8:ake fail} |
|
||||||
|
{10:Press ENTER or type command to continue}{1: } |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
-- Remaining chunks are discarded after vim.paste() failure.
|
-- Remaining chunks are discarded after vim.paste() failure.
|
||||||
feed_data('line 3\nline 4\n')
|
feed_data('line 3\nline 4\n')
|
||||||
feed_data('line 5\nline 6\n')
|
feed_data('line 5\nline 6\n')
|
||||||
feed_data('line 7\nline 8\n')
|
feed_data('line 7\nline 8\n')
|
||||||
-- Stop paste.
|
-- Stop paste.
|
||||||
feed_data('\027[201~')
|
feed_data('\027[201~')
|
||||||
feed_data('\n') -- <Enter>
|
feed_data('\n') -- <CR>
|
||||||
|
expect_child_buf_lines({'foo',''})
|
||||||
--Dot-repeat/redo is not modified by failed paste.
|
--Dot-repeat/redo is not modified by failed paste.
|
||||||
feed_data('.')
|
feed_data('.')
|
||||||
screen:expect{grid=[[
|
screen:expect{grid=[[
|
||||||
@@ -388,7 +396,7 @@ describe('TUI', function()
|
|||||||
vim.paste = _G.save_paste_fn
|
vim.paste = _G.save_paste_fn
|
||||||
]], {})
|
]], {})
|
||||||
feed_data('\027[200~line A\nline B\n\027[201~')
|
feed_data('\027[200~line A\nline B\n\027[201~')
|
||||||
feed_data('\n') -- <Enter>
|
feed_data('\n') -- <CR>
|
||||||
screen:expect{grid=[[
|
screen:expect{grid=[[
|
||||||
foo |
|
foo |
|
||||||
typed input...line A |
|
typed input...line A |
|
||||||
@@ -414,7 +422,15 @@ describe('TUI', function()
|
|||||||
it("paste: 'nomodifiable' buffer", function()
|
it("paste: 'nomodifiable' buffer", function()
|
||||||
child_session:request('nvim_command', 'set nomodifiable')
|
child_session:request('nvim_command', 'set nomodifiable')
|
||||||
feed_data('\027[200~fail 1\nfail 2\n\027[201~')
|
feed_data('\027[200~fail 1\nfail 2\n\027[201~')
|
||||||
screen:expect{any='Vim:E21'}
|
screen:expect{grid=[[
|
||||||
|
|
|
||||||
|
{4:~ }|
|
||||||
|
{5: }|
|
||||||
|
{8:paste: Error executing lua: vim.lua:194: Vim:E21: }|
|
||||||
|
{8:Cannot make changes, 'modifiable' is off} |
|
||||||
|
{10:Press ENTER or type command to continue}{1: } |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
feed_data('\n') -- <Enter>
|
feed_data('\n') -- <Enter>
|
||||||
child_session:request('nvim_command', 'set modifiable')
|
child_session:request('nvim_command', 'set modifiable')
|
||||||
feed_data('\027[200~success 1\nsuccess 2\n\027[201~')
|
feed_data('\027[200~success 1\nsuccess 2\n\027[201~')
|
||||||
|
Reference in New Issue
Block a user