mirror of
https://github.com/neovim/neovim.git
synced 2025-09-18 17:28:23 +00:00
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.
This commit is contained in:
@@ -1361,6 +1361,13 @@ bool is_cmd_ni(cmdidx_T cmdidx)
|
|||||||
bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg)
|
bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg)
|
||||||
{
|
{
|
||||||
char *after_modifier = NULL;
|
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
|
// Initialize cmdinfo
|
||||||
CLEAR_POINTER(cmdinfo);
|
CLEAR_POINTER(cmdinfo);
|
||||||
@@ -1375,13 +1382,10 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
|
|||||||
.cookie = NULL,
|
.cookie = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const bool save_ex_pressedreturn = ex_pressedreturn;
|
|
||||||
// Parse command modifiers
|
// Parse command modifiers
|
||||||
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
|
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
|
||||||
ex_pressedreturn = save_ex_pressedreturn;
|
goto end;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
ex_pressedreturn = save_ex_pressedreturn;
|
|
||||||
after_modifier = eap->cmd;
|
after_modifier = eap->cmd;
|
||||||
|
|
||||||
// Save location after command modifiers
|
// 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);
|
char *p = find_ex_command(eap, NULL);
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
*errormsg = _(e_ambiguous_use_of_user_defined_command);
|
*errormsg = _(e_ambiguous_use_of_user_defined_command);
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set command address type and parse command range
|
// Set command address type and parse command range
|
||||||
set_cmd_addr_type(eap, p);
|
set_cmd_addr_type(eap, p);
|
||||||
eap->cmd = cmd;
|
eap->cmd = cmd;
|
||||||
if (parse_cmd_address(eap, errormsg, false) == FAIL) {
|
if (parse_cmd_address(eap, errormsg, true) == FAIL) {
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip colon and whitespace
|
// Skip colon and whitespace
|
||||||
eap->cmd = skip_colon_white(eap->cmd, true);
|
eap->cmd = skip_colon_white(eap->cmd, true);
|
||||||
// Fail if command is a comment or if command doesn't exist
|
// Fail if command is a comment or if command doesn't exist
|
||||||
if (*eap->cmd == NUL || *eap->cmd == '"') {
|
if (*eap->cmd == NUL || *eap->cmd == '"') {
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
// Fail if command is invalid
|
// Fail if command is invalid
|
||||||
if (eap->cmdidx == CMD_SIZE) {
|
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;
|
char *cmdname = after_modifier ? after_modifier : cmdline;
|
||||||
append_command(cmdname);
|
append_command(cmdname);
|
||||||
*errormsg = (char *)IObuff;
|
*errormsg = (char *)IObuff;
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correctly set 'forceit' for commands
|
// 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
|
// Fail if command doesn't support bang but is used with a bang
|
||||||
if (!(eap->argt & EX_BANG) && eap->forceit) {
|
if (!(eap->argt & EX_BANG) && eap->forceit) {
|
||||||
*errormsg = _(e_nobang);
|
*errormsg = _(e_nobang);
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
// Fail if command doesn't support a range but it is given a range
|
// Fail if command doesn't support a range but it is given a range
|
||||||
if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
|
if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
|
||||||
*errormsg = _(e_norange);
|
*errormsg = _(e_norange);
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
// Set default range for command if required
|
// Set default range for command if required
|
||||||
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
|
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 and count
|
||||||
parse_register(eap);
|
parse_register(eap);
|
||||||
if (parse_count(eap, errormsg, false) == FAIL) {
|
if (parse_count(eap, errormsg, false) == FAIL) {
|
||||||
goto err;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove leading whitespace and colon from next command
|
// 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;
|
cmdinfo->magic.bar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
retval = true;
|
||||||
err:
|
end:
|
||||||
undo_cmdmod(&cmdinfo->cmdmod);
|
if (!retval) {
|
||||||
return false;
|
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)
|
static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview)
|
||||||
|
@@ -3668,6 +3668,55 @@ describe('API', function()
|
|||||||
:^ |
|
:^ |
|
||||||
]])
|
]])
|
||||||
end)
|
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)
|
end)
|
||||||
describe('nvim_cmd', function()
|
describe('nvim_cmd', function()
|
||||||
it('works', function ()
|
it('works', function ()
|
||||||
|
Reference in New Issue
Block a user