mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 21:02:11 +00:00
fix(terminal): missing refresh with partial mappings (#37839)
Problem: Terminal buffers are not refreshed when processing keys that
trigger partial mappings.
Solution: Process due terminal refreshes before redrawing.
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <C-\><C-N>]])
|
||||
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 <Cmd>call chansend(&channel, "j")<CR>')
|
||||
feed([[<C-\><C-N>]])
|
||||
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)',
|
||||
|
||||
Reference in New Issue
Block a user