diff options
-rw-r--r-- | src/nvim/ex_getln.c | 80 | ||||
-rw-r--r-- | test/functional/ex_cmds/cmd_map_spec.lua | 28 |
2 files changed, 69 insertions, 39 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1810056a4a..7f40a984d1 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -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; + char_u *p = ccline.cmdbuff; - if (ui_is_external(kUICmdline)) { - ccline.redraw_state = kCmdRedrawNone; - ui_call_cmdline_hide(ccline.level); - } - ccline.level--; - return p; + if (ui_is_external(kUICmdline)) { + ui_call_cmdline_hide(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; } /* diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index 77d025dcc7..acb76e382d 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -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) |