diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-08-22 18:06:18 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-22 18:06:18 +0800 |
commit | 15a768eeb02e2af39eead1ea1eb4a5a60710d6fb (patch) | |
tree | 2bd35938536d2d4a30ef993b5d8ab53f851c22df | |
parent | 2bed0d1d978b42184564201088ea5fef12ec8581 (diff) | |
download | rneovim-15a768eeb02e2af39eead1ea1eb4a5a60710d6fb.tar.gz rneovim-15a768eeb02e2af39eead1ea1eb4a5a60710d6fb.tar.bz2 rneovim-15a768eeb02e2af39eead1ea1eb4a5a60710d6fb.zip |
fix(api): avoid side effects with nvim_parse_cmd (#19890)
Save and restore the cursor and last search pattern and do not change
search history.
-rw-r--r-- | src/nvim/ex_docmd.c | 41 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 49 |
2 files changed, 74 insertions, 16 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3b1b6e81e3..5e8c7dbdda 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1361,6 +1361,13 @@ bool is_cmd_ni(cmdidx_T cmdidx) bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg) { char *after_modifier = NULL; + bool retval = false; + // parsing the command modifiers may set ex_pressedreturn + const bool save_ex_pressedreturn = ex_pressedreturn; + // parsing the command range may require moving the cursor + const pos_T save_cursor = curwin->w_cursor; + // parsing the command range may set the last search pattern + save_last_search_pattern(); // Initialize cmdinfo CLEAR_POINTER(cmdinfo); @@ -1375,13 +1382,10 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er .cookie = NULL, }; - const bool save_ex_pressedreturn = ex_pressedreturn; // Parse command modifiers if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) { - ex_pressedreturn = save_ex_pressedreturn; - goto err; + goto end; } - ex_pressedreturn = save_ex_pressedreturn; after_modifier = eap->cmd; // Save location after command modifiers @@ -1394,21 +1398,21 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er char *p = find_ex_command(eap, NULL); if (p == NULL) { *errormsg = _(e_ambiguous_use_of_user_defined_command); - goto err; + goto end; } // Set command address type and parse command range set_cmd_addr_type(eap, p); eap->cmd = cmd; - if (parse_cmd_address(eap, errormsg, false) == FAIL) { - goto err; + if (parse_cmd_address(eap, errormsg, true) == FAIL) { + goto end; } // Skip colon and whitespace eap->cmd = skip_colon_white(eap->cmd, true); // Fail if command is a comment or if command doesn't exist if (*eap->cmd == NUL || *eap->cmd == '"') { - goto err; + goto end; } // Fail if command is invalid if (eap->cmdidx == CMD_SIZE) { @@ -1417,7 +1421,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er char *cmdname = after_modifier ? after_modifier : cmdline; append_command(cmdname); *errormsg = (char *)IObuff; - goto err; + goto end; } // Correctly set 'forceit' for commands @@ -1456,12 +1460,12 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er // Fail if command doesn't support bang but is used with a bang if (!(eap->argt & EX_BANG) && eap->forceit) { *errormsg = _(e_nobang); - goto err; + goto end; } // Fail if command doesn't support a range but it is given a range if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) { *errormsg = _(e_norange); - goto err; + goto end; } // Set default range for command if required if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) { @@ -1471,7 +1475,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er // Parse register and count parse_register(eap); if (parse_count(eap, errormsg, false) == FAIL) { - goto err; + goto end; } // Remove leading whitespace and colon from next command @@ -1487,10 +1491,15 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er cmdinfo->magic.bar = true; } - return true; -err: - undo_cmdmod(&cmdinfo->cmdmod); - return false; + retval = true; +end: + if (!retval) { + undo_cmdmod(&cmdinfo->cmdmod); + } + ex_pressedreturn = save_ex_pressedreturn; + curwin->w_cursor = save_cursor; + restore_last_search_pattern(); + return retval; } static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 72a03c409a..24d0b6da45 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3668,6 +3668,55 @@ describe('API', function() :^ | ]]) end) + it('does not move cursor or change search history/pattern #19878 #19890', function() + meths.buf_set_lines(0, 0, -1, true, {'foo', 'bar', 'foo', 'bar'}) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + feed(':') -- call the API in cmdline mode to test whether it changes search history + eq({ + cmd = 'normal', + args = {'x'}, + bang = true, + range = {3, 4}, + count = -1, + reg = '', + addr = 'line', + magic = { + file = false, + bar = false, + }, + nargs = '+', + nextcmd = '', + mods = { + browse = false, + confirm = false, + emsg_silent = false, + filter = { + pattern = "", + force = false, + }, + hide = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = 0, + unsilent = false, + verbose = -1, + vertical = false, + } + }, meths.parse_cmd('+2;/bar/normal! x', {})) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + end) end) describe('nvim_cmd', function() it('works', function () |