aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/ex_getln.c80
-rw-r--r--test/functional/ex_cmds/cmd_map_spec.lua28
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)