mirror of
https://github.com/neovim/neovim.git
synced 2026-04-30 03:04:13 +00:00
vim-patch:9.2.0209: freeze during wildmenu completion (#38386)
Problem: Vim may freeze if setcmdline() is called while the wildmenu or
cmdline popup menu is active (rendcrx)
Solution: Cleanup completion state if cmdbuff_replaced flag has been set
(Yasuhiro Matsumoto)
fixes: vim/vim#19742
closes: vim/vim#19744
332dd22ed4
Co-authored-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
This commit is contained in:
@@ -1261,21 +1261,23 @@ static int command_line_wildchar_complete(CommandLineState *s)
|
|||||||
return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
|
return (res == OK) ? CMDLINE_CHANGED : CMDLINE_NOT_CHANGED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void command_line_end_wildmenu(CommandLineState *s, bool key_is_wc)
|
static void command_line_end_wildmenu(CommandLineState *s, bool key_is_wc, int c)
|
||||||
{
|
{
|
||||||
if (cmdline_pum_active()) {
|
if (cmdline_pum_active()) {
|
||||||
s->skip_pum_redraw = (s->skip_pum_redraw && !key_is_wc
|
if (c != -1) {
|
||||||
&& !ascii_iswhite(s->c)
|
s->skip_pum_redraw = (s->skip_pum_redraw && !key_is_wc
|
||||||
&& (vim_isprintc(s->c)
|
&& !ascii_iswhite(c)
|
||||||
|| s->c == K_BS || s->c == Ctrl_H || s->c == K_DEL
|
&& (vim_isprintc(c)
|
||||||
|| s->c == K_KDEL || s->c == Ctrl_W || s->c == Ctrl_U));
|
|| c == K_BS || c == Ctrl_H || c == K_DEL
|
||||||
cmdline_pum_remove(s->skip_pum_redraw);
|
|| c == K_KDEL || c == Ctrl_W || c == Ctrl_U));
|
||||||
|
}
|
||||||
|
cmdline_pum_remove(c != -1 && s->skip_pum_redraw);
|
||||||
}
|
}
|
||||||
if (s->xpc.xp_numfiles != -1) {
|
if (s->xpc.xp_numfiles != -1) {
|
||||||
ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
|
ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
|
||||||
}
|
}
|
||||||
s->did_wild_list = false;
|
s->did_wild_list = false;
|
||||||
if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) {
|
if (!p_wmnu || (c != K_UP && c != K_DOWN)) {
|
||||||
s->xpc.xp_context = EXPAND_NOTHING;
|
s->xpc.xp_context = EXPAND_NOTHING;
|
||||||
}
|
}
|
||||||
s->wim_index = 0;
|
s->wim_index = 0;
|
||||||
@@ -1292,6 +1294,14 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
CommandLineState *s = (CommandLineState *)state;
|
CommandLineState *s = (CommandLineState *)state;
|
||||||
s->c = key;
|
s->c = key;
|
||||||
|
|
||||||
|
// If the cmdline was replaced externally (e.g. by setcmdline()
|
||||||
|
// during an <expr> mapping), clean up the wildmenu completion
|
||||||
|
// state to avoid using stale completion data.
|
||||||
|
if (ccline.cmdbuff_replaced && s->xpc.xp_numfiles > 0) {
|
||||||
|
command_line_end_wildmenu(s, false, -1);
|
||||||
|
}
|
||||||
|
ccline.cmdbuff_replaced = false;
|
||||||
|
|
||||||
// Skip wildmenu during history navigation via Up/Down keys
|
// Skip wildmenu during history navigation via Up/Down keys
|
||||||
if (s->c == K_WILD && s->did_hist_navigate) {
|
if (s->c == K_WILD && s->did_hist_navigate) {
|
||||||
s->did_hist_navigate = false;
|
s->did_hist_navigate = false;
|
||||||
@@ -1322,7 +1332,7 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@');
|
nextwild(&s->xpc, WILD_PUM_WANT, 0, s->firstc != '@');
|
||||||
if (pum_want.finish) {
|
if (pum_want.finish) {
|
||||||
nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@');
|
nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP, s->firstc != '@');
|
||||||
command_line_end_wildmenu(s, false);
|
command_line_end_wildmenu(s, false, s->c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pum_want.active = false;
|
pum_want.active = false;
|
||||||
@@ -1430,7 +1440,7 @@ static int command_line_execute(VimState *state, int key)
|
|||||||
|
|
||||||
// free expanded names when finished walking through matches
|
// free expanded names when finished walking through matches
|
||||||
if (end_wildmenu) {
|
if (end_wildmenu) {
|
||||||
command_line_end_wildmenu(s, key_is_wc);
|
command_line_end_wildmenu(s, key_is_wc, s->c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_wmnu) {
|
if (p_wmnu) {
|
||||||
@@ -4388,6 +4398,7 @@ static int set_cmdline_str(const char *str, int pos)
|
|||||||
|
|
||||||
p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos;
|
p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos;
|
||||||
new_cmdpos = p->cmdpos;
|
new_cmdpos = p->cmdpos;
|
||||||
|
p->cmdbuff_replaced = true;
|
||||||
|
|
||||||
redrawcmd();
|
redrawcmd();
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ struct cmdline_info {
|
|||||||
int xp_context; ///< type of expansion
|
int xp_context; ///< type of expansion
|
||||||
char *xp_arg; ///< user-defined expansion arg
|
char *xp_arg; ///< user-defined expansion arg
|
||||||
int input_fn; ///< when true Invoked for input() function
|
int input_fn; ///< when true Invoked for input() function
|
||||||
|
bool cmdbuff_replaced; ///< when true cmdline was replaced externally
|
||||||
|
///< (e.g. by setcmdline())
|
||||||
unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
|
unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
|
||||||
Callback highlight_callback; ///< Callback used for coloring user input.
|
Callback highlight_callback; ///< Callback used for coloring user input.
|
||||||
ColoredCmdline last_colors; ///< Last cmdline colors
|
ColoredCmdline last_colors; ///< Last cmdline colors
|
||||||
|
|||||||
@@ -4410,6 +4410,23 @@ func Test_setcmdline()
|
|||||||
call feedkeys(":a\<CR>", 'tx')
|
call feedkeys(":a\<CR>", 'tx')
|
||||||
call assert_equal('let foo=0', @:)
|
call assert_equal('let foo=0', @:)
|
||||||
cunmap a
|
cunmap a
|
||||||
|
|
||||||
|
" setcmdline() during wildmenu completion should not freeze.
|
||||||
|
" Stripping completion state when cmdline was replaced externally.
|
||||||
|
set wildmenu
|
||||||
|
call mkdir('Xsetcmdlinedir', 'pR')
|
||||||
|
call writefile([], 'Xsetcmdlinedir/Xfile1')
|
||||||
|
call writefile([], 'Xsetcmdlinedir/Xfile2')
|
||||||
|
func g:SetCmdLineEmpty()
|
||||||
|
call setcmdline('', 1)
|
||||||
|
return "\<Left>"
|
||||||
|
endfunc
|
||||||
|
cnoremap <expr> a g:SetCmdLineEmpty()
|
||||||
|
call feedkeys(":e Xsetcmdlinedir/\<Tab>a\<C-B>\"\<CR>", 'tx')
|
||||||
|
call assert_equal('"', @:)
|
||||||
|
cunmap a
|
||||||
|
delfunc g:SetCmdLineEmpty
|
||||||
|
set nowildmenu
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_rulerformat_position()
|
func Test_rulerformat_position()
|
||||||
|
|||||||
Reference in New Issue
Block a user