diff --git a/src/nvim/normal.c b/src/nvim/normal.c index eb36b6f60b..b397a7eb92 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -82,6 +82,7 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/terminal.h" #include "nvim/textformat.h" #include "nvim/textobject.h" #include "nvim/types_defs.h" @@ -1445,6 +1446,8 @@ static int normal_check(VimState *state) skip_redraw = false; setcursor(); } else if (do_redraw || stuff_empty()) { + terminal_check_refresh(); + // Ensure curwin->w_topline and curwin->w_leftcol are up to date // before triggering a WinScrolled autocommand. update_topline(curwin); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index e305c2a566..d386b6dda3 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -746,7 +746,7 @@ void terminal_set_state(Terminal *term, bool suspended) { if (term->suspended != suspended) { // Trigger a main loop iteration to redraw the buffer. - multiqueue_put(main_loop.events, terminal_state_change_event, + multiqueue_put(refresh_timer.events, terminal_state_change_event, (void *)(intptr_t)term->buf_handle); } term->suspended = suspended; @@ -1033,6 +1033,8 @@ static int terminal_check(VimState *state) return 0; } + terminal_check_refresh(); + // Validate topline and cursor position for autocommands. Especially important for WinScrolled. terminal_check_cursor(); validate_cursor(curwin); @@ -2283,6 +2285,14 @@ static void invalidate_terminal(Terminal *term, int start_row, int end_row) } } +/// Normally refresh_timer_cb() is called when processing main_loop.events, but with +/// partial mappings main_loop.events isn't processed, while terminal buffers still +/// need refreshing after processing a key, so call this function before redrawing. +void terminal_check_refresh(void) +{ + multiqueue_process_events(refresh_timer.events); +} + static void refresh_terminal(Terminal *term) { buf_T *buf = handle_get_buffer(term->buf_handle); diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index c41cb665ec..56cdaca768 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -296,6 +296,64 @@ describe(':terminal buffer', function() eq('t', fn.mode(1)) end) + it('is refreshed with partial mappings in Terminal mode #9167', function() + command([[set timeoutlen=20000 | tnoremap jk ]]) + feed('j') -- Won't reach the terminal until the next character is typed + screen:expect_unchanged() + feed('j') -- Refresh scheduled for the first 'j' but not processed + screen:expect_unchanged() + for i = 1, 10 do + eq({ mode = 't', blocking = true }, api.nvim_get_mode()) + vim.uv.sleep(10) -- Wait for the previously scheduled refresh timer to arrive + feed('j') -- Refresh scheduled for the last 'j' and processed for the one before + screen:expect(([[ + tty ready | + %s^%s| + |*4 + {5:-- TERMINAL --} | + ]]):format(('j'):rep(i), (' '):rep(50 - i))) + end + feed('l') -- No partial mapping, so all pending refreshes should be processed + screen:expect([[ + tty ready | + jjjjjjjjjjjjl^ | + |*4 + {5:-- TERMINAL --} | + ]]) + end) + + it('is refreshed with partial mappings in Normal mode', function() + command('set timeoutlen=20000 | nnoremap jk :') + command('nnoremap j call chansend(&channel, "j")') + feed([[]]) + screen:expect([[ + tty ready | + ^ | + |*5 + ]]) + feed('j') -- Won't reach the terminal until the next character is typed + screen:expect_unchanged() + feed('j') -- Refresh scheduled for the first 'j' but not processed + screen:expect_unchanged() + for i = 1, 10 do + eq({ mode = 'nt', blocking = true }, api.nvim_get_mode()) + vim.uv.sleep(10) -- Wait for the previously scheduled refresh timer to arrive + feed('j') -- Refresh scheduled for the last 'j' and processed for the one before + screen:expect(([[ + tty ready | + ^%s%s| + |*4 + | + ]]):format(('j'):rep(i), (' '):rep(50 - i))) + end + feed('l') -- No partial mapping, so all pending refreshes should be processed + screen:expect([[ + tty ready | + j^jjjjjjjjjjj | + |*5 + ]]) + end) + it('writing to an existing file with :w fails #13549', function() eq( 'Vim(write):E13: File exists (add ! to override)',