diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-02-06 19:22:02 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-06 19:22:02 +0100 |
commit | f0699f43566cd3e5f7fbc4d1e2802c610b5bfa5c (patch) | |
tree | 4a418cd1bbd545e1a106fb122ea2d8c050da2fbe | |
parent | c7df847c07c3b3cc6c47e3f33be9b66eb69ce448 (diff) | |
parent | 74998b0449c4df0494c3bfe5d4034c575d972406 (diff) | |
download | rneovim-f0699f43566cd3e5f7fbc4d1e2802c610b5bfa5c.tar.gz rneovim-f0699f43566cd3e5f7fbc4d1e2802c610b5bfa5c.tar.bz2 rneovim-f0699f43566cd3e5f7fbc4d1e2802c610b5bfa5c.zip |
Merge pull request #17279 from zeertzjq/state-enter-vpeekc
fix(event-loop): call vpeekc() directly first to check for character
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/state.c | 22 | ||||
-rw-r--r-- | test/functional/ui/input_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/vimscript/timer_spec.lua | 8 |
4 files changed, 43 insertions, 8 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index edf6ed3c12..aa60bc6b22 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3189,7 +3189,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type == VAR_UNKNOWN) { // getchar(): blocking wait. // TODO(bfredl): deduplicate shared logic with state_enter ? - if (!(char_avail() || using_script() || input_available())) { + if (!char_avail()) { (void)os_inchar(NULL, 0, -1, 0, main_loop.events); if (!multiqueue_empty(main_loop.events)) { state_handle_k_event(); diff --git a/src/nvim/state.c b/src/nvim/state.c index 9e4c9b2bad..f9a3aaab7f 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -39,10 +39,16 @@ void state_enter(VimState *s) int key; getkey: - if (char_avail() || using_script() || input_available()) { - // Don't block for events if there's a character already available for - // processing. Characters can come from mappings, scripts and other - // sources, so this scenario is very common. + // Expand mappings first by calling vpeekc() directly. + // - If vpeekc() returns non-NUL, there is a character already available for processing, so + // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence. + // - If vpeekc() returns NUL, vgetc() will block, and there are three cases: + // - There is no input available. + // - All of available input maps to an empty string. + // - There is an incomplete mapping. + // A blocking wait for a character should only be done in the third case, which is the only + // case of the three where typebuf.tb_len > 0 after vpeekc() returns NUL. + if (vpeekc() != NUL || typebuf.tb_len > 0) { key = safe_vgetc(); } else if (!multiqueue_empty(main_loop.events)) { // Event was made available after the last multiqueue_process_events call @@ -55,9 +61,11 @@ getkey: // mapping engine. (void)os_inchar(NULL, 0, -1, 0, main_loop.events); // If an event was put into the queue, we send K_EVENT directly. - key = !multiqueue_empty(main_loop.events) - ? K_EVENT - : safe_vgetc(); + if (!multiqueue_empty(main_loop.events)) { + key = K_EVENT; + } else { + goto getkey; + } } if (key == K_EVENT) { diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 78f4c6ddd3..7000505909 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -140,6 +140,25 @@ describe('input utf sequences that contain CSI (0x9B)', function() end) end) +describe('input split utf sequences', function() + it('ok', function() + local str = '►' + feed('i' .. str:sub(1, 1)) + helpers.sleep(10) + feed(str:sub(2, 3)) + expect('►') + end) + + it('can be mapped', function() + command('inoremap ► E296BA') + local str = '►' + feed('i' .. str:sub(1, 1)) + helpers.sleep(10) + feed(str:sub(2, 3)) + expect('E296BA') + end) +end) + describe('input non-printable chars', function() after_each(function() os.remove('Xtest-overwrite') diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index e45b64422f..25e46062b7 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -272,4 +272,12 @@ describe('timers', function() ]] eq("Vim(call):E48: Not allowed in sandbox", exc_exec("sandbox call timer_start(0, 'Scary')")) end) + + it('can be triggered after an empty string <expr> mapping', function() + local screen = Screen.new(40, 6) + screen:attach() + command([=[imap <expr> <F2> [timer_start(0, { _ -> execute("throw 'x'", "") }), ''][-1]]=]) + feed('i<F2>') + screen:expect({any='E605: Exception not caught: x'}) + end) end) |