From 511f06a56e77f612f7bc8ca563ebecb7239a9914 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Thu, 5 May 2022 23:11:57 +0600 Subject: [PATCH] fix(api): make `nvim_parse_cmd` propagate errors Makes `nvim_parse_cmd` propagate any errors that occur while parsing to give the user a better idea of what's wrong with the command. --- src/nvim/api/vimscript.c | 11 ++++++++--- src/nvim/ex_docmd.c | 28 +++++++++++++++++++++------- test/functional/api/vim_spec.lua | 11 ++++++++--- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 698b2d06fb..3346b5a237 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -802,10 +802,15 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) // Parse command line exarg_T ea; CmdParseInfo cmdinfo; - char_u *cmdline = (char_u *)string_to_cstr(str); + char *cmdline = string_to_cstr(str); + char *errormsg = NULL; - if (!parse_cmdline(cmdline, &ea, &cmdinfo)) { - api_set_error(err, kErrorTypeException, "Error while parsing command line"); + if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { + if (errormsg != NULL) { + api_set_error(err, kErrorTypeException, "Error while parsing command line: %s", errormsg); + } else { + api_set_error(err, kErrorTypeException, "Error while parsing command line"); + } goto end; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5ed058cd89..f5af808175 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1359,12 +1359,17 @@ static int parse_count(exarg_T *eap, char **errormsg) /// Parse command line and return information about the first command. /// +/// @param cmdline Command line string +/// @param[out] eap Ex command arguments +/// @param[out] cmdinfo Command parse information +/// @param[out] errormsg Error message, if any +/// /// @return Success or failure -bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) +bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg) { - char *errormsg = NULL; char *cmd; char *p; + char *after_modifier = NULL; cmdmod_T save_cmdmod = cmdmod; // Initialize cmdinfo @@ -1374,15 +1379,17 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) memset(eap, 0, sizeof(*eap)); eap->line1 = 1; eap->line2 = 1; - eap->cmd = (char *)cmdline; - eap->cmdlinep = (char **)&cmdline; + eap->cmd = cmdline; + eap->cmdlinep = &cmdline; eap->getline = NULL; eap->cookie = NULL; // Parse command modifiers - if (parse_command_modifiers(eap, &errormsg, false) == FAIL) { + if (parse_command_modifiers(eap, errormsg, false) == FAIL) { return false; } + after_modifier = eap->cmd; + // Revert the side-effects of `parse_command_modifiers` if (eap->save_msg_silent != -1) { cmdinfo->silent = !!msg_silent; @@ -1422,7 +1429,7 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) // Set command attribute type and parse command range set_cmd_addr_type(eap, (char_u *)p); eap->cmd = cmd; - if (parse_cmd_address(eap, &errormsg, false) == FAIL) { + if (parse_cmd_address(eap, errormsg, false) == FAIL) { return false; } @@ -1434,6 +1441,11 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) } // Fail if command is invalid if (eap->cmdidx == CMD_SIZE) { + STRCPY(IObuff, _("E492: Not an editor command")); + // If the modifier was parsed OK the error must be in the following command + char *cmdname = after_modifier ? after_modifier : cmdline; + append_command(cmdname); + *errormsg = (char *)IObuff; return false; } @@ -1472,10 +1484,12 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) } // Fail if command doesn't support bang but is used with a bang if (!(eap->argt & EX_BANG) && eap->forceit) { + *errormsg = _(e_nobang); return false; } // 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); return false; } // Set default range for command if required @@ -1485,7 +1499,7 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo) // Parse register and count parse_register(eap); - if (parse_count(eap, NULL) == FAIL) { + if (parse_count(eap, errormsg) == FAIL) { return false; } diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 610036f484..11c1fc6c2c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3426,10 +3426,15 @@ describe('API', function() }, meths.parse_cmd('MyCommand test it', {})) end) it('errors for invalid command', function() - eq('Error while parsing command line', pcall_err(meths.parse_cmd, 'Fubar', {})) + eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {})) + eq('Error while parsing command line', pcall_err(meths.parse_cmd, '" foo', {})) + eq('Error while parsing command line: E492: Not an editor command: Fubar', + pcall_err(meths.parse_cmd, 'Fubar', {})) command('command! Fubar echo foo') - eq('Error while parsing command line', pcall_err(meths.parse_cmd, 'Fubar!', {})) - eq('Error while parsing command line', pcall_err(meths.parse_cmd, '4,6Fubar', {})) + eq('Error while parsing command line: E477: No ! allowed', + pcall_err(meths.parse_cmd, 'Fubar!', {})) + eq('Error while parsing command line: E481: No range allowed', + pcall_err(meths.parse_cmd, '4,6Fubar', {})) end) end) end)