diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2019-08-31 11:18:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-31 11:18:35 +0200 |
commit | 25e0a449bb6619fc534fa862c2f591657be1e1d5 (patch) | |
tree | bf3d8beff647c8e1e5b39c746088843d629338ef | |
parent | 8a03acb8dad4abaf507d502b11a66bd5a2b5e51e (diff) | |
parent | 2c605d1f22a243bc34b680f69c7c8cfe01b80887 (diff) | |
download | rneovim-25e0a449bb6619fc534fa862c2f591657be1e1d5.tar.gz rneovim-25e0a449bb6619fc534fa862c2f591657be1e1d5.tar.bz2 rneovim-25e0a449bb6619fc534fa862c2f591657be1e1d5.zip |
Merge pull request #10878 from bfredl/pastedefer
TUI: defer nvim_paste event properly
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/event/loop.c | 12 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 4 | ||||
-rw-r--r-- | src/nvim/message.c | 2 | ||||
-rw-r--r-- | src/nvim/ops.c | 22 | ||||
-rw-r--r-- | src/nvim/tui/input.c | 35 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 2 | ||||
-rw-r--r-- | src/nvim/ui.c | 6 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 29 | ||||
-rw-r--r-- | test/functional/terminal/tui_spec.lua | 26 |
10 files changed, 108 insertions, 32 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a39ee5d038..21cba96ba7 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1283,8 +1283,6 @@ theend: api_free_array(args); if (cancel || phase == -1 || phase == 3) { // End of paste-stream. draining = false; - // XXX: Tickle main loop to ensure cursor is updated. - loop_schedule_deferred(&main_loop, event_create(loop_dummy_event, 0)); } return !cancel; diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 529ddd8eba..a01bbc9888 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -71,7 +71,7 @@ bool loop_poll_events(Loop *loop, int ms) 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 /// 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 /// skipped. /// @see loop_schedule_deferred -void loop_schedule(Loop *loop, Event event) +void loop_schedule_fast(Loop *loop, Event event) { uv_mutex_lock(&loop->mutex); multiqueue_put_event(loop->thread_events, event); @@ -87,15 +87,15 @@ void loop_schedule(Loop *loop, Event event) uv_mutex_unlock(&loop->mutex); } -/// Schedules an event from another thread. Unlike loop_schedule(), the event -/// is forwarded to `Loop.events`, instead of being processed immediately. +/// Schedules an event from another thread. Unlike loop_schedule_fast(), the +/// 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) { Event *eventp = xmalloc(sizeof(*eventp)); *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) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3de1b531e6..f51aa3c6d4 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -269,7 +269,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL #endif // 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")); return 1; } diff --git a/src/nvim/message.c b/src/nvim/message.c index 9bea9f5c4a..c8deaa590c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -753,7 +753,7 @@ void msg_schedule_emsgf(const char *const fmt, ...) va_end(ap); 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); } /* diff --git a/src/nvim/ops.c b/src/nvim/ops.c index df4452cd4a..bfd02d4ff1 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5685,9 +5685,6 @@ end: /// @param[out] reg Expected to be empty 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; switch (type) { @@ -5707,6 +5704,23 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines) 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_size = lines; reg->additional_data = NULL; @@ -5743,7 +5757,7 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust) } } assert(maxlen <= INT_MAX); - reg->y_width = (int)maxlen - 1; + reg->y_width = MAX(reg->y_width, (int)maxlen - 1); } } diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 1f67e6ce13..ed9b410a19 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -106,17 +106,15 @@ static void tinput_wait_enqueue(void **argv) RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { const String keys = { .data = buf, .size = len }; if (input->paste) { - Error err = ERROR_INIT; - // Paste phase: "continue" (unless handler canceled). - input->paste = !nvim_paste(keys, true, input->paste, &err) - ? 0 : (1 == input->paste ? 2 : input->paste); + String copy = copy_string(keys); + multiqueue_put(main_loop.events, tinput_paste_event, 3, + copy.data, copy.size, (intptr_t)input->paste); + if (input->paste == 1) { + // Paste phase: "continue" + input->paste = 2; + } rbuffer_consumed(input->key_buffer, len); 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 { const size_t consumed = input_enqueue(keys); if (consumed) { @@ -134,12 +132,27 @@ static void tinput_wait_enqueue(void **argv) 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) { size_t drain_boundary = wait_until_empty ? 0 : 0xff; do { 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; while (input->waiting) { 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; if (eof) { - loop_schedule(&main_loop, event_create(tinput_done_event, 0)); + loop_schedule_fast(&main_loop, event_create(tinput_done_event, 0)); return; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index ea8f9d9f71..6e1b7ef2db 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -454,7 +454,7 @@ static void tui_scheduler(Event event, void *d) { UI *ui = d; 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 diff --git a/src/nvim/ui.c b/src/nvim/ui.c index fc4a3a403d..5d191314ba 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -228,7 +228,11 @@ static void ui_refresh_event(void **argv) 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) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 6f7661dd76..02e83c4d58 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -518,6 +518,35 @@ describe('API', function() line 3]]) eq({0,3,6,0}, funcs.getpos('.')) 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) describe('nvim_strwidth', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 8d8c0e3647..07c91d43e1 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -352,15 +352,23 @@ describe('TUI', function() ]]} -- Start pasting... feed_data('\027[200~line 1\nline 2\n') - expect_child_buf_lines({'foo',''}) - screen:expect{any='paste: Error executing lua'} + screen:expect{grid=[[ + 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. feed_data('line 3\nline 4\n') feed_data('line 5\nline 6\n') feed_data('line 7\nline 8\n') -- Stop paste. 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. feed_data('.') screen:expect{grid=[[ @@ -388,7 +396,7 @@ describe('TUI', function() vim.paste = _G.save_paste_fn ]], {}) feed_data('\027[200~line A\nline B\n\027[201~') - feed_data('\n') -- <Enter> + feed_data('\n') -- <CR> screen:expect{grid=[[ foo | typed input...line A | @@ -414,7 +422,15 @@ describe('TUI', function() it("paste: 'nomodifiable' buffer", function() child_session:request('nvim_command', 'set nomodifiable') 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> child_session:request('nvim_command', 'set modifiable') feed_data('\027[200~success 1\nsuccess 2\n\027[201~') |