Merge pull request #8859 from bfredl/cmdline_norm

cmdline: always use save_cmdline before command_line_enter
This commit is contained in:
Björn Linse 2018-08-17 13:33:47 +02:00 committed by GitHub
commit c7db42faab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 39 deletions

View File

@ -177,10 +177,6 @@ typedef struct command_line_state {
int break_ctrl_c; int break_ctrl_c;
expand_T xpc; expand_T xpc;
long *b_im_ptr; long *b_im_ptr;
// Everything that may work recursively should save and restore the
// current command line in save_ccline. That includes update_screen(), a
// custom status line may invoke ":normal".
struct cmdline_info save_ccline;
} CommandLineState; } CommandLineState;
typedef struct cmdline_info CmdlineInfo; typedef struct cmdline_info CmdlineInfo;
@ -225,11 +221,19 @@ static bool need_cursor_update = false;
# include "ex_getln.c.generated.h" # include "ex_getln.c.generated.h"
#endif #endif
static int cmd_hkmap = 0; /* Hebrew mapping during command line */ static int cmd_hkmap = 0; // Hebrew mapping during command line
static int cmd_fkmap = 0; // Farsi mapping during command line
static int cmd_fkmap = 0; /* Farsi mapping during command line */ /// Internal entry point for cmdline mode.
///
/// caller must use save_cmdline and restore_cmdline. Best is to use
/// getcmdline or getcmdline_prompt, instead of calling this directly.
static uint8_t *command_line_enter(int firstc, long count, int indent) static uint8_t *command_line_enter(int firstc, long count, int indent)
{ {
// can be invoked recursively, identify each level
static int cmdline_level = 0;
cmdline_level++;
CommandLineState state, *s = &state; CommandLineState state, *s = &state;
memset(s, 0, sizeof(CommandLineState)); memset(s, 0, sizeof(CommandLineState));
s->firstc = firstc; s->firstc = firstc;
@ -257,7 +261,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
} }
ccline.prompt_id = last_prompt_id++; ccline.prompt_id = last_prompt_id++;
ccline.level++; ccline.level = cmdline_level;
ccline.overstrike = false; // always start in insert mode ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end); clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later s->save_cursor = curwin->w_cursor; // may be restored later
@ -489,20 +493,15 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
sb_text_end_cmdline(); sb_text_end_cmdline();
{
char_u *p = ccline.cmdbuff; char_u *p = ccline.cmdbuff;
// Make ccline empty, getcmdline() may try to use it.
ccline.cmdbuff = NULL;
if (ui_is_external(kUICmdline)) { if (ui_is_external(kUICmdline)) {
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level); ui_call_cmdline_hide(ccline.level);
} }
ccline.level--;
cmdline_level--;
return p; return p;
} }
}
static int command_line_check(VimState *state) static int command_line_check(VimState *state)
{ {
@ -641,9 +640,7 @@ static int command_line_execute(VimState *state, int key)
p_ls = save_p_ls; p_ls = save_p_ls;
p_wmh = save_p_wmh; p_wmh = save_p_wmh;
last_status(false); last_status(false);
save_cmdline(&s->save_ccline);
update_screen(VALID); // redraw the screen NOW update_screen(VALID); // redraw the screen NOW
restore_cmdline(&s->save_ccline);
redrawcmd(); redrawcmd();
save_p_ls = -1; save_p_ls = -1;
wild_menu_showing = 0; wild_menu_showing = 0;
@ -818,18 +815,17 @@ static int command_line_execute(VimState *state, int key)
new_cmdpos = ccline.cmdpos; new_cmdpos = ccline.cmdpos;
} }
save_cmdline(&s->save_ccline);
s->c = get_expr_register(); s->c = get_expr_register();
restore_cmdline(&s->save_ccline);
if (s->c == '=') { if (s->c == '=') {
// Need to save and restore ccline. And set "textlock" // Need to save and restore ccline. And set "textlock"
// to avoid nasty things like going to another buffer when // to avoid nasty things like going to another buffer when
// evaluating an expression. // evaluating an expression.
save_cmdline(&s->save_ccline); CmdlineInfo save_ccline;
++textlock; save_cmdline(&save_ccline);
textlock++;
p = get_expr_line(); p = get_expr_line();
--textlock; textlock--;
restore_cmdline(&s->save_ccline); restore_cmdline(&save_ccline);
if (p != NULL) { if (p != NULL) {
len = (int)STRLEN(p); len = (int)STRLEN(p);
@ -1362,9 +1358,10 @@ static int command_line_handle_key(CommandLineState *s)
beep_flush(); beep_flush();
s->c = ESC; s->c = ESC;
} else { } else {
save_cmdline(&s->save_ccline); CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
s->c = get_expr_register(); s->c = get_expr_register();
restore_cmdline(&s->save_ccline); restore_cmdline(&save_ccline);
} }
} }
@ -1899,9 +1896,7 @@ static int command_line_changed(CommandLineState *s)
curwin->w_redr_status = true; curwin->w_redr_status = true;
} }
save_cmdline(&s->save_ccline);
update_screen(SOME_VALID); update_screen(SOME_VALID);
restore_cmdline(&s->save_ccline);
restore_last_search_pattern(); restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work. // Leave it at the end to make CTRL-R CTRL-W work.
@ -1996,7 +1991,14 @@ getcmdline (
int indent // indent for inside conditionals int indent // indent for inside conditionals
) )
{ {
return command_line_enter(firstc, count, indent); // Be prepared for situations where cmdline can be invoked recursively.
// That includes cmd mappings, event handlers, as well as update_screen()
// (custom status line eval), which all may invoke ":normal :".
CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
char_u *retval = command_line_enter(firstc, count, indent);
restore_cmdline(&save_ccline);
return retval;
} }
/// Get a command line with a prompt /// Get a command line with a prompt
@ -2020,7 +2022,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
{ {
const int msg_col_save = msg_col; const int msg_col_save = msg_col;
struct cmdline_info save_ccline; CmdlineInfo save_ccline;
save_cmdline(&save_ccline); save_cmdline(&save_ccline);
ccline.prompt_id = last_prompt_id++; ccline.prompt_id = last_prompt_id++;
@ -2034,7 +2036,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
int msg_silent_saved = msg_silent; int msg_silent_saved = msg_silent;
msg_silent = 0; msg_silent = 0;
char *const ret = (char *)getcmdline(firstc, 1L, 0); char *const ret = (char *)command_line_enter(firstc, 1L, 0);
restore_cmdline(&save_ccline); restore_cmdline(&save_ccline);
msg_silent = msg_silent_saved; msg_silent = msg_silent_saved;
@ -2176,6 +2178,7 @@ getexline (
/* When executing a register, remove ':' that's in front of each line. */ /* When executing a register, remove ':' that's in front of each line. */
if (exec_from_reg && vpeekc() == ':') if (exec_from_reg && vpeekc() == ':')
(void)vgetc(); (void)vgetc();
return getcmdline(c, 1L, indent); return getcmdline(c, 1L, indent);
} }
@ -3019,16 +3022,16 @@ void cmdline_screen_cleared(void)
} }
int prev_level = ccline.level-1; int prev_level = ccline.level-1;
CmdlineInfo *prev_ccline = ccline.prev_ccline; CmdlineInfo *line = ccline.prev_ccline;
while (prev_level > 0 && prev_ccline) { while (prev_level > 0 && line) {
if (prev_ccline->level == prev_level) { if (line->level == prev_level) {
// don't redraw a cmdline already shown in the cmdline window // don't redraw a cmdline already shown in the cmdline window
if (prev_level != cmdwin_level) { if (prev_level != cmdwin_level) {
prev_ccline->redraw_state = kCmdRedrawAll; line->redraw_state = kCmdRedrawAll;
} }
prev_level--; prev_level--;
} }
prev_ccline = prev_ccline->prev_ccline; line = line->prev_ccline;
} }
need_cursor_update = true; need_cursor_update = true;
@ -3244,6 +3247,7 @@ static void save_cmdline(struct cmdline_info *ccp)
ccline.cmdprompt = NULL; ccline.cmdprompt = NULL;
ccline.xpc = NULL; ccline.xpc = NULL;
ccline.special_char = NUL; ccline.special_char = NUL;
ccline.level = 0;
} }
/* /*

View File

@ -656,7 +656,6 @@ describe('mappings with <Cmd>', function()
end) end)
it('works in cmdline mode', function() it('works in cmdline mode', function()
cmdmap('<F2>', 'call setcmdpos(2)')
feed(':text<F3>') feed(':text<F3>')
eq('c', eval('m')) eq('c', eval('m'))
-- didn't leave cmdline mode -- didn't leave cmdline mode
@ -768,5 +767,32 @@ describe('mappings with <Cmd>', function()
end) end)
it("doesn't crash when invoking cmdline mode recursively #8859", function()
cmdmap('<F2>', 'norm! :foo')
feed(':bar')
screen:expect([[
some short lines |
of test text |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
:bar^ |
]])
feed('<f2>x')
screen:expect([[
some short lines |
of test text |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
:barx^ |
]])
end)
end) end)