From 61fb88992d34d16b3058dcd8b1664f575449d964 Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Wed, 22 Apr 2026 15:04:20 +0200 Subject: [PATCH] fix(cmdline): avoid 'incsearch' recursion after redraw #39303 Problem: A vim.ui_attach() callback that redraws to show a 'verbose' regex message during 'incsearch' results in recusive redrawing. Solution: Check that curwin was redrawn instead of just any window when determining if 'incsearch' highlighting was cleared. --- src/nvim/buffer_defs.h | 1 + src/nvim/drawscreen.c | 2 ++ src/nvim/ex_getln.c | 4 ++-- test/functional/ui/cmdline2_spec.lua | 12 ++++++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 72e67d4fd6..90845531f4 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1294,6 +1294,7 @@ struct window_S { bool w_redr_status; // if true statusline/winbar must be redrawn bool w_redr_border; // if true border must be redrawn bool w_redr_statuscol; // if true 'statuscolumn' must be redrawn + disptick_T w_display_tick; // when window was last drawn. // remember what is shown in the 'statusline'-format elements pos_T w_stl_cursor; // cursor position when last redrawn diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 35f4691726..a7fa6bf688 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -2321,6 +2321,8 @@ redr_statuscol: wp->w_lines_valid = MAX(wp->w_lines_valid, idx); + wp->w_display_tick = display_tick; + // Let the syntax stuff know we stop parsing here. if (syntax_last_parsed != 0 && syntax_present(wp)) { syntax_end_parsing(wp, syntax_last_parsed + 1); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6cc697cc6e..5f384c6ee4 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1291,7 +1291,7 @@ static int command_line_execute(VimState *state, int key) return -1; // get another key } - disptick_T display_tick_saved = display_tick; + disptick_T display_tick_saved = curwin->w_display_tick; CommandLineState *s = (CommandLineState *)state; s->c = key; @@ -1322,7 +1322,7 @@ static int command_line_execute(VimState *state, int key) init_incsearch_state(&s->is_state); } // Re-apply 'incsearch' highlighting in case it was cleared. - if (display_tick > display_tick_saved && s->is_state.did_incsearch) { + if (curwin->w_display_tick > display_tick_saved && s->is_state.did_incsearch) { may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); } // If f_setcmdline() changed the cmdline treat it as such. diff --git a/test/functional/ui/cmdline2_spec.lua b/test/functional/ui/cmdline2_spec.lua index 00d56fd504..e6569dc2a3 100644 --- a/test/functional/ui/cmdline2_spec.lua +++ b/test/functional/ui/cmdline2_spec.lua @@ -263,6 +263,18 @@ describe('cmdline2', function() ]]) t.eq({ CmdlineChanged = 1, CursorMovedC = 1 }, exec_lua('return _G.events')) end) + + it("no 'incsearch' recursion with 'verbose' regex message", function() + exec('set verbose=1') + feed([[:%s/.\{//}]]) + screen:expect([[ + | + {1:~ }|*9 + {3: }| + Switching to backtracking RE engine for pattern: .\{ |*2 + {16::}%{15:s}{16:/.\{//}^ } | + ]]) + end) end) describe('cmdline2', function()