diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 36d368a913..2d8ad458f1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2911,6 +2911,9 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool error = false; const int called_emsg_before = called_emsg; + // Flush screen updates before blocking. + ui_flush(); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout, eval_expr_typval(&expr, false, &argv, 0, &exprval) != OK || tv_get_number_chk(&exprval, &error) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 8b1e2f9105..2370df916c 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1723,7 +1723,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) // getchar(): blocking wait. // TODO(bfredl): deduplicate shared logic with state_enter ? if (!char_avail()) { - // flush output before waiting + // Flush screen updates before blocking. ui_flush(); (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); if (!multiqueue_empty(main_loop.events)) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3333c73bfd..876a5c34bd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -463,7 +463,7 @@ static int nlua_wait(lua_State *lstate) int pcall_status = 0; bool callback_result = false; - // Flush UI before blocking + // Flush screen updates before blocking. ui_flush(); LOOP_PROCESS_EVENTS_UNTIL(&main_loop, diff --git a/src/nvim/state.c b/src/nvim/state.c index 61e8d8d2ff..d2466e4248 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -70,7 +70,7 @@ getkey: update_screen(); setcursor(); // put cursor back where it belongs } - // Flush screen updates before blocking + // Flush screen updates before blocking. ui_flush(); // Call `os_inchar` directly to block for events or user input without // consuming anything from `input_buffer`(os/input.c) or calling the diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index befbd4bc3b..038368c387 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -875,25 +875,41 @@ describe('jobs', function() end) end) - it('hides cursor when waiting', function() - local screen = Screen.new(30, 3) + it('hides cursor and flushes messages before blocking', function() + local screen = Screen.new(50, 6) screen:set_default_attr_ids({ - [0] = {foreground = Screen.colors.Blue1, bold = true}; + [0] = {foreground = Screen.colors.Blue, bold = true}; -- NonText + [1] = {bold = true, reverse = true}; -- MsgSeparator + [2] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg }) screen:attach() command([[let g:id = jobstart([v:progpath, '--clean', '--headless'])]]) - feed_command('call jobwait([g:id], 300)') + source([[ + func PrintAndWait() + echon "aaa\nbbb" + call jobwait([g:id], 300) + echon "\nccc" + endfunc + ]]) + feed_command('call PrintAndWait()') screen:expect{grid=[[ - | - {0:~ }| - :call jobwait([g:id], 300) | + | + {0:~ }| + {0:~ }| + {1: }| + aaa | + bbb | ]], timeout=100} - funcs.jobstop(meths.get_var('id')) screen:expect{grid=[[ - ^ | - {0:~ }| - :call jobwait([g:id], 300) | + | + {1: }| + aaa | + bbb | + ccc | + {2:Press ENTER or type command to continue}^ | ]]} + feed('') + funcs.jobstop(meths.get_var('id')) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 1ebfa9dd1d..a8a72f20c9 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2559,7 +2559,6 @@ describe('lua stdlib', function() ]]) end) - it('should not block other events', function() eq({time = true, wait_result = true}, exec_lua[[ start_time = get_time() @@ -2601,6 +2600,7 @@ describe('lua stdlib', function() } ]]) end) + it('should work with vim.defer_fn', function() eq({time = true, wait_result = true}, exec_lua[[ start_time = get_time() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index a5b2474bc5..215e763fd1 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1312,17 +1312,54 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim ]]) end) - it('echo messages are shown correctly when getchar() immediately follows', function() - feed([[:echo 'foo' | echo 'bar' | call getchar()]]) - screen:expect([[ - | - {1:~ }| - {1:~ }| - {1:~ }| - {3: }| - foo | - bar^ | - ]]) + describe('echo messages are shown when immediately followed by', function() + --- @param to_block string command to cause a blocking wait + --- @param to_unblock number|string number: timeout for blocking screen + --- string: keys to stop the blocking wait + local function test_flush_before_block(to_block, to_unblock) + local timeout = type(to_unblock) == 'number' and to_unblock or nil + exec(([[ + func PrintAndWait() + echon "aaa\nbbb" + %s + echon "\nccc" + endfunc + ]]):format(to_block)) + feed(':call PrintAndWait()') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + aaa | + bbb^ | + ]], timeout=timeout} + if type(to_unblock) == 'string' then + feed(to_unblock) + end + screen:expect{grid=[[ + | + {1:~ }| + {3: }| + aaa | + bbb | + ccc | + {4:Press ENTER or type command to continue}^ | + ]]} + end + + it('getchar()', function() + test_flush_before_block([[call getchar()]], 'k') + end) + + it('wait()', function() + test_flush_before_block([[call wait(300, '0')]], 100) + end) + + it('lua vim.wait()', function() + test_flush_before_block([[lua vim.wait(300, function() end)]], 100) + end) end) it('consecutive calls to win_move_statusline() work after multiline message #21014',function()