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;
expand_T xpc;
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;
typedef struct cmdline_info CmdlineInfo;
@ -225,11 +221,19 @@ static bool need_cursor_update = false;
# include "ex_getln.c.generated.h"
#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)
{
// can be invoked recursively, identify each level
static int cmdline_level = 0;
cmdline_level++;
CommandLineState state, *s = &state;
memset(s, 0, sizeof(CommandLineState));
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.level++;
ccline.level = cmdline_level;
ccline.overstrike = false; // always start in insert mode
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
@ -489,19 +493,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
sb_text_end_cmdline();
{
char_u *p = ccline.cmdbuff;
// Make ccline empty, getcmdline() may try to use it.
ccline.cmdbuff = NULL;
if (ui_is_external(kUICmdline)) {
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
ccline.level--;
cmdline_level--;
return p;
}
}
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_wmh = save_p_wmh;
last_status(false);
save_cmdline(&s->save_ccline);
update_screen(VALID); // redraw the screen NOW
restore_cmdline(&s->save_ccline);
redrawcmd();
save_p_ls = -1;
wild_menu_showing = 0;
@ -818,18 +815,17 @@ static int command_line_execute(VimState *state, int key)
new_cmdpos = ccline.cmdpos;
}
save_cmdline(&s->save_ccline);
s->c = get_expr_register();
restore_cmdline(&s->save_ccline);
if (s->c == '=') {
// Need to save and restore ccline. And set "textlock"
// to avoid nasty things like going to another buffer when
// evaluating an expression.
save_cmdline(&s->save_ccline);
++textlock;
CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
textlock++;
p = get_expr_line();
--textlock;
restore_cmdline(&s->save_ccline);
textlock--;
restore_cmdline(&save_ccline);
if (p != NULL) {
len = (int)STRLEN(p);
@ -1362,9 +1358,10 @@ static int command_line_handle_key(CommandLineState *s)
beep_flush();
s->c = ESC;
} else {
save_cmdline(&s->save_ccline);
CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
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;
}
save_cmdline(&s->save_ccline);
update_screen(SOME_VALID);
restore_cmdline(&s->save_ccline);
restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work.
@ -1996,7 +1991,14 @@ getcmdline (
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
@ -2020,7 +2022,7 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
{
const int msg_col_save = msg_col;
struct cmdline_info save_ccline;
CmdlineInfo save_ccline;
save_cmdline(&save_ccline);
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;
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);
msg_silent = msg_silent_saved;
@ -2176,6 +2178,7 @@ getexline (
/* When executing a register, remove ':' that's in front of each line. */
if (exec_from_reg && vpeekc() == ':')
(void)vgetc();
return getcmdline(c, 1L, indent);
}
@ -3019,16 +3022,16 @@ void cmdline_screen_cleared(void)
}
int prev_level = ccline.level-1;
CmdlineInfo *prev_ccline = ccline.prev_ccline;
while (prev_level > 0 && prev_ccline) {
if (prev_ccline->level == prev_level) {
CmdlineInfo *line = ccline.prev_ccline;
while (prev_level > 0 && line) {
if (line->level == prev_level) {
// don't redraw a cmdline already shown in the cmdline window
if (prev_level != cmdwin_level) {
prev_ccline->redraw_state = kCmdRedrawAll;
line->redraw_state = kCmdRedrawAll;
}
prev_level--;
}
prev_ccline = prev_ccline->prev_ccline;
line = line->prev_ccline;
}
need_cursor_update = true;
@ -3244,6 +3247,7 @@ static void save_cmdline(struct cmdline_info *ccp)
ccline.cmdprompt = NULL;
ccline.xpc = NULL;
ccline.special_char = NUL;
ccline.level = 0;
}
/*

View File

@ -656,7 +656,6 @@ describe('mappings with <Cmd>', function()
end)
it('works in cmdline mode', function()
cmdmap('<F2>', 'call setcmdpos(2)')
feed(':text<F3>')
eq('c', eval('m'))
-- didn't leave cmdline mode
@ -768,5 +767,32 @@ describe('mappings with <Cmd>', function()
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)